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
This commit is contained in:
Tomasz Sowa 2021-05-12 00:27:35 +02:00
parent 009955a0fd
commit c7797ff2f1
5 changed files with 678 additions and 520 deletions

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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() )
{

View File

@ -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<FieldValueHelper> & helper_tab = *model_env->field_value_helper_tab;
if( (size_t)model_env->field_index == helper_tab.size() )
{
ModelEnv model_env_local;
model_env_local.copy_global_objects(*model_env);
model_env_local.has_primary_key_set = field_model.has_primary_key_set;
model_env_local.model_work_mode = MORM_MODEL_WORK_MODE_SET_FIELD_VALUE;
model_env_local.field_value_helper_tab = &helper_tab;
model_env_local.field_index = 0;
field_model.model_env = &model_env_local;
field_model.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<FieldValueHelper> helper_tab;
helper_tab.push_back(helper);
// only one column at the moment, in the future we can have a primary key from more than one column
model_env->field_value_helper_tab = &helper_tab;
field_model_save_key(db_field_name);
field_model_set_parent_key_in_child(db_field_name, field_model);
model_env->field_value_helper_tab = nullptr;
}
void 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<const wchar_t *> key_fields;
key_fields.push_back(db_field_name); // at the moment only one key
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE_PRIMARY_KEY);
field_model.model_env->field_index = 0;
field_model.model_env->set_field_name_helper = &key_fields;
field_model.map_fields();
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE);
if( (size_t)field_model.model_env->field_index != key_fields.size() )
{
// IMPROVEME
// number of keys are different
// put error log here
}
}
}
}
if( db_expression->get_output_type() != MORM_OUTPUT_TYPE_JOIN_TABLES &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_PRIMARY_KEY &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_INSERT &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_UPDATE )
{
field_model.map_fields();
}
field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_NONE;
}
}
}
void 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

View File

@ -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<FieldValueHelper> & helper_tab = *model_env->field_value_helper_tab;
if( (size_t)model_env->field_index == helper_tab.size() )
{
ModelEnv model_env_local;
model_env_local.copy_global_objects(*model_env);
model_env_local.has_primary_key_set = field_model.has_primary_key_set;
model_env_local.model_work_mode = MORM_MODEL_WORK_MODE_SET_FIELD_VALUE;
model_env_local.field_value_helper_tab = &helper_tab;
model_env_local.field_index = 0;
field_model.model_env = &model_env_local;
field_model.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<FieldValueHelper> helper_tab;
helper_tab.push_back(helper);
// only one column at the moment, in the future we can have a primary key from more than one column
model_env->field_value_helper_tab = &helper_tab;
field_model_save_key(db_field_name);
field_model_set_parent_key_in_child(db_field_name, field_model);
model_env->field_value_helper_tab = nullptr;
}
void field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model)
{
if( 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<const wchar_t *> key_fields;
key_fields.push_back(db_field_name); // at the moment only one key
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE_PRIMARY_KEY);
field_model.model_env->field_index = 0;
field_model.model_env->set_field_name_helper = &key_fields;
field_model.map_fields();
db_expression->set_output_type(MORM_OUTPUT_TYPE_DB_UPDATE);
if( (size_t)field_model.model_env->field_index != key_fields.size() )
{
// IMPROVEME
// number of keys are different
// put error log here
}
}
}
}
if( db_expression->get_output_type() != MORM_OUTPUT_TYPE_JOIN_TABLES &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_PRIMARY_KEY &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_INSERT &&
db_expression->get_output_type() != MORM_OUTPUT_TYPE_DB_UPDATE )
{
field_model.map_fields();
}
field_model.model_env->model_work_mode = MORM_MODEL_WORK_MODE_NONE;
}
}
}
void field_model_clear_values(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<typename ModelContainer, typename ModelContainerType>
@ -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);