1019 lines
34 KiB
C++
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, const FT & field_type);
|
|
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
|