From c7797ff2f17119df0b8f8d279777bfda2969c6fc Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Wed, 12 May 2021 00:27:35 +0200 Subject: [PATCH] fixed #2: escape tables/columns names in Finder left join queries some methods moved from model.h to model.cpp and from baseexpression.h to baseexpression.cpp --- src/baseexpression.cpp | 194 +++++++++++++++++ src/baseexpression.h | 80 ++----- src/finder.h | 6 +- src/model.cpp | 451 +++++++++++++++++++++++++++++++++++++-- src/model.h | 467 ++--------------------------------------- 5 files changed, 678 insertions(+), 520 deletions(-) diff --git a/src/baseexpression.cpp b/src/baseexpression.cpp index 0c56c0f..11ef30d 100644 --- a/src/baseexpression.cpp +++ b/src/baseexpression.cpp @@ -74,6 +74,20 @@ int BaseExpression::get_work_mode() +PT::TextStream * BaseExpression::get_text_stream() +{ + return out_stream; +} + +void BaseExpression::set_text_stream(PT::TextStream * out_stream) +{ + this->out_stream = out_stream; +} + + + + + PT::TextStream * BaseExpression::get_current_stream() { return out_stream; @@ -758,6 +772,186 @@ void BaseExpression::after_field_value_string(FT field_type) } +void BaseExpression::put_schema_table(const wchar_t * schema_name, const wchar_t * table_name) +{ + if( out_stream ) + { + before_first_part_long_field_name(); + esc(schema_name, *out_stream); + after_first_part_long_field_name(); + + (*out_stream) << '.'; + + before_second_part_long_field_name(); + esc(table_name, *out_stream); + after_second_part_long_field_name(); + } +} + + +void BaseExpression::put_schema_table(const PT::WTextStream & schema_name, const PT::WTextStream & table_name) +{ + if( out_stream ) + { + before_first_part_long_field_name(); + esc(schema_name, *out_stream); + after_first_part_long_field_name(); + + (*out_stream) << '.'; + + before_second_part_long_field_name(); + esc(table_name, *out_stream); + after_second_part_long_field_name(); + } +} + + +void BaseExpression::put_table(const wchar_t * table_name) +{ + if( out_stream ) + { + before_short_field_name(); + esc(table_name, *out_stream); + after_short_field_name(); + } +} + + +void BaseExpression::put_table(const PT::WTextStream & table_name) +{ + if( out_stream ) + { + before_short_field_name(); + esc(table_name, *out_stream); + after_short_field_name(); + } +} + + +void BaseExpression::put_table_with_index(const wchar_t * table_name, int index) +{ + if( out_stream ) + { + before_short_field_name(); + esc(table_name, *out_stream); + + if( index > 1 ) + { + (*out_stream) << index; + } + + after_short_field_name(); + } +} + + +void BaseExpression::put_table_with_index(const PT::WTextStream & table_name, int index) +{ + if( out_stream ) + { + before_short_field_name(); + esc(table_name, *out_stream); + + if( index > 1 ) + { + (*out_stream) << index; + } + + after_short_field_name(); + } +} + + +void BaseExpression::put_table_with_index_and_field(const wchar_t * table_name, int index, const wchar_t * field_name, const FT & field_type) +{ + if( out_stream ) + { + put_table_with_index(table_name, index); + (*out_stream) << '.'; + + // IMPROVE ME + // put_field_name seems to be too complicated, it is needed to check there whether field_name is long or short? + put_field_name(field_name, field_type, nullptr); + } +} + +void BaseExpression::put_table_with_index_and_field(const PT::WTextStream & table_name, int index, const wchar_t * field_name, const FT & field_type) +{ + if( out_stream ) + { + put_table_with_index(table_name, index); + (*out_stream) << '.'; + put_field_name(field_name, field_type, nullptr); + } +} + + + + +void BaseExpression::schema_table_to_stream(PT::TextStream & stream, const wchar_t * schema_name, const wchar_t * table_name) +{ + this->out_stream = &stream; + put_schema_table(schema_name, table_name); + this->out_stream = nullptr; +} + + +void BaseExpression::schema_table_to_stream(PT::TextStream & stream, const PT::WTextStream & schema_name, const PT::WTextStream & table_name) +{ + this->out_stream = &stream; + put_schema_table(schema_name, table_name); + this->out_stream = nullptr; +} + + +void BaseExpression::table_to_stream(PT::TextStream & stream, const wchar_t * table_name) +{ + this->out_stream = &stream; + put_table(table_name); + this->out_stream = nullptr; +} + + +void BaseExpression::table_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name) +{ + this->out_stream = &stream; + put_table(table_name); + this->out_stream = nullptr; +} + + +void BaseExpression::table_with_index_to_stream(PT::TextStream & stream, const wchar_t * table_name, int index) +{ + this->out_stream = &stream; + put_table_with_index(table_name, index); + this->out_stream = nullptr; +} + + +void BaseExpression::table_with_index_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name, int index) +{ + this->out_stream = &stream; + put_table_with_index(table_name, index); + this->out_stream = nullptr; +} + + +void BaseExpression::table_with_index_and_field_to_stream(PT::TextStream & stream, const wchar_t * table_name, int index, const wchar_t * field_name, const FT & field_type) +{ + this->out_stream = &stream; + put_table_with_index_and_field(table_name, index, field_name, field_type); + this->out_stream = nullptr; +} + + +void BaseExpression::table_with_index_and_field_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name, int index, const wchar_t * field_name, const FT & field_type) +{ + this->out_stream = &stream; + put_table_with_index_and_field(table_name, index, field_name, field_type); + this->out_stream = nullptr; +} + + } diff --git a/src/baseexpression.h b/src/baseexpression.h index e06758c..23a7a76 100644 --- a/src/baseexpression.h +++ b/src/baseexpression.h @@ -60,6 +60,9 @@ public: virtual void set_work_mode(int work_mode); virtual int get_work_mode(); + virtual PT::TextStream * get_text_stream(); + virtual void set_text_stream(PT::TextStream * out_stream); + virtual void clear(); virtual void generate_from_model(PT::TextStream & stream, Model & model); @@ -220,67 +223,23 @@ public: } - void table_to_stream(PT::TextStream & stream, const wchar_t * schema_name, const wchar_t * table_name, ModelEnv * model_env) - { - this->out_stream = &stream; - //table(table_name, model_env); + virtual void put_schema_table(const wchar_t * schema_name, const wchar_t * table_name); + virtual void put_schema_table(const PT::WTextStream & schema_name, const PT::WTextStream & table_name); + virtual void put_table(const wchar_t * table_name); + virtual void put_table(const PT::WTextStream & table_name); + virtual void put_table_with_index(const wchar_t * table_name, int index); + virtual void put_table_with_index(const PT::WTextStream & table_name, int index); + virtual void put_table_with_index_and_field(const wchar_t * table_name, int index, const wchar_t * field_name, const FT & field_type); + virtual void put_table_with_index_and_field(const PT::WTextStream & table_name, int index, const wchar_t * field_name, const FT & field_type); - before_first_part_long_field_name(); - esc(schema_name, *out_stream); - after_first_part_long_field_name(); - - (*out_stream) << '.'; - - before_second_part_long_field_name(); - esc(table_name, *out_stream); - after_second_part_long_field_name(); - - this->out_stream = nullptr; - } - - void table_to_stream(PT::TextStream & stream, const PT::WTextStream & schema_name, const PT::WTextStream & table_name, ModelEnv * model_env) - { - this->out_stream = &stream; - //table(table_name, model_env); - - before_first_part_long_field_name(); - esc(schema_name, *out_stream); - after_first_part_long_field_name(); - - (*out_stream) << '.'; - - before_second_part_long_field_name(); - esc(table_name, *out_stream); - after_second_part_long_field_name(); - - this->out_stream = nullptr; - } - - void table_to_stream(PT::TextStream & stream, const wchar_t * table_name, ModelEnv * model_env) - { - this->out_stream = &stream; - //table(table_name, model_env); - - if( is_long_field_name(table_name) ) - put_long_field_name(table_name); - else - put_short_field_name(table_name, model_env); - - this->out_stream = nullptr; - } - - - void table_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name, ModelEnv * model_env) - { - this->out_stream = &stream; - //table(table_name, model_env); - - before_short_field_name(); - esc(table_name, *out_stream); - after_short_field_name(); - - this->out_stream = nullptr; - } + virtual void schema_table_to_stream(PT::TextStream & stream, const wchar_t * schema_name, const wchar_t * table_name); + virtual void schema_table_to_stream(PT::TextStream & stream, const PT::WTextStream & schema_name, const PT::WTextStream & table_name); + virtual void table_to_stream(PT::TextStream & stream, const wchar_t * table_name); + virtual void table_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name); + virtual void table_with_index_to_stream(PT::TextStream & stream, const wchar_t * table_name, int index); + virtual void table_with_index_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name, int index); + virtual void table_with_index_and_field_to_stream(PT::TextStream & stream, const wchar_t * table_name, int index, const wchar_t * field_name, const FT & field_type); + virtual void table_with_index_and_field_to_stream(PT::TextStream & stream, const PT::WTextStream & table_name, int index, const wchar_t * field_name, const FT & field_type); /* @@ -340,6 +299,7 @@ protected: virtual void field_after(); virtual void put_field_name(const wchar_t * field_name, FT field_type, ModelEnv * model_env); + virtual void save_foreign_key(const wchar_t * field_name, FT field_type, ModelEnv * model_env); virtual void dump_additional_info(Model & model); diff --git a/src/finder.h b/src/finder.h index 8d936b7..2ccfc9e 100644 --- a/src/finder.h +++ b/src/finder.h @@ -240,15 +240,15 @@ public: if( !model.model_env->schema_name.empty() ) { - db_expression->table_to_stream(*out_stream, model.model_env->schema_name, model.model_env->table_name, &model_env); + db_expression->schema_table_to_stream(*out_stream, model.model_env->schema_name, model.model_env->table_name); } else { - db_expression->table_to_stream(*out_stream, model.model_env->table_name, &model_env); + db_expression->table_to_stream(*out_stream, model.model_env->table_name); } (*out_stream) << " AS "; - db_expression->table_to_stream(*out_stream, model.model_env->table_name, &model_env); + db_expression->table_to_stream(*out_stream, model.model_env->table_name); if( !finder_helper.join_tables_str.empty() ) { diff --git a/src/model.cpp b/src/model.cpp index 096bf1a..f9e9845 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -906,19 +906,6 @@ bool Model::is_the_same_field(const wchar_t * field1, const wchar_t * field2) } -void Model::put_table_name_with_index(PT::TextStream & str) -{ - if( model_env ) - { - str << model_env->table_name; - - if( model_env->table_index > 1 ) - { - str << model_env->table_index; - } - } -} - PT::WTextStream Model::get_table_name(bool put_schema_name) { @@ -1014,7 +1001,445 @@ void Model::put_fields_to_log(PT::Log & log, const wchar_t * db_field_name, cons } +/* + * 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 Model::field_model_left_join(const wchar_t * db_field_name, Model & field_model, FT field_type, 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->add_table_name_to_finder_helper(); + join_tables_str << "LEFT JOIN "; + + PT::TextStream * db_expression_stream = db_expression->get_text_stream(); + 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->schema_table_to_stream(join_tables_str, field_model.model_env->schema_name, field_model.model_env->table_name); + join_tables_str << " AS "; + db_expression->table_with_index_to_stream(join_tables_str, field_model.model_env->table_name, field_model.model_env->table_index); + + 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); + db_expression->set_text_stream(db_expression_stream); // restore original value because schema_table_to_stream() will set the stream to null + + if( field_type.is_foreign_key() ) + { + field_model.map_fields(); + + join_tables_str << " ON "; + db_expression->table_with_index_and_field_to_stream(join_tables_str, model_env->table_name, model_env->table_index, db_field_name, field_type); + join_tables_str << " = "; + + db_expression->table_with_index_to_stream(join_tables_str, field_model.model_env->table_name, field_model.model_env->table_index); + 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 "; + db_expression->table_with_index_to_stream(join_tables_str, model_env->table_name, model_env->table_index); + 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 << " = "; + db_expression->table_with_index_and_field_to_stream(join_tables_str, field_model.model_env->table_name, field_model.model_env->table_index, db_field_name, field_type); + } + + 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); + db_expression->set_text_stream(db_expression_stream); + } +} + + +/* + * first we iterate through fields and save primary key values to helper_tab + */ +void Model::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) << PT::Log::log1 << "Morm: I cannot find a primary key in " << get_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 Model::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.prepare_table(); + + field_model.map_fields(); + + if( (size_t)field_model.model_env->field_index != helper_tab.size() && log ) + { + if( field_model.model_env->field_index == 0 ) + { + (*log) << PT::Log::log1 << "Morm: there is no a foreign key in " << field_model.get_table_name() + << " called " << db_field_name << " pointing to " << get_table_name() << PT::Log::logend; + } + else + { + (*log) << PT::Log::log1 << "Morm: primary key in " << get_table_name() << " consists of " << model_env->field_index << " column(s)" + << " but foreign key in " << field_model.get_table_name() << " consists of " + << field_model.model_env->field_index << " column(s)" << PT::Log::logend; + } + } + + field_model.model_env = nullptr; + } + else + if( log ) + { + (*log) << PT::Log::log1 << "Morm: primary key in " << get_table_name() << " consists of incorrect number of columns" + << ", expected " << helper_tab.size() << " column(s) but got " << model_env->field_index << PT::Log::logend; + } + } + } +} + + +void Model::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 Model::field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model) +{ + 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 Model::field_model_generate_flat_string(const wchar_t * flat_field_name, Model & field_model, FT field_type) +{ + FlatConnector * flat_connector = model_connector->get_flat_connector(); + + if( flat_connector ) + { + FlatExpression * flat_expression = flat_connector->get_expression(); + + if( flat_expression ) + { + 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, field_type, model_env); + } + } + } +} + + +void Model::field_model_generate_db_sql(const wchar_t * db_field_name, Model & field_model, 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) ) + { + field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL; + + if( db_expression->get_output_type() == MORM_OUTPUT_TYPE_SELECT_COLUMNS ) + { + field_model_left_join(db_field_name, field_model, field_type, db_expression); + } + + if( field_type.is_foreign_key() ) + { + if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_FIELDS && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT ) + { + if( field_type.is_insertable() ) + { + int not_used_object = 0; + db_expression->field(db_field_name, not_used_object, field_type, model_env); + } + } + + if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_VALUES && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT ) + { + if( field_type.is_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( field_type.is_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 Model::field_model_clear_values(Model & field_model) +{ + Clearer * clearer = model_connector->get_clearer(); + + if( clearer ) + { + clearer->clear_model(field_model); + } +} + + +void Model::field_model_read_values_from_queryresult(const wchar_t * db_field_name, Model & field_model, FT field_type) +{ + DbConnector * db_connector = model_connector->get_db_connector(); + + if( db_connector ) + { + 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.model_env->add_table_name_to_finder_helper(); + } + + field_model.before_select(); + field_model.map_values_from_query(); + + if( field_model.found() ) + { + field_model.after_select(); + } + } + } +} + + +void Model::field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, FT field_type) +{ + 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( !is_empty_field(db_field_name) ) + { + field_model.prepare_table(); + + if( field_type.is_foreign_key() || field_type.is_foreign_key_in_child() ) + { + field_model_for_db(db_field_name, field_model, field_type); + } + else + { + PT::Log * plog = model_connector->get_logger(); + + if( plog ) + { + (*plog) << PT::Log::log1 << "Morm: error in " << get_table_name_with_field(db_field_name) + << " field, you should set FT::is_foreign_key or FT::is_foreign_key_in_child flag for a model child object" << PT::Log::logend; + } + } + } + + if( !is_empty_field(flat_field_name) ) + { + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING ) + { + // calling field_model.prepare_table(); is not needed in generating strings (at least for json/space formats) + field_model_generate_flat_string(flat_field_name, field_model, field_type); + } + } + + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE ) + { + field_model_clear_values(field_model); + } + + field_model.model_env = nullptr; + } +} + + +void Model::field_model_for_db(const wchar_t * db_field_name, Model & field_model, FT field_type) +{ + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_PARENT_ID ) + { + if( field_type.is_foreign_key_in_child() ) + { + 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( field_type.is_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( field_type.is_foreign_key_in_child() ) + { + field_model_iterate_through_childs(db_field_name, field_model); + } + } + + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_DB_SQL ) + { + field_model_generate_db_sql(db_field_name, field_model, field_type); + } + + + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET ) + { + field_model_read_values_from_queryresult(db_field_name, field_model, field_type); + } +} + + +void Model::set_parent_key_in_childs() +{ + if( model_env ) + { + model_env->model_work_mode = MORM_MODEL_WORK_MODE_SET_PARENT_ID; + map_fields(); + } +} } // namespace diff --git a/src/model.h b/src/model.h index ebb4c7a..a9d7e57 100644 --- a/src/model.h +++ b/src/model.h @@ -219,7 +219,7 @@ protected: ///////////////////////////////// /* * IMPLEMENT ME - * field methods for such field_values: signed char, wchar_t, char16_t, char32_t, std::u16string, std::u32string + * 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, FT field_type = FT::default_type) @@ -319,7 +319,6 @@ protected: void field(const wchar_t * field_name, Model & field_value, FT field_type = FT::default_type) { - // has_foreign_key was here field_model(field_name, field_name, field_value, field_type); } @@ -633,429 +632,17 @@ protected: } - /* - * 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, Model & field_model, FT field_type, 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->add_table_name_to_finder_helper(); - - 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( field_type.is_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) << PT::Log::log1 << "Morm: I cannot find a primary key in " << get_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.prepare_table(); - - field_model.map_fields(); - - if( (size_t)field_model.model_env->field_index != helper_tab.size() && log ) - { - if( field_model.model_env->field_index == 0 ) - { - (*log) << PT::Log::log1 << "Morm: there is no a foreign key in " << field_model.get_table_name() - << " called " << db_field_name << " pointing to " << get_table_name() << PT::Log::logend; - } - else - { - (*log) << PT::Log::log1 << "Morm: primary key in " << get_table_name() << " consists of " << model_env->field_index << " column(s)" - << " but foreign key in " << field_model.get_table_name() << " consists of " - << field_model.model_env->field_index << " column(s)" << PT::Log::logend; - } - } - - field_model.model_env = nullptr; - } - else - if( log ) - { - (*log) << PT::Log::log1 << "Morm: primary key in " << get_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( 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 * flat_field_name, Model & field_model, FT field_type) - { - FlatConnector * flat_connector = model_connector->get_flat_connector(); - - if( flat_connector ) - { - FlatExpression * flat_expression = flat_connector->get_expression(); - - if( flat_expression ) - { - 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, field_type, model_env); - } - } - } - } - - - void field_model_generate_db_sql(const wchar_t * db_field_name, Model & field_model, 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) ) - { - field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL; - - if( db_expression->get_output_type() == MORM_OUTPUT_TYPE_SELECT_COLUMNS ) - { - field_model_left_join(db_field_name, field_model, field_type, db_expression); - } - - if( field_type.is_foreign_key() ) - { - if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_FIELDS && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT ) - { - if( field_type.is_insertable() ) - { - int not_used_object = 0; - db_expression->field(db_field_name, not_used_object, field_type, model_env); - } - } - - if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_VALUES && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT ) - { - if( field_type.is_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( field_type.is_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(Model & field_model) - { - 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, Model & field_model, FT field_type) - { - DbConnector * db_connector = model_connector->get_db_connector(); - - if( db_connector ) - { - 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.model_env->add_table_name_to_finder_helper(); - } - - 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, FT field_type) - { - 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( !is_empty_field(db_field_name) ) - { - field_model.prepare_table(); - - if( field_type.is_foreign_key() || field_type.is_foreign_key_in_child() ) - { - field_model_for_db(db_field_name, field_model, field_type); - } - else - { - PT::Log * plog = model_connector->get_logger(); - - if( plog ) - { - (*plog) << PT::Log::log1 << "Morm: error in " << get_table_name_with_field(db_field_name) - << " field, you should set FT::is_foreign_key or FT::is_foreign_key_in_child flag for a model child object" << PT::Log::logend; - } - } - } - - if( !is_empty_field(flat_field_name) ) - { - if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING ) - { - // calling field_model.prepare_table(); is not needed in generating strings (at least for json/space formats) - field_model_generate_flat_string(flat_field_name, field_model, field_type); - } - } - - if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_CLEARING_VALUE ) - { - field_model_clear_values(field_model); - } - - field_model.model_env = nullptr; - } - } - - - void field_model_for_db(const wchar_t * db_field_name, Model & field_model, FT field_type) - { - if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_PARENT_ID ) - { - if( field_type.is_foreign_key_in_child() ) - { - 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( field_type.is_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( field_type.is_foreign_key_in_child() ) - { - field_model_iterate_through_childs(db_field_name, field_model); - } - } - - if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_GENERATING_DB_SQL ) - { - field_model_generate_db_sql(db_field_name, field_model, field_type); - } - - - if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET ) - { - field_model_read_values_from_queryresult(db_field_name, field_model, field_type); - } - } + void field_model_left_join(const wchar_t * db_field_name, Model & field_model, FT field_type, DbExpression * db_expression); + void field_model_save_key(const wchar_t * db_field_name); + void field_model_set_parent_key_in_child(const wchar_t * db_field_name, Model & field_model); + void field_model_set_parent_key(const wchar_t * db_field_name, Model & field_model); + void field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model); + void field_model_generate_flat_string(const wchar_t * flat_field_name, Model & field_model, FT field_type); + void field_model_generate_db_sql(const wchar_t * db_field_name, Model & field_model, 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, FT field_type); + void field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, FT field_type); + void field_model_for_db(const wchar_t * db_field_name, Model & field_model, FT field_type); template @@ -1259,16 +846,18 @@ protected: if( model_env->cursor_helper->use_table_prefix_for_fetching_values && model_env->finder_helper ) { - // CHECK what about escaping field names here? + DbExpression * db_expression = db_connector->get_expression(); - std::wstring table_field_name; - PT::TextStream table_field_name_str; + if( db_expression ) + { + 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()); + // 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 { @@ -1295,15 +884,7 @@ protected: } - virtual void set_parent_key_in_childs() - { - if( model_env ) - { - model_env->model_work_mode = MORM_MODEL_WORK_MODE_SET_PARENT_ID; - map_fields(); - } - } - + virtual void set_parent_key_in_childs(); public: @@ -1385,8 +966,6 @@ 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_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);