/* * This file is a part of morm * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * 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 #include #include #include #include #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 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 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 void field(const wchar_t * field_name, std::list & 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 void field(const wchar_t * field_name, std::vector & 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 void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::list & 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 void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::vector & 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 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 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 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 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 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 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 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 & 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 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 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 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 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 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 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 void field_list_clearing_values(ModelContainer & field_container) { Clearer * clearer = model_connector->get_clearer(); if( clearer ) { clearer->clear_container(field_container); } } template 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()) { 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 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 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 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 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 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 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 friend class Finder; template friend class Cursor; friend class BaseExpression; }; } // namespace #endif