diff --git a/samples/Makefile.dep b/samples/Makefile.dep index 7f40c49..ff14ffb 100644 --- a/samples/Makefile.dep +++ b/samples/Makefile.dep @@ -22,9 +22,9 @@ ./main.o: ../src/cursorhelper.h ../src/finderhelper.h ./main.o: ../src/fieldvaluehelper.h ../src/wrapper.h ../src/spacewrapper.h ./main.o: ../src/baseobjectwrapper.h ../src/modelcontainerwrapper.h -./main.o: ../../pikotools/src/convert/text.h ../src/flatexpression.h -./main.o: ../src/finder.h ../src/cursor.h ../src/jsonexpression.h -./main.o: ../src/postgresqlexpression.h ../src/jsonconnector.h -./main.o: ../src/postgresqlconnector.h ../src/postgresqlqueryresult.h -./main.o: ../src/transaction.h person.h language.h attachment.h type.h -./main.o: attachment2.h +./main.o: ../src/select.h ../../pikotools/src/convert/text.h +./main.o: ../src/flatexpression.h ../src/finder.h ../src/cursor.h +./main.o: ../src/jsonexpression.h ../src/postgresqlexpression.h +./main.o: ../src/jsonconnector.h ../src/postgresqlconnector.h +./main.o: ../src/postgresqlqueryresult.h ../src/transaction.h person.h +./main.o: language.h attachment.h type.h attachment2.h diff --git a/src/Makefile.dep b/src/Makefile.dep index fb7002a..8c1ebc3 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -18,7 +18,7 @@ ./baseexpression.o: ../../pikotools/src/log/log.h ./baseexpression.o: ../../pikotools/src/log/filelog.h finderhelper.h ./baseexpression.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./baseexpression.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h +./baseexpression.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h ./baseexpression.o: ../../pikotools/src/convert/text.h model.h ./baseexpression.o: modelconnector.h clearer.h dbconnector.h flatconnector.h ./baseexpression.o: dbexpression.h flatexpression.h @@ -40,8 +40,8 @@ ./clearer.o: dbexpression.h baseexpression.h morm_types.h modelenv.h ./clearer.o: modeldata.h cursorhelper.h finderhelper.h fieldvaluehelper.h ./clearer.o: wrapper.h spacewrapper.h baseobjectwrapper.h -./clearer.o: modelcontainerwrapper.h ../../pikotools/src/convert/text.h -./clearer.o: flatexpression.h +./clearer.o: modelcontainerwrapper.h select.h +./clearer.o: ../../pikotools/src/convert/text.h flatexpression.h ./dbconnector.o: ../../pikotools/src/space/spaceparser.h ./dbconnector.o: ../../pikotools/src/space/space.h ./dbconnector.o: ../../pikotools/src/textstream/types.h @@ -63,9 +63,10 @@ ./dbconnector.o: dbexpression.h baseexpression.h morm_types.h modelenv.h ./dbconnector.o: modeldata.h cursorhelper.h finderhelper.h fieldvaluehelper.h ./dbconnector.o: wrapper.h spacewrapper.h baseobjectwrapper.h -./dbconnector.o: modelcontainerwrapper.h ../../pikotools/src/convert/text.h -./dbconnector.o: model.h modelconnector.h clearer.h flatconnector.h -./dbconnector.o: flatexpression.h ../../pikotools/src/convert/convert.h +./dbconnector.o: modelcontainerwrapper.h select.h +./dbconnector.o: ../../pikotools/src/convert/text.h model.h modelconnector.h +./dbconnector.o: clearer.h flatconnector.h flatexpression.h +./dbconnector.o: ../../pikotools/src/convert/convert.h ./dbconnector.o: ../../pikotools/src/convert/inttostr.h ./dbconnector.o: ../../pikotools/src/convert/patternreplacer.h ./dbconnector.o: ../../pikotools/src/convert/strtoint.h @@ -90,8 +91,9 @@ ./dbexpression.o: ../../pikotools/src/log/log.h ./dbexpression.o: ../../pikotools/src/log/filelog.h finderhelper.h ./dbexpression.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./dbexpression.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h -./dbexpression.o: ../../pikotools/src/convert/text.h +./dbexpression.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h +./dbexpression.o: ../../pikotools/src/convert/text.h model.h modelconnector.h +./dbexpression.o: clearer.h dbconnector.h flatconnector.h flatexpression.h ./flatconnector.o: flatconnector.h ./flatconnector.o: ../../pikotools/src/textstream/textstream.h ./flatconnector.o: ../../pikotools/src/textstream/stream.h @@ -110,7 +112,7 @@ ./flatconnector.o: cursorhelper.h queryresult.h ../../pikotools/src/log/log.h ./flatconnector.o: ../../pikotools/src/log/filelog.h finderhelper.h ./flatconnector.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./flatconnector.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h +./flatconnector.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h ./flatconnector.o: ../../pikotools/src/convert/text.h model.h ./flatconnector.o: modelconnector.h clearer.h dbconnector.h dbexpression.h ./flatexpression.o: flatexpression.h baseexpression.h @@ -131,7 +133,7 @@ ./flatexpression.o: ../../pikotools/src/log/log.h ./flatexpression.o: ../../pikotools/src/log/filelog.h finderhelper.h ./flatexpression.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./flatexpression.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h +./flatexpression.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h ./flatexpression.o: ../../pikotools/src/convert/text.h ./jsonconnector.o: jsonconnector.h flatconnector.h ./jsonconnector.o: ../../pikotools/src/textstream/textstream.h @@ -152,7 +154,7 @@ ./jsonconnector.o: ../../pikotools/src/log/log.h ./jsonconnector.o: ../../pikotools/src/log/filelog.h finderhelper.h ./jsonconnector.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./jsonconnector.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h +./jsonconnector.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h ./jsonconnector.o: ../../pikotools/src/convert/text.h ./jsonexpression.o: jsonexpression.h flatexpression.h baseexpression.h ./jsonexpression.o: ../../pikotools/src/textstream/textstream.h @@ -172,7 +174,7 @@ ./jsonexpression.o: ../../pikotools/src/log/log.h ./jsonexpression.o: ../../pikotools/src/log/filelog.h finderhelper.h ./jsonexpression.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./jsonexpression.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h +./jsonexpression.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ft.h ./jsonexpression.o: ../../pikotools/src/convert/text.h ./jsonexpression.o: ../../pikotools/src/convert/misc.h ./jsonexpression.o: ../../pikotools/src/convert/text.h @@ -194,8 +196,8 @@ ./model.o: dbexpression.h baseexpression.h morm_types.h modelenv.h ./model.o: modeldata.h cursorhelper.h finderhelper.h fieldvaluehelper.h ./model.o: wrapper.h spacewrapper.h baseobjectwrapper.h -./model.o: modelcontainerwrapper.h ../../pikotools/src/convert/text.h -./model.o: flatexpression.h +./model.o: modelcontainerwrapper.h select.h +./model.o: ../../pikotools/src/convert/text.h flatexpression.h ./modelconnector.o: modelconnector.h clearer.h ./modelconnector.o: ../../pikotools/src/date/date.h ./modelconnector.o: ../../pikotools/src/convert/inttostr.h @@ -233,7 +235,7 @@ ./postgresqlconnector.o: dbexpression.h baseexpression.h morm_types.h ./postgresqlconnector.o: modelenv.h modeldata.h cursorhelper.h finderhelper.h ./postgresqlconnector.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./postgresqlconnector.o: baseobjectwrapper.h modelcontainerwrapper.h +./postgresqlconnector.o: baseobjectwrapper.h modelcontainerwrapper.h select.h ./postgresqlconnector.o: ../../pikotools/src/convert/text.h ./postgresqlconnector.o: ../../pikotools/src/convert/strtoint.h ./postgresqlconnector.o: ../../pikotools/src/convert/text.h @@ -257,8 +259,8 @@ ./postgresqlexpression.o: ../../pikotools/src/log/log.h ./postgresqlexpression.o: ../../pikotools/src/log/filelog.h finderhelper.h ./postgresqlexpression.o: fieldvaluehelper.h wrapper.h spacewrapper.h -./postgresqlexpression.o: baseobjectwrapper.h modelcontainerwrapper.h ft.h -./postgresqlexpression.o: ../../pikotools/src/convert/text.h +./postgresqlexpression.o: baseobjectwrapper.h modelcontainerwrapper.h +./postgresqlexpression.o: select.h ft.h ../../pikotools/src/convert/text.h ./postgresqlqueryresult.o: postgresqlqueryresult.h queryresult.h ./postgresqlqueryresult.o: ../../pikotools/src/log/log.h ./postgresqlqueryresult.o: ../../pikotools/src/textstream/textstream.h diff --git a/src/baseexpression.cpp b/src/baseexpression.cpp index 8e90cec..f60ee54 100644 --- a/src/baseexpression.cpp +++ b/src/baseexpression.cpp @@ -126,6 +126,7 @@ void BaseExpression::generate_from_model(Model & model) before_generate_from_model(); dump_additional_info(model); model.fields(); + add_additional_columns(model); after_generate_from_model(); } } @@ -141,6 +142,11 @@ void BaseExpression::dump_additional_info(Model & model) } +void BaseExpression::add_additional_columns(Model & model) +{ +} + + void BaseExpression::before_generate_from_model() { is_first_field = true; diff --git a/src/baseexpression.h b/src/baseexpression.h index 51cc8fc..41e69cd 100644 --- a/src/baseexpression.h +++ b/src/baseexpression.h @@ -334,6 +334,7 @@ protected: virtual void save_foreign_key(const wchar_t * field_name, const FT & field_type, ModelEnv * model_env); virtual void dump_additional_info(Model & model); + virtual void add_additional_columns(Model & model); template void put_field_value(const FieldValue & field_value, const FT & field_type) diff --git a/src/cursor.h b/src/cursor.h index 69b4780..688eb31 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018-2021, Tomasz Sowa + * Copyright (c) 2018-2022, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +64,8 @@ public: use_table_prefix_for_fetching = c.use_table_prefix_for_fetching; query_result = c.query_result; select_status = c.select_status; + select_flags = c.select_flags; + rows_counter = c.rows_counter; if( query_result ) { @@ -106,6 +108,8 @@ public: finder_helper.clear(); query_result = nullptr; select_status = false; + select_flags = Select::default_type; + rows_counter = 0; } @@ -145,6 +149,18 @@ public: } + virtual void set_select_flags(const Select & select_flags) + { + this->select_flags = select_flags; + } + + + virtual void set_rows_counter_column_name(const std::wstring & column_name) + { + this->rows_counter_column_name = column_name; + } + + virtual QueryResult * get_query_result() { return query_result; @@ -164,6 +180,23 @@ public: } + virtual void prepare_model_env_for_new_object(ModelEnv & model_env) + { + model_env.cursor_helper = &cursor_helper; + model_env.finder_helper = &finder_helper; + model_env.select_flags = select_flags; + model_env.rows_counter = 0; + + if( model_env.select_flags.is_with_rows_counter() ) + { + if( cursor_helper.current_row == 0 ) + { + model_env.rows_counter_column_name = rows_counter_column_name; + } + } + } + + virtual bool get(ModelClass & result) { bool res = false; @@ -177,20 +210,21 @@ public: if( db_connector ) { - ModelEnv model_env_local; - result.model_env = &model_env_local; - result.model_env->cursor_helper = &cursor_helper; - result.model_env->finder_helper = &finder_helper; - result.model_env->model = &result; - try { + ModelEnv model_env_local; + result.model_env = &model_env_local; + result.model_env->model = &result; + result.model_env->model_data = model_data; + finder_helper.clear(); // at the moment used only for calculating table prefixes (indices) cursor_helper.clear(); cursor_helper.query_result = query_result; cursor_helper.has_autogenerated_select = has_autogenerated_select; cursor_helper.use_table_prefix_for_fetching_values = use_table_prefix_for_fetching; - result.model_env->model_data = model_data; + cursor_helper.current_row = 0; + + prepare_model_env_for_new_object(model_env_local); if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { @@ -205,6 +239,7 @@ public: if( query_result->cur_row < query_result->result_rows ) { result.map_values_from_query(); + rows_counter = model_env_local.rows_counter; if( result.found() ) { @@ -273,6 +308,7 @@ public: return get_list_generic(result, clear_list); } + virtual std::vector get_vector() { std::vector result; @@ -282,6 +318,12 @@ public: } + virtual size_t get_rows_counter() + { + return rows_counter; + } + + protected: @@ -293,6 +335,9 @@ protected: FinderHelper finder_helper; // may CursorHelper and FinderHelper should be one class? QueryResult * query_result; bool select_status; + Select select_flags; + size_t rows_counter; + std::wstring rows_counter_column_name; @@ -359,15 +404,15 @@ protected: cursor_helper.query_result = query_result; cursor_helper.has_autogenerated_select = has_autogenerated_select; cursor_helper.use_table_prefix_for_fetching_values = use_table_prefix_for_fetching; + cursor_helper.current_row = query_result->cur_row; added_model.set_connector(model_connector); added_model.clear(); + prepare_model_env_for_new_object(model_env_local); added_model.model_env = &model_env_local; - added_model.model_env->cursor_helper = &cursor_helper; - added_model.model_env->finder_helper = &finder_helper; - added_model.model_env->model_data = model_data; added_model.model_env->model = &added_model; + added_model.model_env->model_data = model_data; if( !cursor_helper.has_autogenerated_select && cursor_helper.use_table_prefix_for_fetching_values ) { @@ -377,6 +422,11 @@ protected: added_model.before_select(); added_model.map_values_from_query(); + if( model_env_local.select_flags.is_with_rows_counter() && query_result->cur_row == 0 ) + { + rows_counter = model_env_local.rows_counter; + } + if( added_model.found() ) { added_model.after_select(); diff --git a/src/cursorhelper.h b/src/cursorhelper.h index 900bc5b..c86ab5f 100644 --- a/src/cursorhelper.h +++ b/src/cursorhelper.h @@ -48,6 +48,7 @@ public: bool has_autogenerated_select; QueryResult * query_result; int current_column; + size_t current_row; // used if has_autogenerated_select is equal false // if use_table_prefix_for_fetching_values is true we find a column in such a form: table.column_name instead of just column_name @@ -71,6 +72,7 @@ public: has_autogenerated_select = false; query_result = nullptr; current_column = 0; + current_row = 0; use_table_prefix_for_fetching_values = false; } diff --git a/src/dbconnector.cpp b/src/dbconnector.cpp index c469bf4..427faaa 100644 --- a/src/dbconnector.cpp +++ b/src/dbconnector.cpp @@ -468,6 +468,7 @@ void DbConnector::generate_remove_query(pt::TextStream & stream, Model & model) } } + bool DbConnector::insert(pt::TextStream & stream, Model & model) { std::unique_ptr query_result_ptr(create_query_result()); diff --git a/src/dbexpression.cpp b/src/dbexpression.cpp index ed63b9b..19852ba 100644 --- a/src/dbexpression.cpp +++ b/src/dbexpression.cpp @@ -33,6 +33,7 @@ */ #include "dbexpression.h" +#include "model.h" namespace morm @@ -295,11 +296,58 @@ DbExpression & DbExpression::group_end(pt::TextStream & stream) DbExpression & DbExpression::page(pt::TextStream & stream, size_t page_number, size_t page_size) { - stream << " limit " << page_number << "," << page_size << " "; + stream << " LIMIT " << page_number << "," << page_size << " "; return *this; } +void DbExpression::generate_rows_counter_column_name(ModelEnv & model_env, pt::TextStream & str) +{ + str << model_env.table_name; + + if( model_env.table_index > 1 ) + { + str << model_env.table_index; + } + + str << DbExpression::COLUMN_ROWS_COUNTER_POSTFIX; +} + + +void DbExpression::add_additional_columns(Model & model) +{ + if( model.model_env ) + { + if( model.model_env->select_flags.is_with_rows_counter() ) + { + add_rows_counter_column(model); + } + } +} + + +void DbExpression::add_rows_counter_column(Model & model) +{ + if( out_stream && model.model_env ) + { + field_before(); + (*out_stream) << "COUNT(*) OVER() AS "; + + pt::TextStream str; + generate_rows_counter_column_name(*model.model_env, str); + + if( !model.model_env->has_autogenerated_select ) + { + str.to_str(model.model_env->rows_counter_column_name); + } + + before_field_name(); + esc(str, *out_stream); + after_field_name(); + + field_after(); + } +} } diff --git a/src/dbexpression.h b/src/dbexpression.h index 713fea6..0fc07ca 100644 --- a/src/dbexpression.h +++ b/src/dbexpression.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018-2021, Tomasz Sowa + * Copyright (c) 2018-2022, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,6 +50,10 @@ public: DbExpression(); virtual ~DbExpression(); + + constexpr static const char * COLUMN_ROWS_COUNTER_POSTFIX = "_autoadded_rows_counter"; + + virtual void set_output_type(int output_type); virtual int get_output_type(); @@ -68,12 +72,13 @@ public: std::wstring column_expression; // field() methods can be called recursively, so don't make it as class object column_expression = new_column_expression; - column_expression += L" as "; + column_expression += L" AS "; column_expression += new_column_name; field(column_expression.c_str(), field_value, field_type, model_env); } + virtual void generate_rows_counter_column_name(ModelEnv & model_env, pt::TextStream & str); protected: @@ -97,12 +102,12 @@ protected: void before_field_name(); void after_field_name(); - -private: + void add_additional_columns(Model & model); void before_field_value_string(const FT & field_type); void after_field_value_string(const FT & field_type); + virtual void add_rows_counter_column(Model & model); }; diff --git a/src/finder.h b/src/finder.h index a421498..9ea817e 100644 --- a/src/finder.h +++ b/src/finder.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018-2021, Tomasz Sowa + * Copyright (c) 2018-2022, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,6 +44,7 @@ #include "dbconnector.h" #include "modelconnector.h" #include "cursor.h" +#include "select.h" namespace morm @@ -52,7 +53,6 @@ namespace morm template class Finder { - public: @@ -113,10 +113,27 @@ public: model_data = nullptr; } + Finder & set_connector(ModelConnector * model_connector) + { + this->model_connector = model_connector; + set_out_stream(); + return *this; + } + Finder & set_connector(ModelConnector & model_connector) + { + this->model_connector = &model_connector; + set_out_stream(); + return *this; + } + Finder & set_stream(pt::TextStream * out_stream) + { + this->out_stream = out_stream; + return *this; + } - Finder & set_out_stream(pt::TextStream * out_stream) + Finder & set_stream(pt::TextStream & out_stream) { this->out_stream = out_stream; return *this; @@ -147,143 +164,19 @@ public: } - Finder & prepare_to_select() + Finder & select(const Select & select_flags = Select::default_type) { - was_query_error = false; - last_query_error.clear(); - has_autogenerated_select = false; - use_table_prefix_for_fetching = false; + start_new_select(select_flags); - if( model_connector ) + if( !select_flags.is_no_auto_generated_columns() ) { - if( out_stream ) - { - set_db_expression(); - out_stream->clear(); - } - else - { - was_query_error = true; - last_query_error = L"out stream object is required"; - } - } - else - { - was_query_error = true; - last_query_error = L"model connector object is required"; - } - - 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; - model.model_env->model = &model; - model.table(); - model.model_env->add_table_name_to_finder_helper(); - - return *this; - } - - - Finder & select(ModelConnector & model_connector) - { - this->model_connector = &model_connector; - set_out_stream(); - - return select(); - } - - Finder & select(ModelConnector * model_connector) - { - this->model_connector = model_connector; - set_out_stream(); - - return select(); - } - - Finder & select(pt::TextStream & out_stream, ModelConnector & model_connector) - { - this->out_stream = &out_stream; - this->model_connector = &model_connector; - - return select(); - } - - Finder & select(pt::TextStream & out_stream, ModelConnector * model_connector) - { - this->out_stream = &out_stream; - this->model_connector = model_connector; - - return select(); - } - - - Finder & select(bool call_prepare = true) - { - if( call_prepare ) - { - prepare_to_select(); - } - - has_autogenerated_select = true; - - if( model_connector && out_stream && db_expression ) - { - (*out_stream) << "SELECT "; - model.generate_select_columns(*out_stream); - (*out_stream) << " FROM "; - - if( !model.model_env->schema_name.empty() ) - { - db_expression->schema_table_to_stream(*out_stream, model.model_env->schema_name, model.model_env->table_name); - } - else - { - db_expression->table_to_stream(*out_stream, model.model_env->table_name); - } - - (*out_stream) << " AS "; - db_expression->table_to_stream(*out_stream, model.model_env->table_name); - - if( !finder_helper.join_tables_str.empty() ) - { - (*out_stream) << " "; - (*out_stream) << finder_helper.join_tables_str; - } + generate_standard_select(); } return *this; } - Finder & select(ModelData * model_data, bool call_prepare = true) - { - if( call_prepare ) - { - prepare_to_select(); - } - - this->model_data = model_data; - return select(false); - } - - - Finder & select(ModelData & model_data, bool call_prepare = true) - { - if( call_prepare ) - { - prepare_to_select(); - } - - this->model_data = &model_data; - return select(false); - } - - /* * used if we are not using auto generated selects */ @@ -763,8 +656,19 @@ public: { Cursor cursor; cursor.set_model_data(model_data); - cursor.set_has_autogenerated_select(has_autogenerated_select); + cursor.set_has_autogenerated_select(model_env.has_autogenerated_select); cursor.use_table_prefix(use_table_prefix_for_fetching); + cursor.set_select_flags(model_env.select_flags); + + if( model_env.select_flags.is_with_rows_counter() ) + { + if( model_env.rows_counter_column_name.empty() ) + { + generate_rows_counter_column_name(model_env); + } + + cursor.set_rows_counter_column_name(model_env.rows_counter_column_name); + } if( model_connector && out_stream ) { @@ -789,7 +693,9 @@ public: bool get(ModelClass & result) { Cursor cursor = get_cursor(); - return cursor.get(result); + bool status = cursor.get(result); + model_env.rows_counter = cursor.get_rows_counter(); + return status; } @@ -805,7 +711,9 @@ public: bool get_list(std::list & result, bool clear_list = true) { Cursor cursor = get_cursor(); - return cursor.get_list(result, clear_list); + bool status = cursor.get_list(result, clear_list); + model_env.rows_counter = cursor.get_rows_counter(); + return status; } @@ -821,7 +729,9 @@ public: bool get_vector(std::vector & result, bool clear_vector = true) { Cursor cursor = get_cursor(); - return cursor.get_vector(result, clear_vector); + bool status = cursor.get_vector(result, clear_vector); + model_env.rows_counter = cursor.get_rows_counter(); + return status; } @@ -834,9 +744,89 @@ public: } + size_t get_rows_counter() + { + return model_env.rows_counter; + } + + protected: - void set_out_stream() + + + virtual void start_new_select(const Select & select_flags = Select::default_type) + { + was_query_error = false; + last_query_error.clear(); + use_table_prefix_for_fetching = false; + + if( model_connector ) + { + if( out_stream ) + { + set_db_expression(); + out_stream->clear(); + } + else + { + was_query_error = true; + last_query_error = L"out stream object is required"; + } + } + else + { + was_query_error = true; + last_query_error = L"model connector object is required"; + } + + model.set_connector(model_connector); + + finder_helper.clear(); + model_env.clear(); + model_env.select_flags = select_flags; + model_env.has_autogenerated_select = false; + model_env.model_data = model_data; + model_env.finder_helper = &finder_helper; + model_env.model = &model; + + model.model_env = &model_env; + model.table(); + model.model_env->add_table_name_to_finder_helper(); + } + + + virtual void generate_standard_select() + { + if( model_connector && out_stream && db_expression && model.model_env ) + { + model.model_env->has_autogenerated_select = true; + + (*out_stream) << "SELECT "; + model.generate_select_columns(*out_stream); + (*out_stream) << " FROM "; + + if( !model.model_env->schema_name.empty() ) + { + db_expression->schema_table_to_stream(*out_stream, model.model_env->schema_name, model.model_env->table_name); + } + else + { + db_expression->table_to_stream(*out_stream, model.model_env->table_name); + } + + (*out_stream) << " AS "; + db_expression->table_to_stream(*out_stream, model.model_env->table_name); + + if( !finder_helper.join_tables_str.empty() ) + { + (*out_stream) << " "; + (*out_stream) << finder_helper.join_tables_str; + } + } + } + + + virtual void set_out_stream() { if( model_connector ) { @@ -849,6 +839,47 @@ protected: } + virtual void generate_rows_counter_column_name(ModelEnv & model_env, pt::TextStream & str) + { + if( model_env.has_autogenerated_select ) + { + /* + * if the column name is empty put a short string which will not allocate dynamic memory + * (special optimisation in std::string - a short static buffer) + * + * we need a non empty string for field(...) method to work correctly + * (if the column name was empty then the field(...) would skip the column) + * (we need the field(...) to correctly advance cursor_helper->current_column) + */ + str << L"morm"; + } + else + { + if( model_connector ) + { + DbConnector * db_connector = model_connector->get_db_connector(); + + if( db_connector ) + { + DbExpression * db_expression = db_connector->get_expression(); + + if( db_expression ) + { + db_expression->generate_rows_counter_column_name(model_env, str); + } + } + } + } + } + + + virtual void generate_rows_counter_column_name(ModelEnv & model_env) + { + pt::TextStream str; + generate_rows_counter_column_name(model_env, str); + str.to_str(model_env.rows_counter_column_name); + } + private: @@ -863,10 +894,10 @@ private: ModelEnv model_env; FinderHelper finder_helper; ModelData * model_data; - bool has_autogenerated_select; bool use_table_prefix_for_fetching; + void set_db_expression() { if( model_connector ) @@ -896,6 +927,7 @@ private: } } + template void field_in(const wchar_t * table_name, int table_index, const wchar_t * field_name, const Container & container) { @@ -911,7 +943,6 @@ private: } } - }; } // namespace diff --git a/src/ft.h b/src/ft.h index 6249009..5c3c98b 100644 --- a/src/ft.h +++ b/src/ft.h @@ -100,6 +100,18 @@ public: return *this; } + FT & operator=(FieldType type) + { + this->type = static_cast(type); + return *this; + } + + FT & operator=(int type) + { + this->type = type; + return *this; + } + bool is_flag_set(int flag_mask) const { return (type & flag_mask) != 0; diff --git a/src/model.cpp b/src/model.cpp index 956a77f..1356452 100644 --- a/src/model.cpp +++ b/src/model.cpp @@ -934,6 +934,7 @@ void Model::map_values_from_query() model_env->was_primary_key_read = false; // whether or not there was at least one column with primary_key flag model_env->has_primary_key_set = true; // whether all primary_columns were different than null fields(); + map_additional_columns_from_query(); model_env->model_work_mode = MORM_MODEL_WORK_MODE_NONE; if( model_env->was_primary_key_read && model_env->has_primary_key_set ) @@ -950,6 +951,40 @@ void Model::map_values_from_query() } +void Model::map_additional_columns_from_query() +{ + if( model_env ) + { + if( model_env->select_flags.is_with_rows_counter() ) + { + map_rows_counter_from_query(); + } + } +} + + +void Model::map_rows_counter_from_query() +{ + if( model_env && model_env->cursor_helper ) + { + /* + * take the value only from the first row, the value should be the same on every row + */ + if( model_env->cursor_helper->current_row == 0 ) + { + field(model_env->rows_counter_column_name.c_str(), L"", model_env->rows_counter); + } + else + { + if( model_env->cursor_helper && model_env->cursor_helper->has_autogenerated_select ) + { + model_env->cursor_helper->current_column += 1; + } + } + } +} + + void Model::clear() { diff --git a/src/model.h b/src/model.h index d80900c..111efa4 100644 --- a/src/model.h +++ b/src/model.h @@ -350,6 +350,8 @@ protected: virtual void save_tree(bool save_whole_tree); virtual void map_values_from_query(); + virtual void map_additional_columns_from_query(); + virtual void map_rows_counter_from_query(); virtual bool db_query(const char * raw_sql); virtual bool db_query(const std::string & raw_sql); @@ -1642,6 +1644,7 @@ protected: template friend class Finder; template friend class Cursor; friend class BaseExpression; + friend class DbExpression; friend class DbConnector; friend class FlatConnector; diff --git a/src/modelenv.h b/src/modelenv.h index c5a4e34..4139c0d 100644 --- a/src/modelenv.h +++ b/src/modelenv.h @@ -41,6 +41,7 @@ #include "fieldvaluehelper.h" #include "morm_types.h" #include "wrapper.h" +#include "select.h" #ifdef MORM_HAS_EZC_LIBRARY @@ -106,6 +107,11 @@ public: #endif bool status; + bool has_autogenerated_select; + Select select_flags; + size_t rows_counter; + std::wstring rows_counter_column_name; + ModelEnv() @@ -142,6 +148,10 @@ public: was_field_found = e.was_field_found; wrapper = e.wrapper; status = e.status; + has_autogenerated_select = e.has_autogenerated_select; + select_flags = e.select_flags; + rows_counter = e.rows_counter; + rows_counter_column_name = e.rows_counter_column_name; #ifdef MORM_HAS_EZC_LIBRARY ezc_fun_info = e.ezc_fun_info; @@ -194,6 +204,10 @@ public: was_field_found = false; wrapper.clear(); status = true; + has_autogenerated_select = false; + select_flags = Select::default_type; + rows_counter = 0; + rows_counter_column_name.clear(); #ifdef MORM_HAS_EZC_LIBRARY ezc_fun_info = nullptr; @@ -215,7 +229,6 @@ public: } } - }; } diff --git a/src/postgresqlexpression.cpp b/src/postgresqlexpression.cpp index bfe9a98..96cb585 100644 --- a/src/postgresqlexpression.cpp +++ b/src/postgresqlexpression.cpp @@ -100,7 +100,7 @@ void PostgreSQLExpression::esc(const pt::Date & date, pt::TextStream & stream, c DbExpression & PostgreSQLExpression::page(pt::TextStream & stream, size_t page_number, size_t page_size) { - stream << " offset " << page_number << " limit " << page_size << " "; + stream << " OFFSET " << page_number << " LIMIT " << page_size << " "; return *this; } diff --git a/src/select.h b/src/select.h new file mode 100644 index 0000000..ec8258d --- /dev/null +++ b/src/select.h @@ -0,0 +1,124 @@ +/* + * This file is a part of morm + * and is distributed under the 2-Clause BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2022, 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_src_select +#define headerfile_morm_src_select + +namespace morm +{ + +/* + * additional arguments for Finder::select method + */ +class Select +{ +public: + + enum SelectType + { + default_type = 0, + no_auto_generated_columns = 1, + with_rows_counter = 2, + }; + + + /* + * type can be a superposition from SelectType values + */ + int type; + + + + Select() + { + type = 0; + } + + Select(const Select & select_type) + { + this->type = select_type.type; + } + + Select(SelectType select_type) + { + this->type = static_cast(select_type); + } + + Select(int type) + { + this->type = type; + } + + Select & operator=(const Select & select_type) + { + type = select_type.type; + return *this; + } + + Select & operator=(SelectType select_type) + { + this->type = static_cast(select_type); + return *this; + } + + Select & operator=(int type) + { + this->type = type; + return *this; + } + + + bool is_flag_set(int flag_mask) const + { + return (type & flag_mask) != 0; + } + + + bool is_with_rows_counter() const + { + return is_flag_set(with_rows_counter); + } + + + bool is_no_auto_generated_columns() const + { + return is_flag_set(no_auto_generated_columns); + } + + +}; + +} + +#endif