diff --git a/.gitignore b/.gitignore index 2da86a3..6bbc54f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ .settings/ *.o *.a +log.txt +samples/log.txt +samples/mormsample diff --git a/samples/Makefile b/samples/Makefile index 0a3dd00..f4336cd 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -7,13 +7,13 @@ GLOBAL_WORKING_DIR := $(shell pwd)/../.. endif -CXX = g++7 +CXX = g++10 #CXX = clang++ # -fsanitize=address # -Wl,-rpath=/usr/local/lib/gcc5 or just compile with -static-libstdc++ -CXXFLAGS = -Wl,-rpath=/usr/local/lib/gcc7 -Wfatal-errors -fPIC -Wall -pedantic -O0 -g3 -gdwarf-2 -pthread -std=c++17 -I/usr/local/include -I$(GLOBAL_WORKING_DIR)/pikotools -I$(GLOBAL_WORKING_DIR)/morm/src +CXXFLAGS = -Wl,-rpath=/usr/local/lib/gcc10 -Wfatal-errors -fPIC -Wall -pedantic -O0 -g3 -gdwarf-2 -pthread -std=c++20 -I/usr/local/include -I$(GLOBAL_WORKING_DIR)/pikotools -I$(GLOBAL_WORKING_DIR)/morm/src LDFLAGS = -L/usr/local/lib diff --git a/samples/Makefile.dep b/samples/Makefile.dep index 2150a94..3bdaf52 100644 --- a/samples/Makefile.dep +++ b/samples/Makefile.dep @@ -20,4 +20,4 @@ main.o: ../../morm/src/cursor.h ../../morm/src/jsonexpression.h main.o: ../../morm/src/postgresqlexpression.h ../../morm/src/jsonconnector.h main.o: ../../morm/src/postgresqlconnector.h main.o: ../../morm/src/postgresqlqueryresult.h person.h language.h -main.o: attachment.h type.h +main.o: attachment.h type.h attachment2.h diff --git a/samples/attachment.h b/samples/attachment.h index 176cf4a..50bc389 100644 --- a/samples/attachment.h +++ b/samples/attachment.h @@ -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 @@ -38,7 +38,7 @@ #include #include "morm.h" #include "type.h" - +#include "language.h" namespace morm @@ -54,7 +54,7 @@ CREATE TABLE public.attachment ( content text, some_flags bool, created_date timestamp with time zone, - bigint language_id, + language_id bigint, primary key(id) ); @@ -82,7 +82,6 @@ public: field(L"name", name); field(L"content", content); field(L"attachment_id", L"types", types); - //field(L"types", types); field(L"some_flags", some_flags); field(L"created_date", created_date); field(L"language_id", L"language", language); @@ -94,9 +93,18 @@ public: stream << "public.attachment"; } + void after_select() + { + if( has_primary_key_set ) + { + morm::Finder finder(model_connector); + types = finder.select().where().eq(L"attachment_id", id).get_vector(); + } + } + void after_insert() { - get_last_sequence(L"public.attachment_id_seq", id); + get_last_sequence_for_primary_key(L"public.attachment_id_seq", id); } diff --git a/samples/attachment2.h b/samples/attachment2.h new file mode 100644 index 0000000..3b654e8 --- /dev/null +++ b/samples/attachment2.h @@ -0,0 +1,120 @@ +/* + * This file is a part of morm + * and is distributed under the 2-Clause BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2021, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef headerfile_morm_samples_attachment2 +#define headerfile_morm_samples_attachment2 + +#include +#include "morm.h" +#include "type.h" +#include "language.h" + + +namespace morm +{ +namespace samples +{ + +/* +CREATE TABLE public.attachment2 ( + id bigserial, + person_id bigint, + name varchar(64), + content text, + some_flags bool, + created_date timestamp with time zone, + language_id bigint, + + primary key(id) +); +*/ + +// copied from Attachment and changed the table name +class Attachment2 : public morm::Model +{ +public: + + long id; + long person_id; + std::wstring name; + std::string content; + std::vector types; + bool some_flags; + PT::Date created_date; + Language language; + + + void map_fields() + { + field(L"id", id, false, false, true); + field(L"person_id", person_id); + field(L"name", name); + field(L"content", content); + field(L"attachment_id", L"types", types); + field(L"some_flags", some_flags); + field(L"created_date", created_date); + field(L"language_id", L"language", language); + } + + void table_name(PT::TextStream & stream) + { + // schema.table_name or just table_name + stream << "public.attachment2"; + } + + void after_select() + { + if( has_primary_key_set ) + { + morm::Finder finder(model_connector); + types = finder.select().where().eq(L"attachment_id", id).get_vector(); + } + } + + void after_insert() + { + get_last_sequence_for_primary_key(L"public.attachment2_id_seq", id); + } + + +}; + + + +} +} + + +#endif + diff --git a/samples/language.h b/samples/language.h index 4fa2088..fa60363 100644 --- a/samples/language.h +++ b/samples/language.h @@ -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 @@ -86,7 +86,7 @@ public: void after_insert() { - get_last_sequence(L"public.language_id_seq", id); + get_last_sequence_for_primary_key(L"public.language_id_seq", id); } diff --git a/samples/person.h b/samples/person.h index f906d59..d70fb03 100644 --- a/samples/person.h +++ b/samples/person.h @@ -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 @@ -39,6 +39,7 @@ #include "morm.h" #include "language.h" #include "attachment.h" +#include "attachment2.h" @@ -64,29 +65,31 @@ class Person : public morm::Model public: long id; + std::wstring first_name; std::wstring last_name; std::wstring email; Language language; std::list attachments; - Attachment attachment; + Attachment2 attachment2; void map_fields() { field(L"id", id, false, false, true); - //field(L"id", id, f::no_insertable | f::no_updatable | f::primary_key); + field(L"language_id", L"language", language); field(L"first_name", first_name); field(L"last_name", last_name); field(L"email", email); - field(L"language_id", L"language", language); field(L"person_id", L"attachments", attachments); - field(L"person_id", L"attachment", attachment, true, true, false); + field(L"person_id", L"attachment2", attachment2, true, true, false); + + //field(L"id", id, f::no_insertable | f::no_updatable | f::primary_key); //field(L"person_id", attachment, f::insertable | f::updatable | f::foreign_key); //field(L"person_id", attachment, f::insertable, f::updatable, f::foreign_key); } @@ -99,13 +102,19 @@ public: void after_select() { - morm::Finder finder(model_connector); - attachments = finder.select().where().eq(L"person_id", id).get_list(); + if( has_primary_key_set ) + { + morm::Finder finder(model_connector); + attachments = finder.select().where().eq(L"person_id", id).get_list(); + + morm::Finder finder2(model_connector); + attachment2 = finder2.select().where().eq(L"person_id", id).get(); + } } void after_insert() { - get_last_sequence(L"public.person_id_seq", id); + get_last_sequence_for_primary_key(L"public.person_id_seq", id); } diff --git a/samples/sample01.h b/samples/sample01.h index 0558729..87f6db3 100644 --- a/samples/sample01.h +++ b/samples/sample01.h @@ -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 @@ -51,23 +51,37 @@ public: void make() { + // delete all datas + // delete from attachment; delete from attachment2; delete from language; delete from person; delete from types; + + // select * from person; select * from attachment; select * from attachment2 ; select * from types; select * from language; + + std::string str; + + person.set_connector(model_connector); load_defaults(person); - std::wstring sss = L"cosik wstawiony dynamicznie"; - person.set_field_value_generic(L"email", L"email", sss); + //std::wstring sss = L"cosik wstawiony dynamicznie"; + //person.set_field_value_generic(L"email", L"email", sss); + + //person.insert(); //person.update(); - person.save(); + //person.save(); //person.remove(); + //person.to_text(str, true, true); morm::Finder finder(model_connector); + Person p = finder.select().where().eq(L"id", 191).get(); + p.to_text(str, true, true); + + //std::list plist = finder.use_table_prefix(false).select().where().eq(L"id", 120).get_list(); - //Person p = finder.use_table_prefix(false).select().where().eq(L"id", 120).get(); // Person p = finder.prepare_to_select().use_table_prefix(true).raw("select person.id as \"person.id\", person.first_name as \"person.first_name\", person.last_name as \"person.last_name\", person.email as \"person.email\", " // "language.id as \"language.id\", language.english_name as \"language.english_name\", language.local_name as \"language.local_name\", language.code_str as \"language.code_str\", language.code_int as \"language.code_int\", " @@ -79,14 +93,13 @@ void make() // "LEFT JOIN public.language AS language2 ON attachment.language_id = language2.id " // "where person.id=120").get(); - std::cout << "--------------------------------" << std::endl; + //std::cout << "--------------------------------" << std::endl; //p.remove(); //std::cout << "--------------------------------" << std::endl; - std::string str; //str += "--------\n"; - person.to_text(str, true, true); + // for(Person & person : plist) // { @@ -112,6 +125,7 @@ private: person.first_name = L"MyFirstName"; person.last_name = L"MyLastName"; person.email = L"myemail@mydomain.ltd"; + //person.set_save_mode(Model::DO_NOTHING_ON_SAVE); person.set_save_mode(Model::DO_INSERT_ON_SAVE); //person.set_save_mode(Model::DO_UPDATE_ON_SAVE); @@ -120,29 +134,38 @@ private: person.language.local_name = L"polish"; person.language.code_str = L"en"; person.language.code_int = 200; + //person.language.set_save_mode(Model::DO_NOTHING_ON_SAVE); person.language.set_save_mode(Model::DO_INSERT_ON_SAVE); //person.language.set_save_mode(Model::DO_UPDATE_ON_SAVE); std::time_t t = std::time(0); -// person.attachment.id = 40; - person.attachment.person_id = person.id; - person.attachment.created_date.FromTime(t); - person.attachment.name = L"attachment name"; - person.attachment.content = "long binary content"; - person.attachment.some_flags = true; - person.attachment.set_save_mode(Model::DO_INSERT_ON_SAVE); -// //person.attachment.set_save_mode(Model::DO_UPDATE_ON_SAVE); -// - person.attachment.language.id = 86; - person.attachment.language.english_name = L"attachment language"; - person.attachment.language.local_name = L"attachment local name"; - person.attachment.language.code_str = L"loen"; - person.attachment.language.code_int = 300; -// person.attachment.language.set_save_mode(Model::DO_UPDATE_ON_SAVE); - Type type; type.id = 0; + type.set_save_mode(Model::DO_INSERT_ON_SAVE); + + person.attachment2.id = 40; + person.attachment2.person_id = person.id; + person.attachment2.created_date.FromTime(t); + person.attachment2.name = L"attachment name"; + person.attachment2.content = "long binary content"; + person.attachment2.some_flags = true; + person.attachment2.set_save_mode(Model::DO_INSERT_ON_SAVE); +// //person.attachment.set_save_mode(Model::DO_UPDATE_ON_SAVE); + + person.attachment2.language.id = 86; + person.attachment2.language.english_name = L"attachment language"; + person.attachment2.language.local_name = L"attachment local name"; + person.attachment2.language.code_str = L"loen"; + person.attachment2.language.code_int = 300; + person.attachment2.language.set_save_mode(Model::DO_INSERT_ON_SAVE); + person.attachment2.language.set_has_primary_key_set(true); + + type.name = L"abcde - fghi"; + person.attachment2.types.push_back(type); + + type.name = L"second type"; + person.attachment2.types.push_back(type); Attachment attachment; attachment.id = 0; @@ -186,7 +209,6 @@ private: type.name = L"Typ dla attachment 3 - 3"; person.attachments.back().types.push_back(type); - //type.name = L"Typik"; //person.attachment.types.push_back(type); diff --git a/samples/type.h b/samples/type.h index 2fb43f7..05ec7fd 100644 --- a/samples/type.h +++ b/samples/type.h @@ -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 @@ -79,7 +79,7 @@ public: void after_insert() { - get_last_sequence(L"public.types_id_seq", id); + get_last_sequence_for_primary_key(L"public.types_id_seq", id); } diff --git a/src/baseexpression.cpp b/src/baseexpression.cpp index 475cd20..59e4931 100644 --- a/src/baseexpression.cpp +++ b/src/baseexpression.cpp @@ -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); } } diff --git a/src/baseexpression.h b/src/baseexpression.h index 66372cd..05c06e4 100644 --- a/src/baseexpression.h +++ b/src/baseexpression.h @@ -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 + 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 void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::set & container, ModelEnv * model_env) { @@ -282,6 +298,12 @@ protected: } + virtual void put_null_value() + { + (*out_stream) << "null"; + } + + virtual void before_field_value_list() { } diff --git a/src/dbconnector.h b/src/dbconnector.h index 7e2753d..4c1b8f0 100644 --- a/src/dbconnector.h +++ b/src/dbconnector.h @@ -110,7 +110,7 @@ public: template - 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; } diff --git a/src/jsonexpression.cpp b/src/jsonexpression.cpp index e9ef3e9..692693e 100644 --- a/src/jsonexpression.cpp +++ b/src/jsonexpression.cpp @@ -105,8 +105,6 @@ void JSONExpression::after_second_part_long_field_name() } - - void JSONExpression::before_field_value_string() { (*out_stream) << "\""; diff --git a/src/model.cpp b/src/model.cpp index 8c4b63f..ce588fc 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -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; diff --git a/src/model.h b/src/model.h index 5da8e56..b8f3535 100644 --- a/src/model.h +++ b/src/model.h @@ -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 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 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 - 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 - 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 - 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 + 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 friend class Finder; diff --git a/src/modelenv.h b/src/modelenv.h index be29b45..9cdca98 100644 --- a/src/modelenv.h +++ b/src/modelenv.h @@ -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 * set_field_name_helper; std::vector * 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; } diff --git a/src/postgresqlexpression.cpp b/src/postgresqlexpression.cpp index 4235383..ee70474 100644 --- a/src/postgresqlexpression.cpp +++ b/src/postgresqlexpression.cpp @@ -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 << " "; diff --git a/src/postgresqlexpression.h b/src/postgresqlexpression.h index b26605b..6443e10 100644 --- a/src/postgresqlexpression.h +++ b/src/postgresqlexpression.h @@ -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);