From 334201fe1550a7007872f6ecc4faaa836d8781f4 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Tue, 24 Sep 2019 17:08:45 +0000 Subject: [PATCH] fixed: a parent key was not properly set to simple childs (insert statements) fixed: a parent key was not set in list of childs (insert statements) git-svn-id: svn://ttmath.org/publicrep/morm/trunk@1215 e52654a7-88a9-db11-a3e9-0013d4bc506e --- samples/Makefile.dep | 2 +- samples/attachment.h | 9 ++- samples/person.h | 3 +- samples/sample01.h | 82 +++++++++++++++++----- samples/type.h | 95 ++++++++++++++++++++++++++ src/fieldvaluehelper.h | 10 +++ src/model.cpp | 31 ++++++++- src/model.h | 150 ++++++++++++++++++++++++++++++----------- 8 files changed, 322 insertions(+), 60 deletions(-) create mode 100644 samples/type.h diff --git a/samples/Makefile.dep b/samples/Makefile.dep index 40630e6..2150a94 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 +main.o: attachment.h type.h diff --git a/samples/attachment.h b/samples/attachment.h index 0b52547..176cf4a 100644 --- a/samples/attachment.h +++ b/samples/attachment.h @@ -37,6 +37,8 @@ #include #include "morm.h" +#include "type.h" + namespace morm @@ -47,7 +49,7 @@ namespace samples /* CREATE TABLE public.attachment ( id bigserial, - bigint person_id, + person_id bigint, name varchar(64), content text, some_flags bool, @@ -67,22 +69,25 @@ public: 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"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 diff --git a/samples/person.h b/samples/person.h index f289db3..f906d59 100644 --- a/samples/person.h +++ b/samples/person.h @@ -85,7 +85,8 @@ public: field(L"person_id", L"attachments", attachments); - //field(L"person_id", L"attachment", attachment, true, true, false); + field(L"person_id", L"attachment", attachment, true, true, false); + //field(L"person_id", attachment, f::insertable | f::updatable | f::foreign_key); //field(L"person_id", attachment, f::insertable, f::updatable, f::foreign_key); } diff --git a/samples/sample01.h b/samples/sample01.h index bf4594c..0558729 100644 --- a/samples/sample01.h +++ b/samples/sample01.h @@ -52,14 +52,14 @@ public: void make() { person.set_connector(model_connector); - //load_defaults(person); + load_defaults(person); - //std::wstring sss = L"cosik"; - //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(); @@ -67,7 +67,7 @@ void make() //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.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\", " @@ -80,13 +80,13 @@ void make() // "where person.id=120").get(); std::cout << "--------------------------------" << std::endl; - p.remove(); - std::cout << "--------------------------------" << std::endl; + //p.remove(); + //std::cout << "--------------------------------" << std::endl; std::string str; //str += "--------\n"; - p.to_text(str, true, true); + person.to_text(str, true, true); // for(Person & person : plist) // { @@ -108,14 +108,14 @@ private: static void load_defaults(Person & person) { - person.id = 60; + person.id = 0; person.first_name = L"MyFirstName"; person.last_name = L"MyLastName"; person.email = L"myemail@mydomain.ltd"; person.set_save_mode(Model::DO_INSERT_ON_SAVE); //person.set_save_mode(Model::DO_UPDATE_ON_SAVE); - person.language.id = 85; + person.language.id = 0; person.language.english_name = L"english"; person.language.local_name = L"polish"; person.language.code_str = L"en"; @@ -125,23 +125,73 @@ private: std::time_t t = std::time(0); - person.attachment.id = 40; - person.attachment.person_id = 60; +// 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.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); +// person.attachment.language.set_save_mode(Model::DO_UPDATE_ON_SAVE); + + Type type; + type.id = 0; + + Attachment attachment; + attachment.id = 0; + attachment.person_id = person.id; + attachment.created_date.FromTime(t); + attachment.name = L"list attachment 1"; + attachment.content = "list attachment content"; + attachment.some_flags = true; + attachment.set_save_mode(Model::DO_INSERT_ON_SAVE); + //attachment.set_save_mode(Model::DO_UPDATE_ON_SAVE); + + attachment.language.english_name = L"Language dla attachment 1"; + person.attachments.push_back(attachment); + + type.name = L"Typ dla attachment 1 - 1"; + person.attachments.back().types.push_back(type); + + type.name = L"Typ dla attachment 1 - 2"; + person.attachments.back().types.push_back(type); + + attachment.name = L"list attachment 2"; + attachment.language.english_name = L"Language dla attachment 2"; + person.attachments.push_back(attachment); + + type.name = L"Typ dla attachment 2 - 1"; + person.attachments.back().types.push_back(type); + + type.name = L"Typ dla attachment 2 - 2"; + person.attachments.back().types.push_back(type); + + attachment.name = L"list attachment 3"; + attachment.language.english_name = L"Language dla attachment 3"; + person.attachments.push_back(attachment); + + type.name = L"Typ dla attachment 3 - 1"; + person.attachments.back().types.push_back(type); + + type.name = L"Typ dla attachment 3 - 2"; + person.attachments.back().types.push_back(type); + + 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 new file mode 100644 index 0000000..2fb43f7 --- /dev/null +++ b/samples/type.h @@ -0,0 +1,95 @@ +/* + * This file is a part of morm + * and is distributed under the 2-Clause BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2019, 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_type +#define headerfile_morm_samples_type + +#include +#include "morm.h" + + +namespace morm +{ +namespace samples +{ + +/* +CREATE TABLE public.types ( + id bigserial, + attachment_id bigint, + name varchar(64), + + primary key(id) +); +*/ + + +class Type : public morm::Model +{ +public: + + long id; + long attachment_id; + std::wstring name; + + void map_fields() + { + field(L"id", id, false, false, true); + field(L"attachment_id", attachment_id); + field(L"name", name); + } + + + void table_name(PT::TextStream & stream) + { + // schema.table_name or just table_name + stream << "public.types"; + } + + void after_insert() + { + get_last_sequence(L"public.types_id_seq", id); + } + + +}; + + + +} +} + + +#endif + diff --git a/src/fieldvaluehelper.h b/src/fieldvaluehelper.h index fe0bfd2..9f5ee81 100644 --- a/src/fieldvaluehelper.h +++ b/src/fieldvaluehelper.h @@ -47,17 +47,27 @@ struct FieldValueHelper const wchar_t * db_field_name; const wchar_t * flat_field_name; + bool compare_db_field_name; + bool compare_flat_field_name; + const void * value_object; const std::type_info * value_type_info; + bool found; + FieldValueHelper() { db_field_name = nullptr; flat_field_name = nullptr; + compare_db_field_name = true; + compare_flat_field_name = true; + value_object = nullptr; value_type_info = nullptr; + + found = false; } }; diff --git a/src/model.cpp b/src/model.cpp index f790218..c80296b 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -703,10 +703,10 @@ bool Model::is_empty_field(const wchar_t * value) bool Model::is_the_same_field(const wchar_t * field1, const wchar_t * field2) { - if( !field1 && !field2 ) + if( is_empty_field(field1) && is_empty_field(field2) ) return true; - if( !field1 || !field2 ) + if( is_empty_field(field1) || is_empty_field(field2) ) return false; bool the_same = false; @@ -763,5 +763,32 @@ void Model::put_table_name_with_index(PT::TextStream & str) } +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; + bool was_flat_field_put = false; + + if( !is_empty_field(db_field_name) ) + { + log << "database field name: " << db_field_name; + was_db_field_put = true; + } + + if( !is_empty_field(flat_field_name) ) + { + if( was_db_field_put ) + log << ", "; + + log << "flat field name: " << flat_field_name; + was_flat_field_put = true; + } + + if( !was_db_field_put && !was_flat_field_put ) + { + log << "(both database field name and flat field name are empty)"; + } +} + + } // namespace diff --git a/src/model.h b/src/model.h index a62d62d..1ed4d4c 100644 --- a/src/model.h +++ b/src/model.h @@ -150,6 +150,18 @@ public: map_fields(); + if( !helper_tab.back().found && model_connector ) + { + PT::Log * plog = model_connector->get_logger(); + + if( plog ) + { + (*plog) << "Morm: I cannot find such a property: "; + put_fields_to_log(*plog, db_field_name, flat_field_name); + (*plog) << PT::Log::logend; + } + } + // what if an exception was thrown? model_env = nullptr; } @@ -452,8 +464,8 @@ protected: FieldValueHelper & helper = (*model_env->field_value_helper_tab)[model_env->field_index]; PT::Log * log = model_connector->get_logger(); - if( is_the_same_field(db_field_name, helper.db_field_name) && - is_the_same_field(flat_field_name, helper.flat_field_name) ) + if( (!helper.compare_db_field_name || is_the_same_field(db_field_name, helper.db_field_name)) && + (!helper.compare_flat_field_name || is_the_same_field(flat_field_name, helper.flat_field_name)) ) { if( helper.value_object && helper.value_type_info ) { @@ -464,19 +476,15 @@ protected: else { table_name(model_env->table_name); + (*log) << PT::Log::log1 << "Morm: incorrect type of a field in " << model_env->table_name << ", "; - - if( !is_empty_field(db_field_name) ) - (*log) << "database field name: " << db_field_name << ", "; - - if( !is_empty_field(flat_field_name) ) - (*log) << "flat field name: " << flat_field_name << ", "; - - (*log) << "type expected " << typeid(field_value).name() + put_fields_to_log(*log, db_field_name, flat_field_name); + (*log) << ", type expected " << typeid(field_value).name() << " got " << helper.value_type_info->name() << PT::Log::logend; } } + helper.found = true; model_env->field_index += 1; } } @@ -677,7 +685,39 @@ protected: } - void field_model_set_parent_key(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model) + /* + * first we iterate through fields and save primary key values to helper_tab + */ + void field_model_save_key(const wchar_t * db_field_name) + { + DbConnector * db_connector = model_connector->get_db_connector(); + PT::Log * plog = model_connector->get_logger(); + + if( db_connector ) + { + DbExpression * db_expression = db_connector->get_expression(); + + if( db_expression && !is_empty_field(db_field_name) && model_env->field_value_helper_tab ) + { + int old_work_mode = model_env->model_work_mode; + model_env->model_work_mode = MORM_MODEL_WORK_MODE_ITERATE_PRIMARY_KEY_VALUES; + model_env->field_index = 0; + map_fields(); + model_env->model_work_mode = old_work_mode; + + if( model_env->field_value_helper_tab->empty() && plog ) + { + (*plog) << "Morm: I cannot find a primary key in " << model_env->table_name << PT::Log::logend; + } + } + } + } + + + /* + * now we iterate through fields in field_model and save primary key values from *this object to the specified fields in field_model + */ + void field_model_set_parent_key_in_child(const wchar_t * db_field_name, Model & field_model) { DbConnector * db_connector = model_connector->get_db_connector(); PT::Log * log = model_connector->get_logger(); @@ -686,25 +726,9 @@ protected: { DbExpression * db_expression = db_connector->get_expression(); - if( db_expression && !is_empty_field(db_field_name) ) + 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; - - FieldValueHelper helper; // in the future we can have a primary key from more than one column - helper.db_field_name = db_field_name; - helper.flat_field_name = flat_field_name; - - std::vector helper_tab; - helper_tab.push_back(helper); - - model_env->field_value_helper_tab = &helper_tab; - model_env->field_index = 0; - - /* - * first we iterate through fields and save primary key values to helper_tab - */ - map_fields(); + std::vector & helper_tab = *model_env->field_value_helper_tab; if( (size_t)model_env->field_index == helper_tab.size() ) { @@ -715,15 +739,15 @@ protected: model_env_local.field_index = 0; field_model.model_env = &model_env_local; - /* - * now we iterate through fields in field_model and save primary key values from *this object to the specified fields in field_model - */ field_model.map_fields(); if( (size_t)field_model.model_env->field_index != helper_tab.size() && log ) { - table_name(model_env->table_name); - field_model.table_name(field_model.model_env->table_name); + if( model_env->table_name.empty() ) + table_name(model_env->table_name); + + if( field_model.model_env->table_name.empty() ) + field_model.table_name(field_model.model_env->table_name); (*log) << PT::Log::log1 << "Morm: primary key in " << model_env->table_name << " consists of " << model_env->field_index << " column(s)" << " but in " << field_model.model_env->table_name << " there are only " @@ -740,14 +764,31 @@ protected: (*log) << PT::Log::log1 << "Morm: primary key in " << model_env->table_name << " consists of incorrect number of columns" << ", expected " << helper_tab.size() << " column(s) but got " << model_env->field_index << PT::Log::logend; } - - model_env->field_value_helper_tab = nullptr; - model_env->model_work_mode = old_work_mode; } } } + void field_model_set_parent_key(const wchar_t * db_field_name, Model & field_model) + { + FieldValueHelper helper; + helper.db_field_name = db_field_name; + helper.flat_field_name = nullptr; + helper.compare_flat_field_name = false; + + std::vector helper_tab; + helper_tab.push_back(helper); + // only one column at the moment, in the future we can have a primary key from more than one column + + model_env->field_value_helper_tab = &helper_tab; + + field_model_save_key(db_field_name); + field_model_set_parent_key_in_child(db_field_name, field_model); + + model_env->field_value_helper_tab = nullptr; + } + + void field_model_iterate_through_childs(const wchar_t * db_field_name, Model & field_model) { if( !is_empty_field(db_field_name) ) @@ -920,7 +961,7 @@ protected: { if( !has_foreign_key ) { - field_model_set_parent_key(db_field_name, flat_field_name, field_model); + field_model_set_parent_key(db_field_name, field_model); } } @@ -965,6 +1006,31 @@ protected: } + template + void field_list_set_parent_key(const wchar_t * db_field_name, ModelContainer & field_container, ModelContainerType * model_container_type) + { + FieldValueHelper helper; + helper.db_field_name = db_field_name; + helper.flat_field_name = nullptr; + helper.compare_flat_field_name = false; + + std::vector helper_tab; + helper_tab.push_back(helper); + // only one column at the moment, in the future we can have a primary key from more than one column + + model_env->field_value_helper_tab = &helper_tab; + field_model_save_key(db_field_name); + + for(ModelContainerType & child_model : field_container) + { + child_model.set_connector(model_connector); + field_model_set_parent_key_in_child(db_field_name, child_model); + } + + model_env->field_value_helper_tab = nullptr; + } + + template void field_list_iterate_through_childs(const wchar_t * db_field_name, ModelContainer & field_container, ModelContainerType * model_container_type) { @@ -1040,6 +1106,11 @@ protected: { if( model_connector && model_env ) { + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_SET_PARENT_ID ) + { + field_list_set_parent_key(db_field_name, field_container, model_container_type); + } + if( model_env->model_work_mode == MORM_MODEL_WORK_MODE_ITERATE_THROUGH_CHILDS_WITHOUT_FOREIGN_KEY ) { field_list_iterate_through_childs(db_field_name, field_container, model_container_type); @@ -1200,6 +1271,9 @@ protected: virtual void prepare_table_names(bool prepare_table_index = true); virtual void put_table_name_with_index(PT::TextStream & str); + + virtual void put_fields_to_log(PT::Log & log, const wchar_t * db_field_name, const wchar_t * flat_field_name); + template friend class Finder; template friend class Cursor; friend class BaseExpression;