diff --git a/src/Makefile.dep b/src/Makefile.dep index 492efda..4a4f46e 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -6,11 +6,12 @@ baseexpression.o: ../../pikotools/textstream/types.h baseexpression.o: ../../pikotools/date/date.h baseexpression.o: ../../pikotools/convert/inttostr.h baseexpression.o: ../../pikotools/membuffer/membuffer.h -baseexpression.o: ../../pikotools/textstream/types.h morm_types.h model.h -baseexpression.o: modelconnector.h clearer.h dbconnector.h +baseexpression.o: ../../pikotools/textstream/types.h morm_types.h modelenv.h +baseexpression.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h +baseexpression.o: model.h modelconnector.h clearer.h dbconnector.h baseexpression.o: ../../pikotools/log/log.h ../../pikotools/log/filelog.h -baseexpression.o: queryresult.h flatconnector.h dbexpression.h -baseexpression.o: flatexpression.h modeldata.h ../../pikotools/utf8/utf8.h +baseexpression.o: flatconnector.h dbexpression.h flatexpression.h +baseexpression.o: ../../pikotools/utf8/utf8.h clearer.o: clearer.h ../../pikotools/date/date.h clearer.o: ../../pikotools/convert/inttostr.h model.h clearer.o: ../../pikotools/textstream/textstream.h @@ -19,7 +20,8 @@ clearer.o: ../../pikotools/membuffer/membuffer.h clearer.o: ../../pikotools/textstream/types.h modelconnector.h dbconnector.h clearer.o: ../../pikotools/log/log.h ../../pikotools/log/filelog.h clearer.o: queryresult.h flatconnector.h dbexpression.h baseexpression.h -clearer.o: morm_types.h flatexpression.h modeldata.h +clearer.o: morm_types.h modelenv.h modeldata.h cursorhelper.h finderhelper.h +clearer.o: flatexpression.h dbconnector.o: dbconnector.h ../../pikotools/textstream/textstream.h dbconnector.o: ../../pikotools/space/space.h dbconnector.o: ../../pikotools/textstream/types.h ../../pikotools/date/date.h @@ -27,8 +29,9 @@ dbconnector.o: ../../pikotools/convert/inttostr.h dbconnector.o: ../../pikotools/membuffer/membuffer.h dbconnector.o: ../../pikotools/textstream/types.h ../../pikotools/log/log.h dbconnector.o: ../../pikotools/log/filelog.h queryresult.h dbexpression.h -dbconnector.o: baseexpression.h morm_types.h model.h modelconnector.h -dbconnector.o: clearer.h flatconnector.h flatexpression.h modeldata.h +dbconnector.o: baseexpression.h morm_types.h modelenv.h modeldata.h +dbconnector.o: cursorhelper.h finderhelper.h model.h modelconnector.h +dbconnector.o: clearer.h flatconnector.h flatexpression.h dbconnector.o: ../../pikotools/utf8/utf8.h ../../pikotools/convert/convert.h dbconnector.o: ../../pikotools/convert/inttostr.h dbconnector.o: ../../pikotools/convert/patternreplacer.h @@ -41,7 +44,8 @@ dbexpression.o: ../../pikotools/textstream/types.h dbexpression.o: ../../pikotools/date/date.h dbexpression.o: ../../pikotools/convert/inttostr.h dbexpression.o: ../../pikotools/membuffer/membuffer.h -dbexpression.o: ../../pikotools/textstream/types.h morm_types.h +dbexpression.o: ../../pikotools/textstream/types.h morm_types.h modelenv.h +dbexpression.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h dochtmlconnector.o: dochtmlconnector.h flatconnector.h dochtmlconnector.o: ../../pikotools/textstream/textstream.h dochtmlconnector.o: ../../pikotools/space/space.h @@ -50,7 +54,8 @@ dochtmlconnector.o: ../../pikotools/date/date.h dochtmlconnector.o: ../../pikotools/convert/inttostr.h dochtmlconnector.o: ../../pikotools/membuffer/membuffer.h dochtmlconnector.o: ../../pikotools/textstream/types.h dochtmlexpression.h -dochtmlconnector.o: flatexpression.h baseexpression.h morm_types.h +dochtmlconnector.o: flatexpression.h baseexpression.h morm_types.h modelenv.h +dochtmlconnector.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h dochtmlexpression.o: dochtmlexpression.h flatexpression.h baseexpression.h dochtmlexpression.o: ../../pikotools/textstream/textstream.h dochtmlexpression.o: ../../pikotools/space/space.h @@ -59,6 +64,8 @@ dochtmlexpression.o: ../../pikotools/date/date.h dochtmlexpression.o: ../../pikotools/convert/inttostr.h dochtmlexpression.o: ../../pikotools/membuffer/membuffer.h dochtmlexpression.o: ../../pikotools/textstream/types.h morm_types.h +dochtmlexpression.o: modelenv.h modeldata.h cursorhelper.h queryresult.h +dochtmlexpression.o: finderhelper.h flatconnector.o: flatconnector.h ../../pikotools/textstream/textstream.h flatconnector.o: ../../pikotools/space/space.h flatconnector.o: ../../pikotools/textstream/types.h @@ -66,10 +73,11 @@ flatconnector.o: ../../pikotools/date/date.h flatconnector.o: ../../pikotools/convert/inttostr.h flatconnector.o: ../../pikotools/membuffer/membuffer.h flatconnector.o: ../../pikotools/textstream/types.h flatexpression.h -flatconnector.o: baseexpression.h morm_types.h model.h modelconnector.h -flatconnector.o: clearer.h dbconnector.h ../../pikotools/log/log.h -flatconnector.o: ../../pikotools/log/filelog.h queryresult.h dbexpression.h -flatconnector.o: modeldata.h +flatconnector.o: baseexpression.h morm_types.h modelenv.h modeldata.h +flatconnector.o: cursorhelper.h queryresult.h finderhelper.h model.h +flatconnector.o: modelconnector.h clearer.h dbconnector.h +flatconnector.o: ../../pikotools/log/log.h ../../pikotools/log/filelog.h +flatconnector.o: dbexpression.h flatexpression.o: flatexpression.h baseexpression.h flatexpression.o: ../../pikotools/textstream/textstream.h flatexpression.o: ../../pikotools/space/space.h @@ -77,7 +85,8 @@ flatexpression.o: ../../pikotools/textstream/types.h flatexpression.o: ../../pikotools/date/date.h flatexpression.o: ../../pikotools/convert/inttostr.h flatexpression.o: ../../pikotools/membuffer/membuffer.h -flatexpression.o: ../../pikotools/textstream/types.h morm_types.h +flatexpression.o: ../../pikotools/textstream/types.h morm_types.h modelenv.h +flatexpression.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h jsonconnector.o: jsonconnector.h flatconnector.h jsonconnector.o: ../../pikotools/textstream/textstream.h jsonconnector.o: ../../pikotools/space/space.h @@ -86,7 +95,8 @@ jsonconnector.o: ../../pikotools/date/date.h jsonconnector.o: ../../pikotools/convert/inttostr.h jsonconnector.o: ../../pikotools/membuffer/membuffer.h jsonconnector.o: ../../pikotools/textstream/types.h jsonexpression.h -jsonconnector.o: flatexpression.h baseexpression.h morm_types.h +jsonconnector.o: flatexpression.h baseexpression.h morm_types.h modelenv.h +jsonconnector.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h jsonexpression.o: jsonexpression.h flatexpression.h baseexpression.h jsonexpression.o: ../../pikotools/textstream/textstream.h jsonexpression.o: ../../pikotools/space/space.h @@ -94,7 +104,8 @@ jsonexpression.o: ../../pikotools/textstream/types.h jsonexpression.o: ../../pikotools/date/date.h jsonexpression.o: ../../pikotools/convert/inttostr.h jsonexpression.o: ../../pikotools/membuffer/membuffer.h -jsonexpression.o: ../../pikotools/textstream/types.h morm_types.h +jsonexpression.o: ../../pikotools/textstream/types.h morm_types.h modelenv.h +jsonexpression.o: modeldata.h cursorhelper.h queryresult.h finderhelper.h model.o: model.h ../../pikotools/textstream/textstream.h model.o: ../../pikotools/space/space.h ../../pikotools/textstream/types.h model.o: ../../pikotools/date/date.h ../../pikotools/convert/inttostr.h @@ -102,8 +113,8 @@ model.o: ../../pikotools/membuffer/membuffer.h model.o: ../../pikotools/textstream/types.h modelconnector.h clearer.h model.o: dbconnector.h ../../pikotools/log/log.h model.o: ../../pikotools/log/filelog.h queryresult.h flatconnector.h -model.o: dbexpression.h baseexpression.h morm_types.h flatexpression.h -model.o: modeldata.h +model.o: dbexpression.h baseexpression.h morm_types.h modelenv.h modeldata.h +model.o: cursorhelper.h finderhelper.h flatexpression.h modelconnector.o: modelconnector.h clearer.h ../../pikotools/date/date.h modelconnector.o: ../../pikotools/convert/inttostr.h dbconnector.h modelconnector.o: ../../pikotools/textstream/textstream.h @@ -125,7 +136,8 @@ postgresqlconnector.o: ../../pikotools/log/log.h postgresqlconnector.o: ../../pikotools/log/filelog.h queryresult.h postgresqlconnector.o: postgresqlqueryresult.h ../../pikotools/utf8/utf8.h postgresqlconnector.o: postgresqlexpression.h dbexpression.h baseexpression.h -postgresqlconnector.o: morm_types.h ../../pikotools/convert/strtoint.h +postgresqlconnector.o: morm_types.h modelenv.h modeldata.h cursorhelper.h +postgresqlconnector.o: finderhelper.h ../../pikotools/convert/strtoint.h postgresqlconnector.o: ../../pikotools/convert/text.h postgresqlconnector.o: ../../pikotools/convert/misc.h postgresqlexpression.o: postgresqlexpression.h dbexpression.h @@ -137,6 +149,8 @@ postgresqlexpression.o: ../../pikotools/date/date.h postgresqlexpression.o: ../../pikotools/convert/inttostr.h postgresqlexpression.o: ../../pikotools/membuffer/membuffer.h postgresqlexpression.o: ../../pikotools/textstream/types.h morm_types.h +postgresqlexpression.o: modelenv.h modeldata.h cursorhelper.h queryresult.h +postgresqlexpression.o: finderhelper.h postgresqlqueryresult.o: postgresqlqueryresult.h queryresult.h queryresult.o: queryresult.h ../../pikotools/utf8/utf8.h queryresult.o: ../../pikotools/textstream/textstream.h diff --git a/src/baseexpression.cpp b/src/baseexpression.cpp index e396399..a108b6b 100644 --- a/src/baseexpression.cpp +++ b/src/baseexpression.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,9 +44,7 @@ namespace morm BaseExpression::BaseExpression() { - out_stream = nullptr; - is_first_field = false; - work_mode = 0; + clear(); } BaseExpression::~BaseExpression() @@ -54,32 +52,47 @@ BaseExpression::~BaseExpression() } +void BaseExpression::clear() +{ + out_stream = nullptr; + is_first_field = false; + work_mode = 0; + use_prefix = false; +} + + void BaseExpression::set_work_mode(int work_mode) { this->work_mode = work_mode; } -void BaseExpression::prepare_to_new_expression() +int BaseExpression::get_work_mode() { - column_prefix.clear(); - out_stream = nullptr; - is_first_field = false; - work_mode = 0; + return work_mode; } + PT::TextStream * BaseExpression::get_current_stream() { return out_stream; } -void BaseExpression::set_column_prefix(const std::wstring & prefix) +void BaseExpression::allow_to_use_prefix(bool use_prefix) { - column_prefix = prefix; + this->use_prefix = use_prefix; } + +bool BaseExpression::get_allow_to_use_prefix() +{ + return use_prefix; +} + + + void BaseExpression::generate_from_model(PT::TextStream & stream, Model & model) { this->out_stream = &stream; @@ -144,14 +157,80 @@ void BaseExpression::field_after() } -void BaseExpression::put_field_name(const wchar_t * field_name, bool add_column_prefix) +bool BaseExpression::is_long_field_name(const wchar_t * field_name) +{ + bool is_long = false; + + while( *field_name != 0 ) + { + if( *field_name == '.' ) + { + is_long = true; + break; + } + + field_name += 1; + } + + return is_long; +} + + +bool BaseExpression::is_long_field_name(const PT::TextStream & field_name) +{ + PT::TextStream::const_iterator i = field_name.begin(); + bool is_long = false; + + while( i != field_name.end() ) + { + if( *i == '.' ) + { + is_long = true; + break; + } + + ++i; + } + + return is_long; +} + + +bool BaseExpression::is_long_table_name(const wchar_t * table_name) +{ + return is_long_field_name(table_name); +} + +bool BaseExpression::is_long_table_name(const PT::TextStream & table_name) +{ + return is_long_field_name(table_name); +} + + +bool BaseExpression::need_to_add_field_prefix(const wchar_t * field_name) +{ + return !is_long_field_name(field_name); +} + + + +void BaseExpression::put_field_name(const wchar_t * field_name, ModelEnv * model_env) { before_field_name(); - if( add_column_prefix && !column_prefix.empty() ) + if( use_prefix && model_env ) { - esc(column_prefix, *out_stream); - (*out_stream) << '.'; + if( need_to_add_field_prefix(field_name) ) + { + esc(model_env->table_name_short, *out_stream); + + if( model_env->table_index > 1 ) + { + (*out_stream) << model_env->table_index; + } + + (*out_stream) << '.'; + } } esc(field_name, *out_stream); @@ -159,15 +238,30 @@ void BaseExpression::put_field_name(const wchar_t * field_name, bool add_column_ } - - -void BaseExpression::put_field_doc(Model & model, const void * field_pointer, bool insertable, bool updatable, bool is_primary_key, ModelData * model_data) +void BaseExpression::save_foreign_key(const wchar_t * field_name, ModelEnv * model_env) { - model.doc_field_pointer = field_pointer; - model.model_data = model_data; + PT::TextStream str; + PT::TextStream * old_out_stream = out_stream; + + out_stream = &str; + put_field_name(field_name, model_env); + out_stream = old_out_stream; + + if( model_env && model_env->finder_helper ) + { + model_env->finder_helper->foreign_keys.emplace_back(); + std::string & key_str = model_env->finder_helper->foreign_keys.back(); + str.to_string(key_str, false); + } +} + +void BaseExpression::put_field_doc(Model & model, const void * field_pointer, bool insertable, bool updatable, bool is_primary_key, ModelEnv * model_env) +{ + //model.doc_field_pointer = field_pointer; + model.model_env = model_env; model.map_doc_fields(); - model.model_data = nullptr; - model.doc_field_pointer = nullptr; + model.model_env = nullptr; + //model.doc_field_pointer = nullptr; } void BaseExpression::before_field_name() @@ -379,6 +473,16 @@ void BaseExpression::esc(const PT::Date & date, PT::TextStream & stream) stream << date; } +void BaseExpression::esc(const PT::TextStream & val, PT::TextStream & stream) +{ + PT::TextStream::const_iterator i = val.begin(); + + for(; i != val.end() ; ++i) + { + esc(*i, stream); + } +} + void BaseExpression::put_type(char val, PT::TextStream & stream) { diff --git a/src/baseexpression.h b/src/baseexpression.h index 879d25f..351b8b9 100644 --- a/src/baseexpression.h +++ b/src/baseexpression.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,14 +38,15 @@ #include #include #include "textstream/textstream.h" -#include "morm_types.h" #include "date/date.h" +#include "morm_types.h" +#include "modelenv.h" + namespace morm { class Model; -class ModelData; class ModelConnector; @@ -57,21 +58,25 @@ public: virtual ~BaseExpression(); virtual void set_work_mode(int work_mode); - virtual void prepare_to_new_expression(); - virtual void set_column_prefix(const std::wstring & prefix); + virtual int get_work_mode(); + + virtual void clear(); virtual void generate_from_model(PT::TextStream & stream, Model & model); virtual PT::TextStream * get_current_stream(); + // rename me + virtual void allow_to_use_prefix(bool use_prefix); + virtual bool get_allow_to_use_prefix(); // give me a better name - virtual void put_field_doc(Model & model, const void * field_pointer, bool insertable, bool updatable, bool is_primary_key, ModelData * model_data); + virtual void put_field_doc(Model & model, const void * field_pointer, bool insertable, bool updatable, bool is_primary_key, ModelEnv * model_env); template - void field(const wchar_t * field_name, const FieldValue & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false, bool add_column_prefix = true) + void field(const wchar_t * field_name, const FieldValue & field_value, bool insertable, bool updatable, bool is_primary_key, ModelEnv * model_env) { if( out_stream && can_field_be_generated(insertable, updatable, is_primary_key) ) { @@ -79,7 +84,12 @@ public: if( work_mode == MORM_WORK_MODE_MODEL_FIELDS ) { - put_field_name(field_name, add_column_prefix); + put_field_name(field_name, model_env); + } + else + if( work_mode == MORM_WORK_MODE_MODEL_SAVE_FIELDS ) + { + save_foreign_key(field_name, model_env); } else if( work_mode == MORM_WORK_MODE_MODEL_VALUES ) @@ -89,29 +99,29 @@ public: else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - put_field_name(field_name); + put_field_name(field_name, model_env); put_name_value_separator(); put_field_value(field_value); } + field_after(); } } + + template - void field_doc(Model & model, const wchar_t * field_name, const FieldValue & field_value, - bool insertable = true, bool updatable = true, - bool is_primary_key = false, bool add_column_prefix = true, - ModelData * model_data = nullptr) + void field_doc(Model & model, const wchar_t * field_name, const FieldValue & field_value, bool insertable, bool updatable, bool is_primary_key, ModelEnv * model_env) { if( out_stream ) { field_before(); - put_field_name(field_name, add_column_prefix); + put_field_name(field_name, model_env); put_name_value_separator(); - put_field_doc(model, reinterpret_cast(&field_value), insertable, updatable, is_primary_key, model_data); + put_field_doc(model, reinterpret_cast(&field_value), insertable, updatable, is_primary_key, model_env); put_name_value_separator(); put_type(field_value, *out_stream); @@ -121,48 +131,45 @@ public: } - /* template - void field(const PT::TextStream & field_name, const FieldValue & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false, bool add_column_prefix = true) + void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::set & container, ModelEnv * model_env) { - std::wstring field_name_str; // field() methods can be called recursively, so don't make it as class object - field_name.to_string(field_name_str); - - return field(field_name_str.c_str(), field_value, insertable, updatable, is_primary_key, add_column_prefix); - } - */ - - template - void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::set & container) - { - field_in_generic>(stream, field_name, container); + field_in_generic>(stream, field_name, container, model_env); } - template - void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::list & container) - { - field_in_generic>(stream, field_name, container); - } template - void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::vector & container) + void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::list & container, ModelEnv * model_env) { - field_in_generic>(stream, field_name, container); + field_in_generic>(stream, field_name, container, model_env); } + + template + void field_in(PT::TextStream & stream, const wchar_t * field_name, const std::vector & container, ModelEnv * model_env) + { + field_in_generic>(stream, field_name, container, model_env); + } + + template void field_list(const wchar_t * field_name, ModelContainer & field_value, bool insertable, bool updatable, bool is_primary_key, - ModelConnector * model_connector, int model_connector_mode, ModelData * model_data) + ModelConnector * model_connector, ModelEnv * model_env) { if( out_stream && can_field_be_generated(insertable, updatable, is_primary_key) ) { field_before(); +// if( work_mode == MORM_WORK_MODE_MODEL_FIELDS ) +// { +// put_field_name(field_name); +// } +// else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - put_field_name(field_name); + put_field_name(field_name, model_env); put_name_value_separator(); - put_field_value_list(field_value, model_connector, model_connector_mode, model_data); + put_field_value_list(field_value, model_connector, model_env); } field_after(); @@ -170,7 +177,7 @@ public: } template - void field_model(const wchar_t * field_name, ModelClass & field_model, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field_model(const wchar_t * field_name, ModelClass & field_model, bool insertable, bool updatable, bool is_primary_key, ModelEnv * model_env) { if( out_stream && can_field_be_generated(insertable, updatable, is_primary_key) ) { @@ -178,7 +185,7 @@ public: if( work_mode == MORM_WORK_MODE_MODEL_FIELDS ) { - put_field_name(field_name); + put_field_name(field_name, model_env); } else if( work_mode == MORM_WORK_MODE_MODEL_VALUES ) @@ -188,7 +195,7 @@ public: else if( work_mode == MORM_WORK_MODE_MODEL_FIELDS_VALUES ) { - put_field_name(field_name); + put_field_name(field_name, model_env); put_name_value_separator(); generate_from_model(field_model); } @@ -198,10 +205,11 @@ public: } template - void field_to_stream(PT::TextStream & stream, const wchar_t * field_name, const FieldValue & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field_to_stream(PT::TextStream & stream, const wchar_t * field_name, const FieldValue & field_value, bool insertable, bool updatable, bool is_primary_key, + ModelEnv * model_env) { this->out_stream = &stream; - field(field_name, field_value, insertable, updatable, is_primary_key); + field(field_name, field_value, insertable, updatable, is_primary_key, model_env); this->out_stream = nullptr; } @@ -231,7 +239,13 @@ public: //virtual void esc(void* val, PT::TextStream & stream); virtual void esc(const PT::Date & date, PT::TextStream & stream); + virtual void esc(const PT::TextStream & val,PT::TextStream & stream); + virtual bool is_long_field_name(const wchar_t * field_name); + virtual bool is_long_field_name(const PT::TextStream & table_name); + + virtual bool is_long_table_name(const wchar_t * field_name); + virtual bool is_long_table_name(const PT::TextStream & table_name); @@ -240,15 +254,9 @@ protected: int work_mode; /* what to do: generating fields list, values list or fields-values list */ bool is_first_field; - // niech Stream bedzie jakims interfejsem z operatorami << dla standardowych typow - // albo w pikotoolsach dodać klase bazowa (intefejs) dla streamow - // i dodatkowo dodac loger ktory dziedziczy po niej i dodaje loglevel i endline - // jako metode bazowa dodac format("string", parametry,...) - // przyda sie do formatowania doubli PT::TextStream * out_stream; - - std::wstring column_prefix; + bool use_prefix; virtual void generate_from_model(Model & model); @@ -260,7 +268,12 @@ protected: //void field(const wchar_t * field_name, Model & field, bool insertable = true, bool updatable = true); - virtual void put_field_name(const wchar_t * field_name, bool add_column_prefix = true); + virtual bool need_to_add_field_prefix(const wchar_t * field_name); + + virtual void put_field_name(const wchar_t * field_name, ModelEnv * model_env); + + virtual void save_foreign_key(const wchar_t * field_name, ModelEnv * model_env); + template void put_field_value(const FieldValue & field_value) @@ -273,49 +286,66 @@ protected: } } + + virtual void before_field_value_list() + { + } + + + virtual void after_field_value_list() + { + } + + + virtual void field_value_list_separator() + { + (*out_stream) << ","; + } + + virtual void put_statement_in_starts() + { + (*out_stream) << "("; + } + + virtual void put_statement_in_ends() + { + (*out_stream) << ") "; + } + // what about lists with a pod types? e.g. list template - void put_field_value_list(ModelContainer & field_value, ModelConnector * model_connector, int model_connector_mode, ModelData * model_data) + void put_field_value_list(ModelContainer & field_value, ModelConnector * model_connector, ModelEnv * model_env) { - if( out_stream ) + if( model_connector && model_env && out_stream ) { - (*out_stream) << "[";// make a virtual method - } + bool is_first = true; + before_field_value_list(); - bool is_first = true; - - for(auto & m : field_value) - { - if( out_stream ) + for(auto & m : field_value) { if( !is_first ) { - if( out_stream ) - (*out_stream) << ","; // make a virtual method + field_value_list_separator(); } + ModelEnv model_env_local(*model_env); + m.model_env = &model_env_local; //before_field_value(field_value); m.set_connector(model_connector); - m.model_connector_mode = model_connector_mode; - m.model_data = model_data; generate_from_model(m); - m.model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; - m.model_data = nullptr; + m.model_env = nullptr; //after_field_value(field_value); is_first = false; } - } - if( out_stream ) - { - (*out_stream) << "]";// make a virtual method + after_field_value_list(); } } // used in 'in()' statements, may should be renamed? template - void field_in_generic(PT::TextStream & stream, const wchar_t * field_name, const Container & container) + void field_in_generic(PT::TextStream & stream, const wchar_t * field_name, const Container & container, ModelEnv * model_env) { // IMPROVE ME // what about if container is empty? @@ -323,24 +353,24 @@ protected: this->out_stream = &stream; field_before(); - put_field_name(field_name); + put_field_name(field_name, model_env); put_name_value_separator(); bool is_first = true; - (*out_stream) << "("; + put_statement_in_starts(); for(const FieldValue & v : container) { if( !is_first ) { - (*out_stream) << ","; + field_value_list_separator(); } put_field_value(v); is_first = false; } - (*out_stream) << ") "; + put_statement_in_ends(); field_after(); this->out_stream = nullptr; } diff --git a/src/cursor.h b/src/cursor.h index 9ff9aa5..e167330 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,7 +51,8 @@ public: Cursor() { - set_default_values(); + query_result = nullptr; + clear(); } @@ -59,6 +60,7 @@ public: { model_connector = c.model_connector; model_data = c.model_data; + has_autogenerated_select = c.has_autogenerated_select; query_result = c.query_result; select_status = c.select_status; @@ -66,6 +68,9 @@ public: { query_result->references_count += 1; } + + // helper doesn't have to be copied + cursor_helper.clear(); } @@ -91,7 +96,13 @@ public: delete query_result; } - set_default_values(); + model_connector = nullptr; + model_data = nullptr; + has_autogenerated_select = false; + cursor_helper.clear(); + query_result = nullptr; + select_status = false; + } @@ -119,6 +130,12 @@ public: } + virtual void set_has_autogenerated_select(bool has_autogenerated_select) + { + this->has_autogenerated_select = has_autogenerated_select; + } + + virtual QueryResult * get_query_result() { return query_result; @@ -150,9 +167,17 @@ public: if( db_connector ) { + ModelEnv model_env_local; + result.model_env = &model_env_local; + result.model_env->cursor_helper = &cursor_helper; + try { - result.model_data = model_data; + cursor_helper.clear(); + cursor_helper.query_result = query_result; + cursor_helper.has_autogenerated_select = has_autogenerated_select; + + result.model_env->model_data = model_data; result.before_select(); res = select_status; @@ -161,7 +186,7 @@ public: if( query_result->cur_row < query_result->result_rows ) { result.set_save_mode(Model::DO_UPDATE_ON_SAVE); // IMPROVE ME check if there is a primary key - result.map_values_from_query(query_result); + result.map_values_from_query(); result.after_select(); query_result->cur_row += 1; } @@ -182,7 +207,7 @@ public: // throw something? } - result.model_data = nullptr; + result.model_env = nullptr; } } @@ -195,7 +220,7 @@ public: ModelClass model; get(model); - return std::move(model); + return model; } @@ -258,17 +283,12 @@ protected: ModelConnector * model_connector; ModelData * model_data; + bool has_autogenerated_select; + CursorHelper cursor_helper; QueryResult * query_result; bool select_status; - virtual void set_default_values() - { - model_connector = nullptr; - model_data = nullptr; - query_result = nullptr; - select_status = false; - } template @@ -281,14 +301,23 @@ protected: result.emplace_back(); // it returns a reference from c++17 ModelClass & added_model = result.back(); + ModelEnv model_env_local; + try { + cursor_helper.clear(); + cursor_helper.query_result = query_result; + cursor_helper.has_autogenerated_select = has_autogenerated_select; + added_model.set_connector(model_connector); added_model.clear(); + + added_model.model_env = &model_env_local; + added_model.model_env->cursor_helper = &cursor_helper; added_model.set_save_mode(Model::DO_UPDATE_ON_SAVE); // IMPROVE ME check if there is a primary key - added_model.model_data = model_data; + added_model.model_env->model_data = model_data; added_model.before_select(); - added_model.map_values_from_query(query_result); + added_model.map_values_from_query(); added_model.after_select(); } catch(...) @@ -297,7 +326,7 @@ protected: // throw or something? } - added_model.model_data = nullptr; + added_model.model_env = nullptr; } return res; diff --git a/src/cursorhelper.h b/src/cursorhelper.h new file mode 100644 index 0000000..2b469f2 --- /dev/null +++ b/src/cursorhelper.h @@ -0,0 +1,76 @@ +/* + * 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_cursorhelper +#define headerfile_morm_cursorhelper + +#include "queryresult.h" + + +namespace morm +{ + +class CursorHelper +{ +public: + + bool has_autogenerated_select; + QueryResult * query_result; + int current_column; + + + + CursorHelper() + { + clear(); + } + + + virtual ~CursorHelper() + { + } + + + virtual void clear() + { + has_autogenerated_select = false; + query_result = nullptr; + current_column = 0; + } + +}; + +} + +#endif diff --git a/src/dbconnector.cpp b/src/dbconnector.cpp index 1c5bf27..5ec5569 100644 --- a/src/dbconnector.cpp +++ b/src/dbconnector.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,7 @@ DbConnector::DbConnector() db_expression = nullptr; expression_allocated = false; log = nullptr; + log_queries = false; } DbConnector::DbConnector(const DbConnector &) @@ -70,12 +71,18 @@ void DbConnector::set_logger(PT::Log * log) this->log = log; } + void DbConnector::set_logger(PT::Log & log) { this->log = &log; } +void DbConnector::set_log_queries(bool log_queries) +{ + this->log_queries = log_queries; +} + bool DbConnector::query(const PT::TextStream & stream, QueryResult & query_result) { @@ -151,16 +158,16 @@ DbExpression * DbConnector::get_expression() } -void DbConnector::generate_select_columns(PT::TextStream & stream, Model & model, const std::wstring & column_prefix) +void DbConnector::generate_select_columns(PT::TextStream & stream, Model & model) { allocate_default_expression_if_needed(); if( db_expression ) { - db_expression->prepare_to_new_expression(); + db_expression->clear(); db_expression->set_work_mode(MORM_WORK_MODE_MODEL_FIELDS); db_expression->set_output_type(MORM_OUTPUT_TYPE_SELECT_COLUMNS); - db_expression->set_column_prefix(column_prefix); + db_expression->allow_to_use_prefix(true); db_expression->generate_from_model(stream, model); } } @@ -172,7 +179,9 @@ void DbConnector::generate_insert_query(PT::TextStream & stream, Model & model) if( db_expression ) { - db_expression->prepare_to_new_expression(); + db_expression->clear(); + db_expression->allow_to_use_prefix(false); + stream << "insert into "; model.table_name(stream); @@ -195,7 +204,9 @@ void DbConnector::generate_update_query(PT::TextStream & stream, Model & model) if( db_expression ) { - db_expression->prepare_to_new_expression(); + db_expression->clear(); + db_expression->allow_to_use_prefix(false); + stream << "update "; model.table_name(stream); @@ -218,7 +229,9 @@ void DbConnector::generate_remove_query(PT::TextStream & stream, Model & model) if( db_expression ) { - db_expression->prepare_to_new_expression(); + db_expression->clear(); + db_expression->allow_to_use_prefix(false); + stream << "delete from "; model.table_name(stream); stream << " where "; diff --git a/src/dbconnector.h b/src/dbconnector.h index 34b7907..7e2753d 100644 --- a/src/dbconnector.h +++ b/src/dbconnector.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,11 +57,13 @@ public: virtual void set_logger(PT::Log * log); virtual void set_logger(PT::Log & log); + virtual void set_log_queries(bool log_queries); + DbExpression * get_expression(); //virtual void clear_last_query_result(); - virtual void generate_select_columns(PT::TextStream & stream, Model & model, const std::wstring & column_prefix); + virtual void generate_select_columns(PT::TextStream & stream, Model & model); virtual void generate_insert_query(PT::TextStream & stream, Model & model); virtual void generate_update_query(PT::TextStream & stream, Model & model); virtual void generate_remove_query(PT::TextStream & stream, Model & model); @@ -115,6 +117,11 @@ public: if( val_str ) { get_value(val_str, field_value); + + if( log && log_queries ) + { + (*log) << PT::Log::log3 << "Morm: sequence value: " << field_value << PT::Log::logend; + } } } @@ -124,6 +131,7 @@ protected: DbExpression * db_expression; bool expression_allocated; PT::Log * log; + bool log_queries; virtual void allocate_default_expression() = 0; diff --git a/src/dbexpression.cpp b/src/dbexpression.cpp index f7ba295..c489b7f 100644 --- a/src/dbexpression.cpp +++ b/src/dbexpression.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,6 +56,12 @@ void DbExpression::set_output_type(int output_type) } +int DbExpression::get_output_type() +{ + return output_type; +} + + bool DbExpression::can_field_be_generated(bool insertable, bool updatable, bool is_primary_key) { if( output_type == MORM_OUTPUT_TYPE_DB_INSERT ) @@ -68,7 +74,7 @@ bool DbExpression::can_field_be_generated(bool insertable, bool updatable, bool return updatable; } else - if( output_type == MORM_OUTPUT_TYPE_DB_PRIMARY_KEY ) + if( output_type == MORM_OUTPUT_TYPE_DB_PRIMARY_KEY || output_type == MORM_OUTPUT_TYPE_JOIN_TABLES ) { return is_primary_key; } @@ -90,7 +96,7 @@ void DbExpression::field_before() output_type == MORM_OUTPUT_TYPE_DB_UPDATE || output_type == MORM_OUTPUT_TYPE_SELECT_COLUMNS ) { - (*out_stream) << ","; + (*out_stream) << ", "; } else if( output_type == MORM_OUTPUT_TYPE_DB_PRIMARY_KEY ) @@ -131,27 +137,32 @@ void DbExpression::put_name_value_separator() output_type == MORM_OUTPUT_TYPE_WHERE_EQ || output_type == MORM_OUTPUT_TYPE_DB_UPDATE) { - (*out_stream) << " = "; + (*out_stream) << "="; + } + else + if( output_type == MORM_OUTPUT_TYPE_WHERE_NOT_EQ ) + { + (*out_stream) << "<>"; } else if( output_type == MORM_OUTPUT_TYPE_WHERE_LT ) { - (*out_stream) << " < "; + (*out_stream) << "<"; } else if( output_type == MORM_OUTPUT_TYPE_WHERE_GT ) { - (*out_stream) << " > "; + (*out_stream) << ">"; } else if( output_type == MORM_OUTPUT_TYPE_WHERE_LE ) { - (*out_stream) << " <= "; + (*out_stream) << "<="; } else if( output_type == MORM_OUTPUT_TYPE_WHERE_GE ) { - (*out_stream) << " >= "; + (*out_stream) << ">="; } else if( output_type == MORM_OUTPUT_TYPE_WHERE_IN ) @@ -290,4 +301,40 @@ DbExpression & DbExpression::page(PT::TextStream & stream, size_t page_number, s return *this; } + + + + +void DbExpression::prepare_short_table_name(const PT::TextStream & table_name, PT::TextStream & short_table_name) +{ + short_table_name.clear(); + + if( is_long_table_name(table_name) ) + { + PT::TextStream::const_iterator i = table_name.begin(); + bool was_dot = false; + + while( i != table_name.end() ) + { + if( was_dot ) + { + short_table_name << *i; + } + else + if( *i == '.' ) + { + was_dot = true; + } + + ++i; + } + } + + if( short_table_name.empty() ) + { + short_table_name = table_name; + } +} + + } diff --git a/src/dbexpression.h b/src/dbexpression.h index b48b989..53d2dba 100644 --- a/src/dbexpression.h +++ b/src/dbexpression.h @@ -51,6 +51,7 @@ public: virtual ~DbExpression(); virtual void set_output_type(int output_type); + virtual int get_output_type(); virtual void prepare_to_where_clause(); @@ -61,7 +62,6 @@ public: virtual DbExpression & page(PT::TextStream & stream, size_t page_number, size_t page_size); - template void add_field_for_select(const wchar_t * new_column_expression, const wchar_t * new_column_name, FieldValue & field_value) { @@ -71,10 +71,15 @@ public: column_expression += L" as "; column_expression += new_column_name; - field(column_expression.c_str(), field_value, false, false, false, false); + // put nullptr to ModelEnv* to not allow to use prefix + // or may better remember current value of use_prefix and set it to false for a while? + field(column_expression.c_str(), field_value, false, false, false, nullptr); } + virtual void prepare_short_table_name(const PT::TextStream & table_name, PT::TextStream & short_table_name); + + protected: int output_type; diff --git a/src/finder.h b/src/finder.h index 0fdb3a3..86faf13 100644 --- a/src/finder.h +++ b/src/finder.h @@ -39,6 +39,7 @@ #include #include #include "model.h" +#include "utf8/utf8.h" #include "textstream/textstream.h" #include "dbconnector.h" #include "modelconnector.h" @@ -115,23 +116,6 @@ public: -protected: - - void set_out_stream() - { - if( model_connector ) - { - this->out_stream = model_connector->get_stream(); - } - else - { - this->out_stream = nullptr; - } - } - - -public: - Finder & set_out_stream(PT::TextStream * out_stream) { this->out_stream = out_stream; @@ -156,25 +140,18 @@ public: return was_query_error; } + std::wstring get_error_msg() { return last_query_error; } - Finder & prefix(const wchar_t * prefix) - { - column_prefix = prefix; - return *this; - } - - Finder & prepare_to_select() { was_query_error = false; last_query_error.clear(); - model_data = nullptr; - column_prefix.clear(); + has_autogenerated_select = false; if( model_connector ) { @@ -182,7 +159,6 @@ public: { set_db_expression(); out_stream->clear(); - model.set_connector(model_connector); } else { @@ -242,22 +218,36 @@ public: prepare_to_select(); } - if( model_connector && out_stream ) + model.set_connector(model_connector); + + finder_helper.clear(); + model_env.clear(); + + model.model_env = &model_env; + model.model_env->model_data = model_data; + model.model_env->finder_helper = &finder_helper; + + has_autogenerated_select = true; + + if( model_connector && out_stream && db_expression ) { + model.table_name(model.model_env->table_name); + db_expression->prepare_short_table_name(model.model_env->table_name, model.model_env->table_name_short); + + model.model_env->table_index = finder_helper.add_join_table(model.model_env->table_name_short); + (*out_stream) << "SELECT "; - model.generate_select_columns(*out_stream, column_prefix); - (*out_stream) << " FROM "; - model.table_name(*out_stream); - - if( !column_prefix.empty() ) - (*out_stream) << " AS " << column_prefix; // what about escaping? - + model.generate_select_columns(*out_stream); + (*out_stream) << " FROM " << model.model_env->table_name << " AS "; + (*out_stream) << model.model_env->table_name_short; (*out_stream) << " "; + (*out_stream) << finder_helper.join_tables_str; } return *this; } + Finder & select(ModelData * model_data, bool call_prepare = true) { if( call_prepare ) @@ -269,6 +259,7 @@ public: return select(false); } + Finder & select(ModelData & model_data, bool call_prepare = true) { if( call_prepare ) @@ -280,6 +271,7 @@ public: return select(false); } + Finder & where() { if( out_stream && db_expression ) @@ -333,20 +325,32 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_EQ); - db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); } return *this; } + template + Finder & neq(const wchar_t * field_name, const FieldValue & field_value) + { + if( db_expression ) + { + db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_NOT_EQ); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); + } + + return *this; + } + template Finder & lt(const wchar_t * field_name, const FieldValue & field_value) { if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_LT); - db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); } return *this; @@ -359,7 +363,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_GT); - db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); } return *this; @@ -372,7 +376,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_LE); - db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); } return *this; @@ -385,7 +389,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_GE); - db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false); + db_expression->field_to_stream(*out_stream, field_name, field_value, false, false, false, &model_env); } return *this; @@ -399,7 +403,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_IN); - db_expression->field_in(*out_stream, field_name, container); + db_expression->field_in(*out_stream, field_name, container, &model_env); } return *this; @@ -411,7 +415,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_IN); - db_expression->field_in(*out_stream, field_name, container); + db_expression->field_in(*out_stream, field_name, container, &model_env); } return *this; @@ -423,7 +427,7 @@ public: if( db_expression ) { db_expression->set_output_type(MORM_OUTPUT_TYPE_WHERE_IN); - db_expression->field_in(*out_stream, field_name, container); + db_expression->field_in(*out_stream, field_name, container, &model_env); } return *this; @@ -495,6 +499,7 @@ public: { Cursor cursor; cursor.set_model_data(model_data); + cursor.set_has_autogenerated_select(has_autogenerated_select); if( model_connector && out_stream ) { @@ -544,7 +549,23 @@ public: std::list result; get_list(result, false); - return std::move(result); + return result; + } + + + +protected: + + void set_out_stream() + { + if( model_connector ) + { + this->out_stream = model_connector->get_stream(); + } + else + { + this->out_stream = nullptr; + } } @@ -559,9 +580,10 @@ private: ModelClass model; bool was_query_error; std::wstring last_query_error; + ModelEnv model_env; + FinderHelper finder_helper; ModelData * model_data; - std::wstring column_prefix; - + bool has_autogenerated_select; void set_db_expression() @@ -578,6 +600,8 @@ private: } + + }; } // namespace diff --git a/src/finderhelper.h b/src/finderhelper.h new file mode 100644 index 0000000..4898fe8 --- /dev/null +++ b/src/finderhelper.h @@ -0,0 +1,101 @@ +/* + * 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_finderhelper +#define headerfile_morm_finderhelper + +#include "queryresult.h" + + +namespace morm +{ + + +class FinderHelper +{ +public: + + PT::TextStream join_tables_str; + + std::map join_tables_map; + + std::list foreign_keys; + + + + FinderHelper() + { + } + + + virtual ~FinderHelper() + { + } + + + virtual void clear() + { + join_tables_str.clear(); + join_tables_map.clear(); + foreign_keys.clear(); + } + + + virtual int add_join_table(const PT::TextStream & table_name) + { + std::string table_name_str; + table_name.to_string(table_name_str); + + return add_join_table(table_name_str); + } + + + virtual int add_join_table(const std::string & table_name) + { + auto res = join_tables_map.insert(std::make_pair(table_name, 1)); + + if( !res.second ) + { + res.first->second += 1; + } + + return res.first->second; + } + + +}; + +} + +#endif diff --git a/src/flatconnector.cpp b/src/flatconnector.cpp index d686fd5..99bdb7d 100644 --- a/src/flatconnector.cpp +++ b/src/flatconnector.cpp @@ -74,7 +74,9 @@ void FlatConnector::to_text(PT::TextStream & stream, Model & model) if( flat_expression ) { + flat_expression->clear(); flat_expression->set_work_mode(MORM_WORK_MODE_MODEL_FIELDS_VALUES); + flat_expression->allow_to_use_prefix(false); flat_expression->generate_from_model(stream, model); } } diff --git a/src/jsonexpression.cpp b/src/jsonexpression.cpp index 6987460..1212c3f 100644 --- a/src/jsonexpression.cpp +++ b/src/jsonexpression.cpp @@ -137,6 +137,18 @@ void JSONExpression::put_name_value_separator() } +void JSONExpression::before_field_value_list() +{ + (*out_stream) << "["; +} + + +void JSONExpression::after_field_value_list() +{ + (*out_stream) << "]"; +} + + void JSONExpression::esc(char val, PT::TextStream & stream) { switch( val ) diff --git a/src/jsonexpression.h b/src/jsonexpression.h index abbdb24..2d149f5 100644 --- a/src/jsonexpression.h +++ b/src/jsonexpression.h @@ -64,15 +64,8 @@ protected: void before_field_value(const PT::Date &); void after_field_value(const PT::Date &); - - template - void print_field_name_value(const wchar_t * field_name, const FieldValue & field_value) - { - put_field_name(field_name); - (*out_stream) << ":"; - put_field_value(field_value); - } - + void before_field_value_list(); + void after_field_value_list(); void esc(char val, PT::TextStream & stream); diff --git a/src/model.cpp b/src/model.cpp index ba899fa..7ce8d7f 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,11 +41,8 @@ namespace morm Model::Model() { model_connector = nullptr; - model_data = nullptr; - doc_field_pointer = nullptr; + model_env = nullptr; save_mode = DO_INSERT_ON_SAVE; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; - query_result = nullptr; } @@ -53,11 +50,7 @@ Model::Model(const Model & m) { model_connector = m.model_connector; save_mode = m.save_mode; - - model_data = nullptr; - doc_field_pointer = nullptr; // does it need to be copied? - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; - query_result = nullptr; + model_env = m.model_env; // or just set to null? } @@ -65,6 +58,7 @@ Model::~Model() { } + void Model::set_save_mode(SaveMode save_mode) { this->save_mode = save_mode; @@ -105,6 +99,7 @@ void Model::table_name(PT::TextStream & stream) } + void Model::set_connector(ModelConnector & connector) { set_connector(&connector); @@ -136,7 +131,10 @@ void Model::to_text(PT::TextStream & stream, ModelData * model_data, bool clear_ stream.clear(); } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING; + ModelEnv model_env_local; + model_env = &model_env_local; + + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING; if( model_connector ) { @@ -144,13 +142,14 @@ void Model::to_text(PT::TextStream & stream, ModelData * model_data, bool clear_ if( flat_connector ) { - this->model_data = model_data; + model_env->model_data = model_data; flat_connector->to_text(stream, *this); - this->model_data = nullptr; + model_env->model_data = nullptr; } } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + // what about if an exception was thrown? this pointer will not be null + model_env = nullptr; } @@ -213,7 +212,7 @@ std::string Model::to_string() void Model::generate_insert_query(PT::TextStream & stream) { - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; if( model_connector ) { @@ -225,7 +224,7 @@ void Model::generate_insert_query(PT::TextStream & stream) } } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } @@ -236,8 +235,11 @@ bool Model::insert(ModelData * model_data) if( model_connector ) { - this->model_data = model_data; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + ModelEnv model_env_local; + model_env = &model_env_local; + + model_env->model_data = model_data; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; DbConnector * db_connector = model_connector->get_db_connector(); // CHECK ME what if the stream is being used by something other? @@ -260,8 +262,8 @@ bool Model::insert(ModelData * model_data) } } - this->model_data = nullptr; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + // what about if an exception was thrown? this pointer will not be null + model_env = nullptr; } return result; @@ -277,7 +279,7 @@ bool Model::insert(ModelData & model_data) void Model::generate_update_query(PT::TextStream & stream) { - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; if( model_connector ) { @@ -289,7 +291,7 @@ void Model::generate_update_query(PT::TextStream & stream) } } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } @@ -299,8 +301,11 @@ bool Model::update(ModelData * model_data) if( model_connector ) { - this->model_data = model_data; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + ModelEnv model_env_local; + model_env = &model_env_local; + + model_env->model_data = model_data; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; DbConnector * db_connector = model_connector->get_db_connector(); // CHECK ME what if the stream is being used by something other? @@ -318,8 +323,8 @@ bool Model::update(ModelData * model_data) after_update_failure(); } - this->model_data = nullptr; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + // what about if an exception was thrown? this pointer will not be null + model_env = nullptr; } return result; @@ -337,14 +342,14 @@ void Model::generate_remove_query(PT::TextStream & stream) if( model_connector ) { DbConnector * db_connector = model_connector->get_db_connector(); - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; if( db_connector ) { db_connector->generate_remove_query(stream, *this); } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } } @@ -355,8 +360,11 @@ bool Model::remove(ModelData * model_data) if( model_connector ) { - this->model_data = model_data; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + ModelEnv model_env_local; + model_env = &model_env_local; + + model_env->model_data = model_data; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; DbConnector * db_connector = model_connector->get_db_connector(); // CHECK ME what if the stream is being used by something other? @@ -379,8 +387,8 @@ bool Model::remove(ModelData * model_data) } } - this->model_data = nullptr; - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + // what about if an exception was thrown? this pointer will not be null + model_env = nullptr; } return result; @@ -426,19 +434,17 @@ bool Model::save(ModelData & model_data) } -void Model::generate_select_columns(PT::TextStream & stream, const std::wstring & column_prefix) +void Model::generate_select_columns(PT::TextStream & stream) { - if( model_connector ) + if( model_connector && model_env ) { - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { - db_connector->generate_select_columns(stream, *this, column_prefix); + db_connector->generate_select_columns(stream, *this); } - - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } } @@ -451,7 +457,7 @@ void Model::generate_doc_for_flat(PT::TextStream & stream, bool clear_stream) stream.clear(); } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT; if( model_connector ) { @@ -463,7 +469,7 @@ void Model::generate_doc_for_flat(PT::TextStream & stream, bool clear_stream) } } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } @@ -474,7 +480,7 @@ void Model::generate_doc_for_db(PT::TextStream & stream, bool clear_stream) stream.clear(); } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_DB; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_DB; if( model_connector ) { @@ -486,31 +492,32 @@ void Model::generate_doc_for_db(PT::TextStream & stream, bool clear_stream) } } - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; } -void Model::map_values_from_query(QueryResult * query_result) +void Model::map_values_from_query() { - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_READING_VALUE_FROM_DB_RESULTSET; - this->query_result = query_result; - - map_fields(); - - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; - this->query_result = nullptr; + if( model_env ) + { + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_READING_VALUE_FROM_DB_RESULTSET; + map_fields(); + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + } } void Model::clear() { - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE; + ModelEnv model_env_local; + model_env = &model_env_local; + + model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE; map_fields(); - model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + model_env = nullptr; save_mode = DO_INSERT_ON_SAVE; - model_data = nullptr; } @@ -569,9 +576,30 @@ void Model::after_remove_failure() int Model::get_connector_mode() { - return model_connector_mode; + if( model_env ) + { + return model_env->model_connector_mode; + } + else + { + return MORM_MODEL_CONNECTOR_MODE_NONE; + } } + +ModelData * Model::get_model_data() +{ + if( model_env ) + { + return model_env->model_data; + } + else + { + return nullptr; + } +} + + bool Model::is_empty_field(const wchar_t * value) { return (!value || *value == '\0'); diff --git a/src/model.h b/src/model.h index 730af48..e14b616 100644 --- a/src/model.h +++ b/src/model.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,8 +41,7 @@ #include "modelconnector.h" #include "dbexpression.h" #include "flatexpression.h" -#include "queryresult.h" -#include "modeldata.h" +#include "modelenv.h" @@ -97,33 +96,33 @@ public: */ virtual void table_name(PT::TextStream & stream); - virtual void to_text(PT::TextStream & stream, ModelData * model_data = nullptr, bool clear_stream = true); - virtual void to_text(PT::TextStream & stream, ModelData & model_data, bool clear_stream = true); + virtual void to_text(PT::TextStream & stream, ModelData * model_data, bool clear_stream = true); + virtual void to_text(PT::TextStream & stream, ModelData & model_data, bool clear_stream = true); virtual void to_text(PT::TextStream & stream, bool clear_stream = true); - virtual void to_text(std::string & str, ModelData * model_data = nullptr, bool clear_string = true); - virtual void to_text(std::string & str, ModelData & model_data, bool clear_string = true); + virtual void to_text(std::string & str, ModelData * model_data, bool clear_string = true); + virtual void to_text(std::string & str, ModelData & model_data, bool clear_string = true); virtual void to_text(std::string & str, bool clear_string = true); virtual std::string to_text(); virtual std::string to_string(); - virtual void generate_insert_query(PT::TextStream & stream); + virtual void generate_insert_query(PT::TextStream & stream); // FIX ME needed model_data virtual bool insert(ModelData * model_data = nullptr); virtual bool insert(ModelData & model_data); - virtual void generate_update_query(PT::TextStream & stream); + virtual void generate_update_query(PT::TextStream & stream); // FIX ME needed model_data virtual bool update(ModelData * model_data = nullptr); virtual bool update(ModelData & model_data); - virtual void generate_remove_query(PT::TextStream & stream); + virtual void generate_remove_query(PT::TextStream & stream); // FIX ME needed model_data virtual bool remove(ModelData * model_data = nullptr); virtual bool remove(ModelData & model_data); virtual bool save(ModelData * model_data = nullptr); virtual bool save(ModelData & model_data); - virtual void generate_select_columns(PT::TextStream & stream, const std::wstring & column_prefix); + virtual void generate_select_columns(PT::TextStream & stream); virtual void generate_doc_for_flat(PT::TextStream & stream, bool clear_stream = true); virtual void generate_doc_for_db(PT::TextStream & stream, bool clear_stream = true); @@ -132,14 +131,13 @@ public: virtual void clear(); + protected: ModelConnector * model_connector; SaveMode save_mode; - ModelData * model_data; - const void * doc_field_pointer; - int model_connector_mode; - QueryResult * query_result; + ModelEnv * model_env; + Model(); @@ -163,8 +161,10 @@ protected: virtual int get_connector_mode(); - // used by Cursor - virtual void map_values_from_query(QueryResult * query_result); + virtual ModelData * get_model_data(); + + + virtual void map_values_from_query(); ///////////////////////////////// @@ -269,15 +269,16 @@ protected: field_generic(field_name, field_name, field_value, insertable, updatable, is_primary_key); } - void field(const wchar_t * field_name, Model & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field(const wchar_t * field_name, Model & field_value, bool insertable = true, bool updatable = true) { - field_model(field_name, field_name, field_value, insertable, updatable, is_primary_key); + field_model(field_name, field_name, field_value, insertable, updatable, true); } template - void field(const wchar_t * field_name, std::list & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field(const wchar_t * field_name, std::list & field_value, bool insertable = true, bool updatable = true) { - field_list(field_name, field_name, field_value, insertable, updatable, is_primary_key); + ModelClass * list_model_null_pointer = nullptr; + field_list(field_name, field_name, field_value, list_model_null_pointer, insertable, updatable); } @@ -384,15 +385,16 @@ protected: } - void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_value, bool insertable = true, bool updatable = true) { - field_model(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key); + field_model(db_field_name, flat_field_name, field_value, insertable, updatable, true); } template - void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::list & field_value, bool insertable = true, bool updatable = true, bool is_primary_key = false) + void field(const wchar_t * db_field_name, const wchar_t * flat_field_name, std::list & field_value, bool insertable = true, bool updatable = true) { - field_list(db_field_name, flat_field_name, field_value, insertable, updatable, is_primary_key); + ModelClass * list_model_null_pointer = nullptr; + field_list(db_field_name, flat_field_name, field_value, list_model_null_pointer, insertable, updatable); } ////////////////////// @@ -631,9 +633,9 @@ protected: template void field_generic(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value, bool insertable, bool updatable, bool is_primary_key) { - if( model_connector ) + if( model_connector && model_env ) { - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) { FlatConnector * flat_connector = model_connector->get_flat_connector(); @@ -644,12 +646,12 @@ protected: if( flat_expression && !is_empty_field(flat_field_name) ) { // insertable, updatable and is_primary_key are ignored here - flat_expression->field(flat_field_name, field_value, insertable, updatable, is_primary_key); + flat_expression->field(flat_field_name, field_value, insertable, updatable, is_primary_key, model_env); } } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL ) { DbConnector * db_connector = model_connector->get_db_connector(); @@ -659,23 +661,33 @@ protected: if( db_expression && !is_empty_field(db_field_name) ) { - db_expression->field(db_field_name, field_value, insertable, updatable, is_primary_key); + db_expression->field(db_field_name, field_value, insertable, updatable, is_primary_key, model_env); } } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_READING_VALUE_FROM_DB_RESULTSET ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_READING_VALUE_FROM_DB_RESULTSET ) { DbConnector * db_connector = model_connector->get_db_connector(); if( db_connector ) { if( !is_empty_field(db_field_name) ) - get_value_by_field_name(db_field_name, field_value); + { + if( model_env->model_data && model_env->cursor_helper && model_env->cursor_helper->has_autogenerated_select ) + { + get_value_by_field_index(model_env->cursor_helper->current_column, field_value); + model_env->cursor_helper->current_column += 1; + } + else + { + get_value_by_field_name(db_field_name, field_value); + } + } } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) { Clearer * clearer = model_connector->get_clearer(); @@ -685,7 +697,7 @@ protected: } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) { FlatConnector * doc_connector = model_connector->get_doc_connector(); // different connector will be here (doc, flat) @@ -697,7 +709,7 @@ protected: if( doc_expression && !is_empty_field(flat_field_name) ) { // insertable, updatable and is_primary_key are ignored here - doc_expression->field_doc(*this, flat_field_name, field_value, insertable, updatable, is_primary_key); + doc_expression->field_doc(*this, flat_field_name, field_value, insertable, updatable, is_primary_key, model_env); } } } @@ -705,70 +717,205 @@ protected: } - void field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool is_primary_key) + void field_model_left_join(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key, DbExpression * db_expression) { - if( model_connector ) + if( model_env && field_model.model_env && model_env->finder_helper ) { + model_env->finder_helper->foreign_keys.clear(); + field_model.model_env->table_index = model_env->finder_helper->add_join_table(field_model.model_env->table_name_short); + + model_env->finder_helper->join_tables_str << "LEFT JOIN " << field_model.model_env->table_name + << " AS " << field_model.model_env->table_name_short; + + if( field_model.model_env->table_index > 1 ) + model_env->finder_helper->join_tables_str << field_model.model_env->table_index; + + 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( has_foreign_key ) + { + field_model.map_fields(); + + model_env->finder_helper->join_tables_str << " ON " << model_env->table_name_short << '.' + << db_field_name << " = " << field_model.model_env->table_name_short << '.'; + + if( model_env->finder_helper->foreign_keys.size() == 1 ) + { + model_env->finder_helper->join_tables_str << model_env->finder_helper->foreign_keys.front(); + } + } + else + { + map_fields(); + + model_env->finder_helper->join_tables_str << " ON " << model_env->table_name_short << '.'; + + if( model_env->finder_helper->foreign_keys.size() == 1 ) + { + model_env->finder_helper->join_tables_str << model_env->finder_helper->foreign_keys.front(); + } + + model_env->finder_helper->join_tables_str << " = " << field_model.model_env->table_name_short << '.' << db_field_name; + } + + model_env->finder_helper->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); + } + } + + + + void field_model_generate_flat_string(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable) + { + FlatConnector * flat_connector = model_connector->get_flat_connector(); + + if( flat_connector ) + { + FlatExpression * flat_expression = flat_connector->get_expression(); + + if( flat_expression && !is_empty_field(flat_field_name) ) + { + field_model.model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING; + flat_expression->field_model(flat_field_name, field_model, insertable, updatable, false, model_env); + } + } + } + + + void field_model_generate_db_sql(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key) + { + 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_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL; + + field_model.table_name(field_model.model_env->table_name); + db_expression->prepare_short_table_name(field_model.model_env->table_name, field_model.model_env->table_name_short); + + if( db_expression->get_output_type() == MORM_OUTPUT_TYPE_SELECT_COLUMNS ) + { + field_model_left_join(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key, db_expression); + } + + if( db_expression->get_output_type() != MORM_OUTPUT_TYPE_JOIN_TABLES ) + { + field_model.map_fields(); + } + + field_model.model_env->model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + } + } + } + + + void field_model_clear_values(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key) + { + Clearer * clearer = model_connector->get_clearer(); + + if( clearer ) + { + clearer->clear_model(field_model); + } + } + + + void field_model_generate_doc_flat(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key) + { + FlatConnector * doc_connector = model_connector->get_doc_connector(); // different connector will be here (doc, flat) + + if( doc_connector ) + { + FlatExpression * doc_expression = doc_connector->get_expression(); + + // potrzebujemy dwa rozne doce, jeden dla flat drugi dla bazy danych + if( doc_expression && !is_empty_field(flat_field_name) ) + { + // insertable, updatable and is_primary_key are ignored here + doc_expression->field_doc(*this, flat_field_name, field_model, insertable, updatable, false, model_env); + } + } + } + + + void field_model_read_values_from_queryresult(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key) + { + DbConnector * db_connector = model_connector->get_db_connector(); + + if( db_connector ) + { + if( !is_empty_field(db_field_name) ) + { + // we need to test if the object is actually defined, test nulls on primary key? + + field_model.before_select(); + field_model.set_save_mode(Model::DO_UPDATE_ON_SAVE); // IMPROVE ME check if there is a primary key + field_model.map_values_from_query(); + field_model.after_select(); + } + } + } + + + void field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model, bool insertable, bool updatable, bool has_foreign_key) + { + 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.set_connector(model_connector); - field_model.model_data = model_data; - // IMPLEMENTME what about db? - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) { - FlatConnector * flat_connector = model_connector->get_flat_connector(); - - if( flat_connector ) - { - FlatExpression * flat_expression = flat_connector->get_expression(); - - if( flat_expression && !is_empty_field(flat_field_name) ) - { - field_model.model_connector_mode = MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING; - flat_expression->field_model(flat_field_name, field_model, insertable, updatable, is_primary_key); - field_model.model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; - } - } + field_model_generate_flat_string(db_field_name, flat_field_name, field_model, insertable, updatable); } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL ) { - Clearer * clearer = model_connector->get_clearer(); - - if( clearer ) - { - clearer->clear_model(field_model); - } + field_model_generate_db_sql(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key); } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) { - FlatConnector * doc_connector = model_connector->get_doc_connector(); // different connector will be here (doc, flat) - - if( doc_connector ) - { - FlatExpression * doc_expression = doc_connector->get_expression(); - - // potrzebujemy dwa rozne doce, jeden dla flat drugi dla bazy danych - if( doc_expression && !is_empty_field(flat_field_name) ) - { - // insertable, updatable and is_primary_key are ignored here - doc_expression->field_doc(*this, flat_field_name, field_model, insertable, updatable, is_primary_key); - } - } + field_model_clear_values(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key); } - field_model.model_data = nullptr; + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) + { + field_model_generate_doc_flat(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key); + } + + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_READING_VALUE_FROM_DB_RESULTSET ) + { + field_model_read_values_from_queryresult(db_field_name, flat_field_name, field_model, insertable, updatable, has_foreign_key); + } + + field_model.model_env = nullptr; } } - template - void field_list(const wchar_t * db_field_name, const wchar_t * flat_field_name, ModelContainer & field_container, bool insertable, bool updatable, bool is_primary_key) + template + void field_list(const wchar_t * db_field_name, const wchar_t * flat_field_name, ModelContainer & field_container, ModelContainerType * model_container_type, bool insertable, bool updatable) { - // IMPLEMENTME what about db? - if( model_connector ) + if( model_connector && model_env ) { - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_FLAT_STRING ) { FlatConnector * flat_connector = model_connector->get_flat_connector(); @@ -781,12 +928,30 @@ protected: // IMPROVE ME // what about model_data and save_mode? // may it should be placed inside some structure? - flat_expression->field_list(flat_field_name, field_container, insertable, updatable, is_primary_key, model_connector, model_connector_mode, model_data); + flat_expression->field_list(flat_field_name, field_container, insertable, updatable, false, model_connector, model_env); } } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DB_SQL ) + { + 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) && db_expression->get_output_type() != MORM_OUTPUT_TYPE_JOIN_TABLES ) + { + // another select will be used (from another Finder) + // we need only columns name + // but columns are defined in the other model + } + } + } + + + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_CLEARING_VALUE ) { Clearer * clearer = model_connector->get_clearer(); @@ -796,7 +961,7 @@ protected: } } - if( model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) + if( model_env->model_connector_mode == MORM_MODEL_CONNECTOR_MODE_GENERATING_DOC_FOR_FLAT ) { FlatConnector * doc_connector = model_connector->get_doc_connector(); // different connector will be here (doc, flat) @@ -808,7 +973,7 @@ protected: if( doc_expression && !is_empty_field(flat_field_name) ) { // insertable, updatable and is_primary_key are ignored here - doc_expression->field_doc(*this, flat_field_name, field_container, insertable, updatable, is_primary_key, model_data); + doc_expression->field_doc(*this, flat_field_name, field_container, insertable, updatable, false, model_env); } } } @@ -816,12 +981,32 @@ protected: } + template + void get_value_by_field_index(int field_index, FieldValue & field_value) + { + if( model_env->cursor_helper && model_env->cursor_helper->query_result ) + { + const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(field_index); + + if( val_str ) + { + DbConnector * db_connector = model_connector->get_db_connector(); + + if( db_connector ) + { + db_connector->get_value(val_str, field_value); + } + } + } + } + + template void get_value_by_field_name(const wchar_t * field_name, FieldValue & field_value) { - if( query_result ) + if( model_env->cursor_helper && model_env->cursor_helper->query_result ) { - const char * val_str = query_result->get_field_string_value(field_name); + const char * val_str = model_env->cursor_helper->query_result->get_field_string_value(field_name); if( val_str ) { @@ -840,7 +1025,7 @@ protected: template void doc_field_generic(const wchar_t * db_field_name, const wchar_t * flat_field_name, FieldValue & field_value) { - if( reinterpret_cast(&field_value) == doc_field_pointer && model_connector ) + if( reinterpret_cast(&field_value) == model_env->doc_field_pointer && model_connector ) { FlatConnector * doc_connector = model_connector->get_doc_connector(); @@ -864,7 +1049,7 @@ protected: void doc_field_model(const wchar_t * db_field_name, const wchar_t * flat_field_name, Model & field_model) { - if( reinterpret_cast(&field_model) == doc_field_pointer && model_connector ) + if( reinterpret_cast(&field_model) == model_env->doc_field_pointer && model_connector ) { FlatConnector * doc_connector = model_connector->get_doc_connector(); @@ -889,7 +1074,7 @@ protected: template void doc_field_list(const wchar_t * db_field_name, const wchar_t * flat_field_name, ModelContainer & field_container) { - if( reinterpret_cast(&field_container) == doc_field_pointer && model_connector ) + if( reinterpret_cast(&field_container) == model_env->doc_field_pointer && model_connector ) { FlatConnector * doc_connector = model_connector->get_doc_connector(); @@ -976,8 +1161,7 @@ protected: virtual bool is_empty_field(const wchar_t * value); - -// template friend class Finder; + template friend class Finder; template friend class Cursor; friend class BaseExpression; diff --git a/src/modeldata.h b/src/modeldata.h index 327cddb..472bf69 100644 --- a/src/modeldata.h +++ b/src/modeldata.h @@ -36,6 +36,7 @@ #define headerfile_morm_modeldata + namespace morm { @@ -43,19 +44,14 @@ class ModelData { public: - ModelData() {} - virtual ~ModelData() {} + ModelData() + { + } + + virtual ~ModelData() + { + } - /* - * may to add: - * std::set skip_columns; - * and Finder can use this - * also update and insert from Model - * (but what about the same name of columns through the whole objects-tree? - * may we can save the name of the table too?) - * - * - */ }; } diff --git a/src/modelenv.h b/src/modelenv.h new file mode 100644 index 0000000..776d439 --- /dev/null +++ b/src/modelenv.h @@ -0,0 +1,115 @@ +/* + * 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_modelenv +#define headerfile_morm_modelenv + +#include "modeldata.h" +#include "cursorhelper.h" +#include "finderhelper.h" +#include "morm_types.h" + + +namespace morm +{ + +class ModelEnv +{ +public: + + ModelData * model_data; + FinderHelper * finder_helper; + CursorHelper * cursor_helper; + + int model_connector_mode; + const void * doc_field_pointer; + + PT::TextStream table_name; + PT::TextStream table_name_short; + int table_index; + + + + ModelEnv() + { + clear(); + } + + + ~ModelEnv() + { + } + + + ModelEnv(const ModelEnv & e) + { + model_data = e.model_data; + finder_helper = e.finder_helper; + cursor_helper = e.cursor_helper; + model_connector_mode = e.model_connector_mode; + table_index = e.table_index; + doc_field_pointer = e.doc_field_pointer; + } + + + void copy_global_objects(const ModelEnv & e) + { + model_data = e.model_data; + finder_helper = e.finder_helper; + cursor_helper = e.cursor_helper; + + model_connector_mode = e.model_connector_mode; + + // what about doc_field_pointer? + } + + void clear() + { + model_data = nullptr; + finder_helper = nullptr; + cursor_helper = nullptr; + model_connector_mode = MORM_MODEL_CONNECTOR_MODE_NONE; + doc_field_pointer = nullptr; + table_name.clear(); + table_name_short.clear(); + table_index = 0; + } + + +}; + +} + + +#endif diff --git a/src/morm_types.h b/src/morm_types.h index 379412e..d1a2c61 100644 --- a/src/morm_types.h +++ b/src/morm_types.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,6 +53,7 @@ #define MORM_WORK_MODE_MODEL_VALUES 2 #define MORM_WORK_MODE_MODEL_FIELDS_VALUES 3 +#define MORM_WORK_MODE_MODEL_SAVE_FIELDS 4 // IMPROVE ME give me a better name @@ -68,12 +69,15 @@ //#define MORM_OUTPUT_TYPE_WHERE_CUSTOM 5 #define MORM_OUTPUT_TYPE_SELECT_COLUMNS 6 -#define MORM_OUTPUT_TYPE_WHERE_EQ 7 -#define MORM_OUTPUT_TYPE_WHERE_LT 8 -#define MORM_OUTPUT_TYPE_WHERE_GT 9 -#define MORM_OUTPUT_TYPE_WHERE_LE 10 -#define MORM_OUTPUT_TYPE_WHERE_GE 11 -#define MORM_OUTPUT_TYPE_WHERE_IN 12 +#define MORM_OUTPUT_TYPE_JOIN_TABLES 7 + +#define MORM_OUTPUT_TYPE_WHERE_EQ 10 +#define MORM_OUTPUT_TYPE_WHERE_LT 11 +#define MORM_OUTPUT_TYPE_WHERE_GT 12 +#define MORM_OUTPUT_TYPE_WHERE_LE 13 +#define MORM_OUTPUT_TYPE_WHERE_GE 14 +#define MORM_OUTPUT_TYPE_WHERE_IN 15 +#define MORM_OUTPUT_TYPE_WHERE_NOT_EQ 16 #define MORM_CONJUNCTION_AND 1 diff --git a/src/postgresqlconnector.cpp b/src/postgresqlconnector.cpp index 39272f4..bad375d 100644 --- a/src/postgresqlconnector.cpp +++ b/src/postgresqlconnector.cpp @@ -47,7 +47,6 @@ namespace morm PostgreSQLConnector::PostgreSQLConnector() { pg_conn = nullptr; - log_queries = false; } @@ -77,10 +76,6 @@ void PostgreSQLConnector::allocate_default_expression() -void PostgreSQLConnector::set_log_queries(bool log_queries) -{ - this->log_queries = log_queries; -} QueryResult * PostgreSQLConnector::create_query_result() @@ -97,7 +92,7 @@ bool PostgreSQLConnector::do_query(const char * query_str, PostgreSQLQueryResult if( log_queries && log ) { - (*log) << PT::Log::log1 << "Morm: query: " << query_str << PT::Log::logend; + (*log) << PT::Log::log3 << "Morm: query: " << query_str << PT::Log::logend; } psql_result->psql_result = PQexec(pg_conn, query_str); @@ -115,6 +110,7 @@ bool PostgreSQLConnector::do_query(const char * query_str, PostgreSQLQueryResult { psql_result->psql_status = PQresultStatus(psql_result->psql_result); psql_result->result_rows = static_cast(PQntuples(psql_result->psql_result)); + psql_result->result_cols = static_cast(PQnfields(psql_result->psql_result)); } if( !psql_result->psql_result || psql_result->psql_status == PGRES_FATAL_ERROR ) diff --git a/src/postgresqlconnector.h b/src/postgresqlconnector.h index 86561d3..27e4f1a 100644 --- a/src/postgresqlconnector.h +++ b/src/postgresqlconnector.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, Tomasz Sowa + * Copyright (c) 2018-2019, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,7 +54,6 @@ public: virtual ~PostgreSQLConnector(); - virtual void set_log_queries(bool log_queries); bool query(const PT::TextStream & stream, QueryResult & query_result); bool query(const char * query_str, QueryResult & query_result); @@ -86,10 +85,8 @@ public: protected: PGconn * pg_conn; - bool log_queries; PT::TextStream stream; std::string query_str; - std::string temp_column_name; std::wstring db_database; std::wstring db_user; diff --git a/src/postgresqlqueryresult.cpp b/src/postgresqlqueryresult.cpp index bcb45d9..a28f240 100644 --- a/src/postgresqlqueryresult.cpp +++ b/src/postgresqlqueryresult.cpp @@ -67,6 +67,23 @@ bool PostgreSQLQueryResult::has_db_result() } +const char * PostgreSQLQueryResult::get_field_string_value(int column_index) +{ + const char * value_str = nullptr; + + if( column_index >= 0 && (size_t)column_index < result_cols ) + { + if( cur_row < result_rows ) + { + value_str = PQgetvalue(psql_result, cur_row, column_index); + } + } + + return value_str; +} + + + const char * PostgreSQLQueryResult::get_field_string_value(const char * column_name) { const char * value_str = nullptr; diff --git a/src/postgresqlqueryresult.h b/src/postgresqlqueryresult.h index 74c4dd5..3efa8f3 100644 --- a/src/postgresqlqueryresult.h +++ b/src/postgresqlqueryresult.h @@ -57,6 +57,7 @@ struct PostgreSQLQueryResult : public QueryResult virtual bool has_db_result(); + const char * get_field_string_value(int column_index); const char * get_field_string_value(const char * column_name); int get_column_index(const char * column_name); diff --git a/src/queryresult.cpp b/src/queryresult.cpp index b5bd092..2e3c16b 100644 --- a/src/queryresult.cpp +++ b/src/queryresult.cpp @@ -56,6 +56,7 @@ QueryResult::~QueryResult() void QueryResult::clear() { result_rows = 0; + result_cols = 0; cur_row = 0; status = false; error_msg.clear(); @@ -70,6 +71,12 @@ bool QueryResult::has_db_result() } +const char * QueryResult::get_field_string_value(int column_index) +{ + return nullptr; +} + + const char * QueryResult::get_field_string_value(const char * column_name) { return nullptr; diff --git a/src/queryresult.h b/src/queryresult.h index 5caef0e..9ca13bb 100644 --- a/src/queryresult.h +++ b/src/queryresult.h @@ -45,6 +45,7 @@ struct QueryResult { bool status; size_t result_rows; // how many rows in the result query + size_t result_cols; // how many columns in the result query size_t cur_row; // used for reading std::wstring error_msg; @@ -57,6 +58,7 @@ struct QueryResult virtual void clear(); virtual bool has_db_result(); + virtual const char * get_field_string_value(int column_index); virtual const char * get_field_string_value(const char * column_name); virtual const char * get_field_string_value(const wchar_t * field_name);