morm/src/model.h

1361 lines
47 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 "modelconnector.h"
#include "dbexpression.h"
#include "flatexpression.h"
#include "modelenv.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();
/*
* map fields to names
*
*
*/
virtual void map_fields() = 0;
/*
*
* we can use the object for a different purpose than database (e.g. json)
* so let the table_name be non pure-virtual
*
*/
virtual void table_name(PT::TextStream & stream);
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();
// 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;
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;
map_fields();
if( !helper_tab.back().found && model_connector )
{
PT::Log * plog = model_connector->get_logger();
if( plog )
{
(*plog) << "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();
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();
/////////////////////////////////
/*
* IMPLEMENT ME
* field methods for such field_values: signed char, wchar_t, char16_t, char32_t, std::u16string, std::u32string
*
*/
void field(const wchar_t * field_name, char & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, unsigned char & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, std::wstring & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * field_name, wchar_t * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * field_name, std::string & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * field_name, char * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * field_name, bool & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, short & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, unsigned short & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, int & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, unsigned int & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, unsigned long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, long long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, unsigned long long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, float & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, double & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, long double & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * field_name, void * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * field_name, PT::Date & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * field_name, Model & field_value, bool insertable = true, bool updatable = true, bool has_foreign_key = true)
{
field_model(field_name, field_name, field_value, insertable, updatable, has_foreign_key);
}
template<typename ModelClass>
void field(const wchar_t * field_name, std::list<ModelClass> & field_value, bool insertable = true, bool updatable = true)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(field_name, field_name, field_value, list_model_null_pointer, insertable, updatable);
}
template<typename ModelClass>
void field(const wchar_t * field_name, std::vector<ModelClass> & field_value, bool insertable = true, bool updatable = true)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(field_name, field_name, field_value, list_model_null_pointer, insertable, updatable);
}
//////////////////////
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, char & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned char & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::wstring & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, wchar_t * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::string & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, char * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, bool & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, short & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned short & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, int & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned int & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, unsigned long long & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, float & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, double & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, long double & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
// void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, void * field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
// {
// field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
// }
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, PT::Date & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false)
{
field_generic(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_value, bool insertable = true, bool updatable = true, bool has_foreign_key = true)
{
field_model(db_field_name, flat_field_name, field_value, insertable, updatable, has_foreign_key);
}
template<typename ModelClass>
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::list<ModelClass> & field_value, bool insertable = true, bool updatable = true)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(db_field_name, flat_field_name, field_value, list_model_null_pointer, insertable, updatable);
}
template<typename ModelClass>
void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::vector<ModelClass> & field_value, bool insertable = true, bool updatable = true)
{
ModelClass * list_model_null_pointer = nullptr;
field_list(db_field_name, flat_field_name, field_value, list_model_null_pointer, insertable, updatable);
}
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, bool insertable, bool updatable, bool 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];
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
{
table_name(model_env->table_name);
if( log )
{
(*log) << PT::Log::log1 << "Morm: incorrect type of a field in " << model_env->table_name << ", ";
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, bool insertable, bool updatable, bool is_primary_key)
{
if( 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, bool insertable, bool updatable, bool is_primary_key)
{
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) )
{
// insertable, updatable and is_primary_key are ignored here
flat_expression->field(flat_field_name, field_value, insertable, updatable, is_primary_key, 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, bool insertable, bool updatable, bool is_primary_key)
{
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, insertable, updatable, is_primary_key, 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, bool insertable, bool updatable, bool is_primary_key)
{
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, is_primary_key);
model_env->cursor_helper->current_column += 1;
}
else
{
get_value_by_field_name(db_field_name, field_value, is_primary_key);
}
}
}
}
template<typename FieldValue>
void field_generic_clear_value(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, bool insertable, bool updatable, bool is_primary_key)
{
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, bool insertable, bool updatable, bool is_primary_key)
{
if( model_connector && model_env )
{
if( 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, insertable, updatable, is_primary_key);
}
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, insertable, updatable, is_primary_key);
}
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, insertable, updatable, is_primary_key);
}
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, insertable, updatable, is_primary_key);
}
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, insertable, updatable, is_primary_key);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_generic_clear_value(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key);
}
}
}
/*
* IMPROVE ME there can be more rows in the result set when there are more items on the left hand side of the join
* this is only in a case when has_foreign_key is false, may it can be ignored? we can use unique index in such a case
*
*/
void field_model_left_join(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key, DbExpression * db_expression)
{
if( model_env && field_model.model_env && model_env->finder_helper )
{
model_env->finder_helper->foreign_keys.clear();
PT::TextStream & join_tables_str = model_env->finder_helper->join_tables_str;
field_model.model_env->table_index = model_env->finder_helper->add_join_table(field_model.model_env->table_name_short);
join_tables_str << "LEFT JOIN " << field_model.model_env->table_name << " AS ";
field_model.put_table_name_with_index(join_tables_str);
int expr_work_mode = db_expression->get_work_mode();
int expr_output_type = db_expression->get_output_type();
bool expr_allow_prefix = db_expression->get_allow_to_use_prefix();
db_expression->set_work_mode(MORM_WORK_MODE_MODEL_SAVE_FIELDS);
db_expression->set_output_type(MORM_OUTPUT_TYPE_JOIN_TABLES);
db_expression->allow_to_use_prefix(false);
if( has_foreign_key )
{
field_model.map_fields();
join_tables_str << " ON ";
put_table_name_with_index(join_tables_str);
join_tables_str << '.' << db_field_name << " = ";
field_model.put_table_name_with_index(join_tables_str);
join_tables_str << '.';
// IMPROVE ME at the moment support only for foreign keys consisting of only one column
if( model_env->finder_helper->foreign_keys.size() == 1 )
{
join_tables_str << model_env->finder_helper->foreign_keys.front();
}
}
else
{
ModelEnv * old_model_env = field_model.model_env;
map_fields(); // map_fields() will set field_model.model_env to null
field_model.model_env = old_model_env;
join_tables_str << " ON ";
put_table_name_with_index(join_tables_str);
join_tables_str << '.';
// IMPROVE ME at the moment support only for foreign keys consisting of only one column
if( model_env->finder_helper->foreign_keys.size() == 1 )
{
join_tables_str << model_env->finder_helper->foreign_keys.front();
}
join_tables_str << " = ";
field_model.put_table_name_with_index(join_tables_str);
join_tables_str << '.' << db_field_name;
}
join_tables_str << ' ';
db_expression->set_work_mode(expr_work_mode);
db_expression->set_output_type(expr_output_type);
db_expression->allow_to_use_prefix(expr_allow_prefix);
}
}
/*
* first we iterate through fields and save primary key values to helper_tab
*/
void field_model_save_key(const wchar_t * db_field_name)
{
DbConnector * db_connector = model_connector->get_db_connector();
PT::Log * plog = model_connector->get_logger();
if( db_connector )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression && !is_empty_field(db_field_name) && model_env->field_value_helper_tab )
{
int old_work_mode = model_env->model_work_mode;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_ITERATE_PRIMARY_KEY_VALUES;
model_env->field_index = 0;
map_fields();
model_env->model_work_mode = old_work_mode;
if( model_env->field_value_helper_tab->empty() && plog )
{
(*plog) << "Morm: I cannot find a primary key in " << model_env->table_name << PT::Log::logend;
}
}
}
}
/*
* now we iterate through fields in field_model and save primary key values from *this object to the specified fields in field_model
*/
void field_model_set_parent_key_in_child(const wchar_t * db_field_name, Model & field_model)
{
DbConnector * db_connector = model_connector->get_db_connector();
PT::Log * log = model_connector->get_logger();
if( db_connector )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression && !is_empty_field(db_field_name) && model_env->field_value_helper_tab )
{
std::vector<FieldValueHelper> & helper_tab = *model_env->field_value_helper_tab;
if( (size_t)model_env->field_index == helper_tab.size() )
{
ModelEnv model_env_local;
model_env_local.copy_global_objects(*model_env);
model_env_local.has_primary_key_set = field_model.has_primary_key_set;
model_env_local.model_work_mode = MORM_MODEL_WORK_MODE_SET_FIELD_VALUE;
model_env_local.field_value_helper_tab = &helper_tab;
model_env_local.field_index = 0;
field_model.model_env = &model_env_local;
field_model.map_fields();
if( (size_t)field_model.model_env->field_index != helper_tab.size() && log )
{
if( model_env->table_name.empty() )
table_name(model_env->table_name);
if( field_model.model_env->table_name.empty() )
field_model.table_name(field_model.model_env->table_name);
(*log) << PT::Log::log1 << "Morm: primary key in " << model_env->table_name << " consists of " << model_env->field_index << " column(s)"
<< " but in " << field_model.model_env->table_name << " there are only "
<< field_model.model_env->field_index << " matching column(s)" << PT::Log::logend;
}
field_model.model_env = nullptr;
}
else
if( log )
{
table_name(model_env->table_name);
(*log) << PT::Log::log1 << "Morm: primary key in " << model_env->table_name << " consists of incorrect number of columns"
<< ", expected " << helper_tab.size() << " column(s) but got " << model_env->field_index << PT::Log::logend;
}
}
}
}
void field_model_set_parent_key(const wchar_t * db_field_name, Model & field_model)
{
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);
field_model_set_parent_key_in_child(db_field_name, field_model);
model_env->field_value_helper_tab = nullptr;
}
void field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model)
{
if( !is_empty_field(db_field_name) )
{
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_INSERT )
{
field_model.insert_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_UPDATE )
{
field_model.update_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_REMOVE )
{
field_model.remove_tree(true);
}
if( model_env->model_work_submode == MORM_MODEL_WORK_SUBMODE_SAVE )
{
field_model.save_tree(true);
}
}
}
void field_model_generate_flat_string(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable)
{
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) )
{
if( model_env->dump_mode || field_model.save_mode == DO_INSERT_ON_SAVE || field_model.save_mode == DO_UPDATE_ON_SAVE )
{
field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING;
flat_expression->field_model(flat_field_name, field_model, insertable, updatable, false, model_env);
}
}
}
}
void field_model_generate_db_sql(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key)
{
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) )
{
field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL;
field_model.table_name(field_model.model_env->table_name);
db_expression->prepare_short_table_name(field_model.model_env->table_name, field_model.model_env->table_name_short);
if( db_expression->get_output_type() == MORM_OUTPUT_TYPE_SELECT_COLUMNS )
{
field_model_left_join(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key, db_expression);
}
if( has_foreign_key )
{
if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_FIELDS && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT )
{
if( insertable )
{
int not_used_object = 0;
db_expression->field(db_field_name, not_used_object, insertable, updatable, false, model_env);
}
}
if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_VALUES && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT )
{
if( insertable )
{
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_INSERT_PRIMARY_KEY);
field_model.map_fields();
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_INSERT);
}
}
if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_FIELDS_VALUES && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_UPDATE )
{
if( updatable )
{
std::vector<const wchar_t *> key_fields;
key_fields.push_back(db_field_name); // at the moment only one key
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE_PRIMARY_KEY);
field_model.model_env->field_index = 0;
field_model.model_env->set_field_name_helper = &key_fields;
field_model.map_fields();
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE);
if( (size_t)field_model.model_env->field_index != key_fields.size() )
{
// IMPROVEME
// number of keys are different
// put error log here
}
}
}
}
if( db_expression->get_output_type() != MORM_OUTPUT_TYPE_JOIN_TABLES &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_PRIMARY_KEY &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_INSERT &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_UPDATE )
{
field_model.map_fields();
}
field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_NONE;
}
}
}
void field_model_clear_values(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key)
{
Clearer * clearer = model_connector->get_clearer();
if( clearer )
{
clearer->clear_model(field_model);
}
}
void field_model_read_values_from_queryresult(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key)
{
DbConnector * db_connector = model_connector->get_db_connector();
if( db_connector )
{
if( !is_empty_field(db_field_name) )
{
DbExpression * db_expression = db_connector->get_expression();
if( db_expression )
{
if( model_env->cursor_helper &&
!model_env->cursor_helper->has_autogenerated_select &&
model_env->cursor_helper->use_table_prefix_for_fetching_values )
{
field_model.prepare_table_names();
}
field_model.before_select();
field_model.map_values_from_query();
if( field_model.found() )
{
field_model.after_select();
}
}
}
}
}
void field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key)
{
if( model_connector && model_env )
{
ModelEnv model_env_local;
model_env_local.copy_global_objects(*model_env);
field_model.model_env = &model_env_local;
field_model.model_env->has_primary_key_set = field_model.has_primary_key_set;
field_model.set_connector(model_connector);
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_PARENT_ID )
{
if( !has_foreign_key )
{
field_model_set_parent_key(db_field_name, field_model);
}
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_ITERATE_THROUGH_CHILDS_WITH_FOREIGN_KEY )
{
if( has_foreign_key )
{
field_model_iterate_through_childs(db_field_name, field_model);
}
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_ITERATE_THROUGH_CHILDS_WITHOUT_FOREIGN_KEY )
{
if( !has_foreign_key )
{
field_model_iterate_through_childs(db_field_name, field_model);
}
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_model_generate_flat_string(db_field_name, flat_field_name, field_model, insertable, updatable);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_DB_SQL )
{
field_model_generate_db_sql(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_model_clear_values(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key);
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET )
{
field_model_read_values_from_queryresult(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key);
}
field_model.model_env = nullptr;
}
}
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)
{
if( !is_empty_field(db_field_name) )
{
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);
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, bool insertable, bool updatable)
{
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) )
{
// insertable and updatable will be ignored there
flat_expression->field_list(flat_field_name, field_container, insertable, updatable, false, 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, bool insertable, bool updatable)
{
if( model_connector && model_env )
{
if( !is_empty_field(db_field_name) )
{
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
{
PT::Log * plog = model_connector->get_logger();
if( plog )
{
(*plog) << "Morm: ignoring " << db_field_name << " as this is not a container with Model objects" << PT::Log::logend;
}
}
}
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_list_generate_flat_string(flat_field_name, field_container, insertable, updatable);
}
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, bool is_primary_key)
{
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);
}
}
else
{
if( 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, bool is_primary_key)
{
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 )
{
// CHECK what about escaping field names here?
std::wstring table_field_name;
PT::TextStream table_field_name_str;
put_table_name_with_index(table_field_name_str);
table_field_name_str << '.';
table_field_name_str << field_name;
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);
}
}
else
{
if( is_primary_key )
{
model_env->has_primary_key_set = false;
}
}
}
}
virtual void set_parent_key_in_childs()
{
if( model_env )
{
model_env->model_work_mode = MORM_MODEL_WORK_MODE_SET_PARENT_ID;
map_fields();
}
}
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)
{
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);
}
}
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING )
{
field_generic(L"", flat_field_name, field_value, false, false, false);
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET )
{
field_generic(new_column_name, L"", field_value, false, false, false);
}
else
if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE )
{
field_generic(L"", L"", field_value, false, false, false); // 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 prepare_table_names(bool prepare_table_index = true);
virtual void put_table_name_with_index(PT::TextStream & str);
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);
template<typename ModelClass> friend class Finder;
template<typename ModelClass> friend class Cursor;
friend class BaseExpression;
};
} // namespace
#endif