Added flag has_primary_key_set to Model

Now we know whether the primary key is defined or not
and we do not allow to make update/remove if the key is not defined.

And when doing insert/update we can put NULL if child models don't have
the primary key set (fields with has_foreign_key set to true).

Now in after_select() we should also set has_primary_key_set flag
or just call get_last_sequence_for_primary_key instead of get_last_sequence.

fixed: added prefix +00 when serializing PT::Date to PostgreSQL (time zone)
(for a column with a time zone there was a wrong value saved)
This commit is contained in:
2021-03-09 18:10:34 +01:00
parent ff551a64b8
commit 133a45c84b
18 changed files with 474 additions and 110 deletions

View File

@@ -118,6 +118,7 @@ void BaseExpression::dump_additional_info(Model & model)
if( model.model_env && model.model_env->dump_mode )
{
field(L"model_save_mode", model.save_mode, false, false, false, model.model_env);
field(L"has_primary_key_set", model.has_primary_key_set, false, false, false, model.model_env);
}
}

View File

@@ -91,7 +91,7 @@ public:
else
if( work_mode == MORM_WORK_MODE_MODEL_VALUES )
{
put_field_value(field_value);
put_field_value_or_null(field_value, is_primary_key, model_env);
}
else
if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES )
@@ -102,7 +102,7 @@ public:
{
put_field_name((*model_env->set_field_name_helper)[model_env->field_index], model_env);
put_name_value_separator();
put_field_value(field_value);
put_field_value_or_null(field_value, is_primary_key, model_env);
}
model_env->field_index += 1;
@@ -111,7 +111,7 @@ public:
{
put_field_name(field_name, model_env);
put_name_value_separator();
put_field_value(field_value);
put_field_value_or_null(field_value, is_primary_key, model_env);
}
}
@@ -121,6 +121,22 @@ public:
}
template<typename FieldValue>
void put_field_value_or_null(const FieldValue & field_value, bool is_primary_key, ModelEnv * model_env)
{
if( is_primary_key )
{
if( model_env && model_env->has_primary_key_set )
put_field_value(field_value);
else
put_null_value();
}
else
{
put_field_value(field_value);
}
}
template<typename FieldValue>
void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::set<FieldValue> & container, ModelEnv * model_env)
{
@@ -282,6 +298,12 @@ protected:
}
virtual void put_null_value()
{
(*out_stream) << "null";
}
virtual void before_field_value_list()
{
}

View File

@@ -110,7 +110,7 @@ public:
template<typename FieldValue>
void get_last_sequence(const wchar_t * sequence_table_name, FieldValue & field_value)
bool get_last_sequence(const wchar_t * sequence_table_name, FieldValue & field_value)
{
const char * val_str = query_last_sequence(sequence_table_name);
@@ -122,7 +122,11 @@ public:
{
(*log) << PT::Log::log3 << "Morm: sequence value: " << field_value << PT::Log::logend;
}
return true;
}
return false;
}

View File

@@ -105,8 +105,6 @@ void JSONExpression::after_second_part_long_field_name()
}
void JSONExpression::before_field_value_string()
{
(*out_stream) << "\"";

View File

@@ -43,6 +43,7 @@ Model::Model()
model_connector = nullptr;
model_env = nullptr;
save_mode = DO_INSERT_ON_SAVE;
has_primary_key_set = false;
}
@@ -50,7 +51,8 @@ Model::Model(const Model & m)
{
model_connector = m.model_connector;
save_mode = m.save_mode;
model_env = m.model_env; // or just set to null?
model_env = nullptr;
has_primary_key_set = m.has_primary_key_set;
}
@@ -71,6 +73,19 @@ Model::SaveMode Model::get_save_mode()
}
void Model::set_has_primary_key_set(bool has_primary_key)
{
this->has_primary_key_set = has_primary_key;
}
bool Model::get_has_primary_key_set()
{
return this->has_primary_key_set;
}
void Model::mark_to_delete()
{
save_mode = DO_DELETE_ON_SAVE;
@@ -140,8 +155,10 @@ void Model::to_text(PT::TextStream & stream, ModelData * model_data, bool clear_
ModelEnv model_env_local;
model_env = &model_env_local;
model_env->has_primary_key_set = has_primary_key_set;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_FLAT_STRING;
model_env->dump_mode = dump_mode;
model_env->model_data = model_data;
if( model_connector )
{
@@ -149,13 +166,18 @@ void Model::to_text(PT::TextStream & stream, ModelData * model_data, bool clear_
if( flat_connector )
{
model_env->model_data = model_data;
flat_connector->to_text(stream, *this);
model_env->model_data = nullptr;
try
{
flat_connector->to_text(stream, *this);
}
catch(...)
{
model_env = nullptr;
throw;
}
}
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
}
@@ -221,6 +243,7 @@ void Model::generate_insert_query(PT::TextStream & stream, ModelData * model_dat
{
ModelEnv model_env_local;
model_env = &model_env_local;
model_env->has_primary_key_set = has_primary_key_set;
model_env->model_data = model_data;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL;
@@ -230,11 +253,18 @@ void Model::generate_insert_query(PT::TextStream & stream, ModelData * model_dat
if( db_connector )
{
db_connector->generate_insert_query(stream, *this);
try
{
db_connector->generate_insert_query(stream, *this);
}
catch(...)
{
model_env = nullptr;
throw;
}
}
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
}
@@ -257,11 +287,19 @@ bool Model::insert(ModelData * model_data, bool insert_whole_tree)
model_env = &model_env_local;
model_env->model_data = model_data;
bool status = insert_tree(insert_whole_tree);
bool status = false;
try
{
status = insert_tree(insert_whole_tree);
}
catch(...)
{
model_env = nullptr;
throw;
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
return status;
}
@@ -271,6 +309,8 @@ bool Model::insert(ModelData * model_data, bool insert_whole_tree)
bool Model::insert_tree(bool insert_whole_tree)
{
bool result = false;
has_primary_key_set = false; // the key will be overwritten (the database will create a new key)
model_env->has_primary_key_set = false;
if( insert_whole_tree )
{
@@ -296,9 +336,21 @@ bool Model::insert_tree(bool insert_whole_tree)
if( result )
{
save_mode = DO_UPDATE_ON_SAVE; // IMPROVE ME check if there is a primary key
/*
* after_insert() should read the new primary key and set has_primary_key_set flag if the key was read correctly
*/
after_insert();
set_parent_key_in_childs();
model_env->has_primary_key_set = has_primary_key_set;
if( has_primary_key_set )
{
save_mode = DO_UPDATE_ON_SAVE;
set_parent_key_in_childs(); // may it would be better to set it even if we do not have a primary key? set it to zero or something?
}
else
{
save_mode = DO_NOTHING_ON_SAVE;
}
}
else
{
@@ -323,6 +375,7 @@ void Model::generate_update_query(PT::TextStream & stream, ModelData * model_dat
{
ModelEnv model_env_local;
model_env = &model_env_local;
model_env->has_primary_key_set = has_primary_key_set;
model_env->model_data = model_data;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL;
@@ -359,11 +412,19 @@ bool Model::update(ModelData * model_data, bool update_whole_tree)
model_env = &model_env_local;
model_env->model_data = model_data;
bool status = update_tree(update_whole_tree);
bool status = false;
try
{
status = update_tree(update_whole_tree);
}
catch(...)
{
model_env = nullptr;
throw;
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
return status;
}
@@ -372,6 +433,13 @@ bool Model::update(ModelData * model_data, bool update_whole_tree)
bool Model::update_tree(bool update_whole_tree)
{
bool result = false;
model_env->has_primary_key_set = has_primary_key_set;
if( !has_primary_key_set )
{
put_to_log(L"Morm: call update but model doesn't have a primary key set");
return result;
}
if( update_whole_tree )
{
@@ -416,6 +484,7 @@ void Model::generate_remove_query(PT::TextStream & stream, ModelData * model_dat
{
ModelEnv model_env_local;
model_env = &model_env_local;
model_env->has_primary_key_set = has_primary_key_set;
model_env->model_data = model_data;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_GENERATING_DB_SQL;
@@ -453,11 +522,19 @@ bool Model::remove(ModelData * model_data, bool remove_whole_tree)
model_env = &model_env_local;
model_env->model_data = model_data;
bool status = remove_tree(remove_whole_tree);
bool status = false;
try
{
status = remove_tree(remove_whole_tree);
}
catch(...)
{
model_env = nullptr;
throw;
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
return status;
}
@@ -466,6 +543,13 @@ bool Model::remove(ModelData * model_data, bool remove_whole_tree)
bool Model::remove_tree(bool remove_whole_tree)
{
bool result = false;
model_env->has_primary_key_set = has_primary_key_set;
if( !has_primary_key_set )
{
put_to_log(L"Morm: call remove but model doesn't have a primary key set");
return result;
}
if( remove_whole_tree )
{
@@ -490,7 +574,9 @@ bool Model::remove_tree(bool remove_whole_tree)
if( result )
{
save_mode = DO_INSERT_ON_SAVE; // CHECKME may it would be better to set DO_NOTHING_ON_SAVE?
save_mode = DO_NOTHING_ON_SAVE;
has_primary_key_set = false;
model_env->has_primary_key_set = false;
after_remove();
}
else
@@ -531,11 +617,19 @@ bool Model::save(ModelData * model_data, bool save_whole_tree)
model_env = &model_env_local;
model_env->model_data = model_data;
bool status = save_tree(save_whole_tree);
bool status = false;
try
{
status = save_tree(save_whole_tree);
}
catch(...)
{
model_env = nullptr;
throw;
}
// what about if an exception was thrown? this pointer will not be null
model_env = nullptr;
return status;
}
@@ -544,6 +638,7 @@ bool Model::save(ModelData * model_data, bool save_whole_tree)
bool Model::save_tree(bool save_whole_tree)
{
bool result = false;
model_env->has_primary_key_set = has_primary_key_set;
if( save_whole_tree )
{
@@ -617,17 +712,20 @@ void Model::map_values_from_query()
if( model_env )
{
model_env->model_work_mode = MORM_MODEL_WORK_MODE_READING_VALUE_FROM_DB_RESULTSET;
model_env->all_fields_are_null = true;
model_env->was_primary_key_read = false; // whether or not there was at least one column with primary_key flag
model_env->has_primary_key_set = true; // whether all primary_columns were different than null
map_fields();
model_env->model_work_mode = MORM_MODEL_WORK_MODE_NONE;
if( model_env->all_fields_are_null )
if( model_env->was_primary_key_read && model_env->has_primary_key_set )
{
save_mode = DO_NOTHING_ON_SAVE;
has_primary_key_set = true;
save_mode = DO_UPDATE_ON_SAVE;
}
else
{
save_mode = DO_UPDATE_ON_SAVE;
has_primary_key_set = false;
save_mode = DO_NOTHING_ON_SAVE;
}
}
}
@@ -640,10 +738,20 @@ void Model::clear()
model_env = &model_env_local;
model_env->model_work_mode = MORM_MODEL_WORK_MODE_CLEARING_VALUE;
map_fields();
model_env = nullptr;
try
{
map_fields();
}
catch(...)
{
model_env = nullptr;
throw;
}
model_env = nullptr;
save_mode = DO_INSERT_ON_SAVE;
has_primary_key_set = false;
}
@@ -794,6 +902,20 @@ void Model::put_table_name_with_index(PT::TextStream & str)
}
void Model::put_to_log(const wchar_t * str)
{
if( model_connector )
{
PT::Log * log = model_connector->get_logger();
if( log )
{
(*log) << str << PT::Log::logend;
}
}
}
void Model::put_fields_to_log(PT::Log & log, const wchar_t * db_field_name, const wchar_t * flat_field_name)
{
bool was_db_field_put = false;

View File

@@ -67,6 +67,9 @@ public:
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();
@@ -176,8 +179,9 @@ public:
protected:
ModelConnector * model_connector;
SaveMode save_mode;
ModelEnv * model_env;
SaveMode save_mode;
bool has_primary_key_set;
@@ -568,12 +572,12 @@ protected:
{
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);
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);
get_value_by_field_name(db_field_name, field_value, is_primary_key);
}
}
}
@@ -596,6 +600,11 @@ protected:
{
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);
@@ -748,6 +757,7 @@ protected:
{
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;
@@ -875,32 +885,42 @@ protected:
{
if( db_expression->get_work_mode() == MORM_WORK_MODE_MODEL_FIELDS && db_expression->get_output_type() == MORM_OUTPUT_TYPE_DB_INSERT )
{
int not_used_object = 0;
db_expression->field(db_field_name, not_used_object, insertable, updatable, false, model_env);
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 )
{
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( 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 )
{
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() )
if( updatable )
{
// number of keys are different
// put error log here
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
}
}
}
}
@@ -969,6 +989,7 @@ protected:
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 )
@@ -1056,6 +1077,7 @@ protected:
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 )
@@ -1159,7 +1181,7 @@ protected:
template<typename FieldValue>
void get_value_by_field_index(int field_index, FieldValue & field_value)
void get_value_by_field_index(int field_index, FieldValue & field_value, bool is_primary_key)
{
DbConnector * db_connector = model_connector->get_db_connector();
@@ -1167,7 +1189,6 @@ protected:
{
if( !model_env->cursor_helper->query_result->is_null(field_index) )
{
model_env->all_fields_are_null = false;
const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(field_index);
if( val_str )
@@ -1175,12 +1196,19 @@ protected:
db_connector->get_value(val_str, field_value);
}
}
else
{
if( is_primary_key )
{
model_env->has_primary_key_set = false;
}
}
}
}
template<typename FieldValue>
void get_value_by_field_name(const wchar_t * field_name, FieldValue & field_value)
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();
@@ -1208,7 +1236,6 @@ protected:
if( column_index != -1 && !model_env->cursor_helper->query_result->is_null(column_index) )
{
model_env->all_fields_are_null = false;
const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(column_index);
if( val_str )
@@ -1216,6 +1243,13 @@ protected:
db_connector->get_value(val_str, field_value);
}
}
else
{
if( is_primary_key )
{
model_env->has_primary_key_set = false;
}
}
}
}
@@ -1234,7 +1268,7 @@ public:
template<typename FieldValue>
void get_last_sequence(const wchar_t * sequence_table_name, FieldValue & field_value)
bool get_last_sequence(const wchar_t * sequence_table_name, FieldValue & field_value)
{
if( model_connector )
{
@@ -1242,9 +1276,19 @@ public:
if( db_connector && !is_empty_field(sequence_table_name) )
{
db_connector->get_last_sequence(sequence_table_name, field_value);
return db_connector->get_last_sequence(sequence_table_name, field_value);
}
}
return false;
}
template<typename FieldValue>
bool get_last_sequence_for_primary_key(const wchar_t * sequence_table_name, FieldValue & field_value)
{
has_primary_key_set = get_last_sequence(sequence_table_name, field_value);
return has_primary_key_set;
}
@@ -1302,6 +1346,7 @@ protected:
virtual void put_table_name_with_index(PT::TextStream & str);
virtual void put_to_log(const wchar_t * str);
virtual void put_fields_to_log(PT::Log & log, const wchar_t * db_field_name, const wchar_t * flat_field_name);
template<typename ModelClass> friend class Finder;

View File

@@ -5,7 +5,7 @@
*/
/*
* Copyright (c) 2019, Tomasz Sowa
* Copyright (c) 2019-2021, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -63,7 +63,8 @@ public:
PT::TextStream table_name_short;
int table_index;
int field_index;
bool all_fields_are_null;
bool was_primary_key_read;
bool has_primary_key_set;
std::vector<const wchar_t *> * set_field_name_helper;
std::vector<FieldValueHelper> * field_value_helper_tab;
@@ -74,7 +75,6 @@ public:
clear();
}
~ModelEnv()
{
}
@@ -92,7 +92,8 @@ public:
set_field_name_helper = e.set_field_name_helper;
field_value_helper_tab = e.field_value_helper_tab;
field_index = e.field_index;
all_fields_are_null = e.all_fields_are_null;
was_primary_key_read = e.was_primary_key_read;
has_primary_key_set = e.has_primary_key_set;
// table_name and table_name_short don't have to bo copied
}
@@ -124,7 +125,8 @@ public:
set_field_name_helper = nullptr;
field_value_helper_tab = nullptr;
field_index = 0;
all_fields_are_null = false;
was_primary_key_read = false;
has_primary_key_set = false;
}

View File

@@ -152,6 +152,12 @@ void PostgreSQLExpression::esc(char val, PT::TextStream & stream)
}
void PostgreSQLExpression::esc(const PT::Date & date, PT::TextStream & stream)
{
stream << date << "+00";
}
DbExpression & PostgreSQLExpression::page(PT::TextStream & stream, size_t page_number, size_t page_size)
{
stream << " offset " << page_number << " limit " << page_size << " ";

View File

@@ -46,6 +46,8 @@ class PostgreSQLExpression : public DbExpression
public:
void esc(char val, PT::TextStream & stream);
void esc(const PT::Date & date, PT::TextStream & stream);
DbExpression & page(PT::TextStream & stream, size_t page_number, size_t page_size);