morm/src/model.h

1019 lines
34 KiB
C++

/*
* This file is a part of morm
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2018-2021, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef headerfile_morm_model
#define headerfile_morm_model
#include <string>
#include <list>
#include <vector>
#include <typeinfo>
#include <type_traits>
#include "textstream/textstream.h"
#include "space/space.h"
#include "modelconnector.h"
#include "dbexpression.h"
#include "flatexpression.h"
#include "modelenv.h"
#include "ft.h"
namespace morm
{
class Model
{
public:
enum SaveMode
{
DO_INSERT_ON_SAVE = 0,
DO_UPDATE_ON_SAVE,
DO_DELETE_ON_SAVE,
DO_NOTHING_ON_SAVE,
};
virtual void set_save_mode(SaveMode save_mode);
virtual SaveMode get_save_mode();
virtual void set_has_primary_key_set(bool has_primary_key);
virtual bool get_has_primary_key_set();
virtual void mark_to_delete();
virtual void mark_to_remove();
virtual void mark_to_insert();
virtual void mark_to_update();
virtual bool object_exists();
virtual bool found();
void set_connector(ModelConnector & connector);
void set_connector(ModelConnector * connector);
ModelConnector * get_connector();
virtual void get_table_name(pt::WTextStream & stream, bool with_schema_name = true, ModelData * model_data = nullptr, bool clear_stream = true);
virtual void get_table_name(std::wstring & str, bool with_schema_name = true, ModelData * model_data = nullptr, bool clear_string = true);
virtual void get_table_name(std::string & str, bool with_schema_name = true, ModelData * model_data = nullptr, bool clear_string = true);
virtual void to_text(pt::TextStream & stream, ModelData * model_data, bool clear_stream = true, bool dump_mode = false);
virtual void to_text(pt::TextStream & stream, ModelData & model_data, bool clear_stream = true, bool dump_mode = false);
virtual void to_text(pt::TextStream & stream, bool clear_stream = true, bool dump_mode = false);
virtual void to_text(std::string & str, ModelData * model_data, bool clear_string = true, bool dump_mode = false);
virtual void to_text(std::string & str, ModelData & model_data, bool clear_string = true, bool dump_mode = false);
virtual void to_text(std::string & str, bool clear_string = true, bool dump_mode = false);
virtual std::string to_text();
virtual std::string to_string();
virtual void generate_insert_query(pt::TextStream & stream, ModelData * model_data = nullptr);
virtual bool insert(ModelData * model_data, bool insert_whole_tree = true);
virtual bool insert(ModelData & model_data, bool insert_whole_tree = true);
virtual bool insert(bool insert_whole_tree = true);
virtual void generate_update_query(pt::TextStream & stream, ModelData * model_data = nullptr);
virtual bool update(ModelData * model_data, bool update_whole_tree = true);
virtual bool update(ModelData & model_data, bool update_whole_tree = true);
virtual bool update(bool update_whole_tree = true);
virtual void generate_remove_query(pt::TextStream & stream, ModelData * model_data = nullptr);
virtual bool remove(ModelData * model_data, bool remove_whole_tree = true);
virtual bool remove(ModelData & model_data, bool remove_whole_tree = true);
virtual bool remove(bool remove_whole_tree = true);
virtual bool save(ModelData * model_data, bool save_whole_tree = true);
virtual bool save(ModelData & model_data, bool save_whole_tree = true);
virtual bool save(bool save_whole_tree = true);
virtual void generate_select_columns(pt::TextStream & stream);
// set object to default values
virtual void clear();
virtual bool do_migration(int & current_table_version);
// IMPROVE ME this will be protected
// add set_field_value() functions for each POD type
template<typename FieldValue>
void set_field_value_generic(const wchar_t * db_field_name, const wchar_t * flat_field_name, const FieldValue & field_value)
{
ModelEnv model_env_local;
model_env = &model_env_local;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_SET_FIELD_VALUE;
model_env->field_index = 0;
table(); // CHECK ME it is needed to set table name?
FieldValueHelper field_value_helper;
field_value_helper.db_field_name = db_field_name;
field_value_helper.flat_field_name = flat_field_name;
field_value_helper.value_object = &field_value;
field_value_helper.value_type_info = &typeid(field_value);
std::vector<FieldValueHelper> helper_tab;
helper_tab.push_back(field_value_helper);
model_env->field_value_helper_tab = &helper_tab;
fields();
if( !helper_tab.back().found && model_connector )
{
pt::Log * plog = model_connector->get_logger();
if( plog )
{
(*plog) << pt::Log::log1 << "Morm: I cannot find such a property: ";
put_fields_to_log(*plog, db_field_name, flat_field_name);
(*plog) << pt::Log::logend;
}
}
// what if an exception was thrown?
model_env = nullptr;
}
protected:
ModelConnector * model_connector;
ModelEnv * model_env;
SaveMode save_mode;
bool has_primary_key_set;
Model();
Model(const Model & m);
virtual ~Model();
/*
* the main method of mapping between fields and database resultsets
*/
virtual void fields() = 0;
virtual void table();
virtual void table_name(const wchar_t * table_name);
virtual void table_name(const wchar_t * schema_name, const wchar_t * table_name);
virtual void before_select();
virtual void before_insert();
virtual void before_update();
virtual void before_remove();
virtual void after_select();
virtual void after_insert();
virtual void after_update();
virtual void after_remove();
virtual void after_select_failure();
virtual void after_insert_failure();
virtual void after_update_failure();
virtual void after_remove_failure();
virtual int get_work_mode();
virtual ModelData * get_model_data();
virtual bool insert_tree(bool insert_whole_tree);
virtual bool update_tree(bool update_whole_tree);
virtual bool remove_tree(bool remove_whole_tree);
virtual bool save_tree(bool save_whole_tree);
virtual void map_values_from_query();
virtual bool db_query(const char * raw_sql);
virtual bool db_query(const std::string & raw_sql);
virtual bool db_query(const pt::TextStream & raw_sql);
/////////////////////////////////
/*
* IMPLEMENT ME
* field methods for such field_values: signed char, char16_t, char32_t, std::u16string, std::u32string
*
*/
void field(const wchar_t * field_name, char & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, unsigned char & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, wchar_t & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, std::wstring & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, std::string & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, bool & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, short & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, unsigned short & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, int & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, unsigned int & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, long & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, unsigned long & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, long long & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, unsigned long long & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, float & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, double & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, long double & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, pt::Date & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, pt::Space & field_value, const FT & field_type = FT::default_type)
{
field_generic(field_name, field_name, field_value, field_type);
}
void field(const wchar_t * field_name, Model & field_value, const FT & field_type = FT::default_type)
{
field_model(field_name, field_name, field_value, field_type);
}
template<typename ModelClass>
void field(const wchar_t * field_name, std::list<ModelClass> & field_value, const FT & field_type = FT::default_type)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(field_name, field_name, field_value, list_model_null_pointer, field_type);
}
template<typename ModelClass>
void field(const wchar_t * field_name, std::vector<ModelClass> & field_value, const FT & field_type = FT::default_type)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(field_name, field_name, field_value, list_model_null_pointer, field_type);
}
/*
* field methods which take two names: db_field_name and flat_field_name
*/
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, char & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned char & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, wchar_t & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::wstring & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::string & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, bool & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, short & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned short & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, int & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned int & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned long & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long long & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned long long & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, float & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, double & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long double & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, pt::Date & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, pt::Space & field_value, const FT & field_type = FT::default_type)
{
field_generic(db_field_name, flat_field_name, field_value, field_type);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_value, const FT & field_type = FT::default_type)
{
// has_foreign_key was here
field_model(db_field_name, flat_field_name, field_value, field_type);
}
template<typename ModelClass>
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::list<ModelClass> & field_value, const FT & field_type = FT::default_type)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(db_field_name, flat_field_name, field_value, list_model_null_pointer, field_type);
}
template<typename ModelClass>
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::vector<ModelClass> & field_value, const FT & field_type = FT::default_type)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(db_field_name, flat_field_name, field_value, list_model_null_pointer, field_type);
}
protected:
template<typename FieldValue>
void field_generic_set_field_value(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value)
{
if( model_env->field_value_helper_tab )
{
if( model_env->field_index >= 0 && (size_t)model_env->field_index < model_env->field_value_helper_tab->size() )
{
FieldValueHelper & helper = (*model_env->field_value_helper_tab)[model_env->field_index];
pt::Log * log = model_connector->get_logger();
if( (!helper.compare_db_field_name || is_the_same_field(db_field_name, helper.db_field_name)) &&
(!helper.compare_flat_field_name || is_the_same_field(flat_field_name, helper.flat_field_name)) )
{
if( helper.value_object && helper.value_type_info )
{
if( typeid(field_value) == *helper.value_type_info )
{
field_value = *(FieldValue*)helper.value_object;
}
else
{
if( log )
{
(*log) << pt::Log::log1 << "Morm: incorrect type of a field in ";
log_table_name();
(*log) << ", ";
put_fields_to_log(*log, db_field_name, flat_field_name);
(*log) << ", type expected " << typeid(field_value).name()
<< " got " << helper.value_type_info->name() << pt::Log::logend;
}
}
}
helper.found = true;
model_env->field_index += 1;
}
}
}
}
template<typename FieldValue>
void field_generic_iterate_primary_key_values(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
if( field_type.is_primary_key() )
{
if( model_env->field_value_helper_tab )
{
if( model_env->field_index >= 0 && (size_t)model_env->field_index < model_env->field_value_helper_tab->size() )
{
FieldValueHelper & helper = (*model_env->field_value_helper_tab)[model_env->field_index];
helper.value_object = &field_value;
helper.value_type_info = &typeid(field_value);
}
}
model_env->field_index += 1;
}
}
template<typename FieldValue>
void field_generic_generate_flat_string(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
FlatConnector * flat_connector = model_connector->get_flat_connector();
if( flat_connector )
{
FlatExpression * flat_expression = flat_connector->get_expression();
if( flat_expression && !is_empty_field(flat_field_name) )
{
flat_expression->field(flat_field_name, field_value, field_type, model_env);
}
}
}
template<typename FieldValue>
void field_generic_generate_db_sql(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression && !is_empty_field(db_field_name) )
{
db_expression->field(db_field_name, field_value, field_type, model_env);
}
}
}
template<typename FieldValue>
void field_generic_read_value_from_db_resultset(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector )
{
if( !is_empty_field(db_field_name) )
{
if( model_env->cursor_helper && model_env->cursor_helper->has_autogenerated_select )
{
get_value_by_field_index(model_env->cursor_helper->current_column, field_value, field_type);
model_env->cursor_helper->current_column += 1;
}
else
{
get_value_by_field_name(db_field_name, field_value, field_type);
}
}
}
}
template<typename FieldValue>
void field_generic_clear_value(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
Clearer * clearer = model_connector->get_clearer();
if( clearer )
{
clearer->clear_value(field_value);
}
}
template<typename FieldValue>
void field_generic(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, const FT & field_type)
{
if( model_connector && model_env )
{
if( field_type.is_primary_key() )
{
model_env->was_primary_key_read = true;
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_FIELD_VALUE )
{
field_generic_set_field_value(db_field_name, flat_field_name, field_value);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_ITERATE_PRIMARY_KEY_VALUES )
{
field_generic_iterate_primary_key_values(db_field_name, flat_field_name, field_value, field_type);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_generic_generate_flat_string(db_field_name, flat_field_name, field_value, field_type);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_DB_SQL )
{
field_generic_generate_db_sql(db_field_name, flat_field_name, field_value, field_type);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET )
{
field_generic_read_value_from_db_resultset(db_field_name, flat_field_name, field_value, field_type);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_generic_clear_value(db_field_name, flat_field_name, field_value, field_type);
}
}
}
void field_model_left_join(const wchar_t * db_field_name, Model & field_model, const FT & field_type, DbExpression * db_expression);
void field_model_save_key(const wchar_t * db_field_name);
void field_model_set_parent_key_in_child(const wchar_t * db_field_name, Model & field_model);
void field_model_set_parent_key(const wchar_t * db_field_name, Model & field_model);
void field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model);
void field_model_generate_flat_string(const wchar_t * flat_field_name, Model & field_model, const FT & field_type);
void field_model_generate_db_sql(const wchar_t * db_field_name, Model & field_model, const FT & field_type);
void field_model_clear_values(Model & field_model);
void field_model_read_values_from_queryresult(const wchar_t * db_field_name, Model & field_model, const FT & field_type);
void field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, const FT & field_type);
void field_model_for_db(const wchar_t * db_field_name, Model & field_model, const FT & field_type);
template<typename ModelContainer, typename ModelContainerType>
void field_list_set_parent_key(const wchar_t * db_field_name, ModelContainer & field_container, ModelContainerType * model_container_type)
{
FieldValueHelper helper;
helper.db_field_name = db_field_name;
helper.flat_field_name = nullptr;
helper.compare_flat_field_name = false;
std::vector<FieldValueHelper> helper_tab;
helper_tab.push_back(helper);
// only one column at the moment, in the future we can have a primary key from more than one column
model_env->field_value_helper_tab = &helper_tab;
field_model_save_key(db_field_name);
for(ModelContainerType & child_model : field_container)
{
child_model.set_connector(model_connector);
field_model_set_parent_key_in_child(db_field_name, child_model);
}
model_env->field_value_helper_tab = nullptr;
}
template<typename ModelContainer, typename ModelContainerType>
void field_list_iterate_through_childs(const wchar_t * db_field_name, ModelContainer & field_container, ModelContainerType * model_container_type)
{
for(ModelContainerType & child_model : field_container)
{
ModelEnv model_env_local;
model_env_local.copy_global_objects(*model_env);
child_model.model_env = &model_env_local;
child_model.model_env->has_primary_key_set = child_model.has_primary_key_set;
child_model.set_connector(model_connector);
child_model.table();
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_INSERT )
{
child_model.insert_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_UPDATE )
{
child_model.update_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_REMOVE )
{
child_model.remove_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_SAVE )
{
child_model.save_tree(true);
}
child_model.model_env = nullptr;
}
}
template<typename ModelContainer>
void field_list_generate_flat_string(const wchar_t * flat_field_name, ModelContainer & field_container, const FT & field_type)
{
FlatConnector * flat_connector = model_connector->get_flat_connector();
if( flat_connector )
{
FlatExpression * flat_expression = flat_connector->get_expression();
if( flat_expression )
{
flat_expression->field_list(flat_field_name, field_container, field_type, model_connector, model_env);
}
}
}
template<typename ModelContainer>
void field_list_clearing_values(ModelContainer & field_container)
{
Clearer * clearer = model_connector->get_clearer();
if( clearer )
{
clearer->clear_container(field_container);
}
}
template<typename ModelContainer, typename ModelContainerType>
void field_list(const wchar_t * db_field_name, const wchar_t * flat_field_name, ModelContainer & field_container, ModelContainerType * model_container_type, const FT & field_type)
{
if( model_connector && model_env )
{
pt::Log * plog = model_connector->get_logger();
if( !is_empty_field(db_field_name) )
{
/*
* IMPROVEME
* field_type.is_foreign_key() is not implemented for lists yet
* (in such a case parent will point only to one object (or none) so the list will consists of only one object (or none))
*/
if( field_type.is_foreign_key() )
{
if( plog )
{
(*plog) << pt::Log::log1 << "Morm: error: FT::is_foreign_key is not implemented for a list/vector yet" << pt::Log::logend;
return;
}
}
if( field_type.is_foreign_key() || field_type.is_foreign_key_in_child() )
{
if constexpr (std::is_base_of<Model, ModelContainerType>())
{
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_PARENT_ID )
{
field_list_set_parent_key(db_field_name, field_container, model_container_type);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_ITERATE_THROUGH_CHILDS_WITHOUT_FOREIGN_KEY )
{
field_list_iterate_through_childs(db_field_name, field_container, model_container_type);
}
}
else
{
if( plog )
{
(*plog) << pt::Log::log1 << "Morm: ignoring ";
log_table_name_with_field(db_field_name);
(*plog) << " as this is not a container with Model objects" << pt::Log::logend;
}
}
}
else
{
if( plog )
{
(*plog) << pt::Log::log1 << "Morm: error in ";
log_table_name_with_field(db_field_name);
(*plog) << " field, you should set FT::is_foreign_key or FT::is_foreign_key_in_child flag for a list of child objects" << pt::Log::logend;
}
}
}
if( !is_empty_field(flat_field_name) )
{
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_list_generate_flat_string(flat_field_name, field_container, field_type);
}
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_list_clearing_values(field_container);
}
}
}
template<typename FieldValue>
void get_value_by_field_index(int field_index, FieldValue & field_value, const FT & field_type)
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector && model_env->cursor_helper && model_env->cursor_helper->query_result )
{
if( !model_env->cursor_helper->query_result->is_null(field_index) )
{
const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(field_index);
if( val_str )
{
db_connector->get_value(val_str, field_value, field_type);
}
}
else
{
if( field_type.is_primary_key() )
{
model_env->has_primary_key_set = false;
}
}
}
}
template<typename FieldValue>
void get_value_by_field_name(const wchar_t * field_name, FieldValue & field_value, const FT & field_type)
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector && model_env->cursor_helper && model_env->cursor_helper->query_result )
{
int column_index = -1;
if( model_env->cursor_helper->use_table_prefix_for_fetching_values && model_env->finder_helper )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression )
{
std::wstring table_field_name;
pt::TextStream table_field_name_str;
// CHECK ME not tested yet after changing to db_expression->table_with_index_and_field_to_stream()
db_expression->table_with_index_and_field_to_stream(table_field_name_str, model_env->table_name, model_env->table_index, field_name, field_type);
table_field_name_str.to_string(table_field_name);
column_index = model_env->cursor_helper->query_result->get_column_index(table_field_name.c_str());
}
}
else
{
column_index = model_env->cursor_helper->query_result->get_column_index(field_name);
}
if( column_index != -1 && !model_env->cursor_helper->query_result->is_null(column_index) )
{
const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(column_index);
if( val_str )
{
db_connector->get_value(val_str, field_value, field_type);
}
}
else
{
if( field_type.is_primary_key() )
{
model_env->has_primary_key_set = false;
}
}
}
}
virtual void set_parent_key_in_childs();
public:
template<typename FieldValue>
bool get_last_sequence(const wchar_t * sequence_table_name, FieldValue & field_value)
{
if( model_connector )
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector && !is_empty_field(sequence_table_name) )
{
return db_connector->get_last_sequence(sequence_table_name, field_value);
}
}
return false;
}
template<typename FieldValue>
bool get_last_sequence_for_primary_key(const wchar_t * sequence_table_name, FieldValue & field_value)
{
has_primary_key_set = get_last_sequence(sequence_table_name, field_value);
return has_primary_key_set;
}
template<typename FieldValue>
void add_field_for_select(const wchar_t * new_column_expression, const wchar_t * new_column_name, const wchar_t * flat_field_name, FieldValue & field_value)
{
FT field_type = FT::no_insertable | FT::no_updatable | FT::raw_field_name;
if( model_connector && model_env )
{
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_DB_SQL )
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression && !is_empty_field(new_column_expression) )
{
db_expression->add_field_for_select(new_column_expression, new_column_name, field_value, field_type, model_env);
}
}
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_generic(L"", flat_field_name, field_value, field_type);
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET )
{
field_generic(new_column_name, L"", field_value, field_type);
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_generic(L"", L"", field_value, field_type); // the names don't matter here
}
}
}
// RENAME ME to something like select_field() or field_select()
template<typename FieldValue>
void add_field_for_select(const wchar_t * new_column_expression, const wchar_t * new_column_name, FieldValue & field_value)
{
add_field_for_select(new_column_expression, new_column_name, new_column_name, field_value);
}
protected:
virtual bool is_empty_field(const wchar_t * value);
virtual bool is_the_same_field(const wchar_t * field1, const wchar_t * field2);
virtual void put_to_log(const wchar_t * str);
virtual void put_fields_to_log(pt::Log & log, const wchar_t * db_field_name, const wchar_t * flat_field_name);
virtual void log_table_name(bool put_schema_name = true);
virtual void log_table_name_with_field(const wchar_t * db_field_name = nullptr, bool put_schema_name = true);
template<typename ModelObject>
bool do_migration(int & current_table_version, int destination_table_version, ModelObject * model_object, bool (ModelObject::*migration_function)())
{
bool status = true;
if( current_table_version < destination_table_version )
{
if( (model_object->*migration_function)() )
{
current_table_version = destination_table_version;
}
else
{
status = false;
}
}
return status;
}
template<typename ModelClass> friend class Finder;
template<typename ModelClass> friend class Cursor;
friend class BaseExpression;
friend class DbConnector;
};
} // namespace
#endif