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
This commit is contained in:
Tomasz Sowa 2019-09-24 17:08:45 +00:00
parent cff4c1518e
commit 334201fe15
8 changed files with 322 additions and 60 deletions

View File

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

View File

@ -37,6 +37,8 @@
#include <string>
#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<Type> 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

View File

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

View File

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

95
samples/type.h Normal file
View File

@ -0,0 +1,95 @@
/*
* This file is a part of morm
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* 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 <string>
#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

View File

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

View File

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

View File

@ -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<FieldValueHelper> 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<FieldValueHelper> & 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<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( !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<typename ModelContainer, typename ModelContainerType>
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<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);
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<typename ModelContainer, typename ModelContainerType>
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<typename ModelClass> friend class Finder;
template<typename ModelClass> friend class Cursor;
friend class BaseExpression;