add possibility of calculating how many rows there were before LIMIT was applied

The Finder has get_rows_counter() method which returns how many rows there were
before LIMIT clause was applied. The select(...) method should be called with
Select::with_rows_counter flag in such a case.

while here:
- change the semantic of Finder, now the select(...) method takes a morm::Select flags,
  and we have such flags:
  - Select::no_auto_generated_columns - do not generate columns from models
  - with_rows_counter - add an additional column for the rows counter
- remove Finder::prepare_to_select() - now use select(...) with no_auto_generated_columns flag
This commit is contained in:
Tomasz Sowa 2022-07-11 17:48:13 +02:00
parent 4e8f3af8fc
commit 43dfbd5d5a
16 changed files with 512 additions and 179 deletions

View File

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

View File

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

View File

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

View File

@ -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<typename FieldValue>
void put_field_value(const FieldValue & field_value, const FT & field_type)

View File

@ -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<ModelClass> get_vector()
{
std::vector<ModelClass> 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();

View File

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

View File

@ -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<QueryResult> query_result_ptr(create_query_result());

View File

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

View File

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

View File

@ -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<typename ModelClass>
class Finder
{
public:
@ -113,10 +113,27 @@ public:
model_data = nullptr;
}
Finder<ModelClass> & set_connector(ModelConnector * model_connector)
{
this->model_connector = model_connector;
set_out_stream();
return *this;
}
Finder<ModelClass> & set_connector(ModelConnector & model_connector)
{
this->model_connector = &model_connector;
set_out_stream();
return *this;
}
Finder<ModelClass> & set_stream(pt::TextStream * out_stream)
{
this->out_stream = out_stream;
return *this;
}
Finder<ModelClass> & set_out_stream(pt::TextStream * out_stream)
Finder<ModelClass> & set_stream(pt::TextStream & out_stream)
{
this->out_stream = out_stream;
return *this;
@ -147,143 +164,19 @@ public:
}
Finder<ModelClass> & prepare_to_select()
Finder<ModelClass> & 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<ModelClass> & select(ModelConnector & model_connector)
{
this->model_connector = &model_connector;
set_out_stream();
return select();
}
Finder<ModelClass> & select(ModelConnector * model_connector)
{
this->model_connector = model_connector;
set_out_stream();
return select();
}
Finder<ModelClass> & select(pt::TextStream & out_stream, ModelConnector & model_connector)
{
this->out_stream = &out_stream;
this->model_connector = &model_connector;
return select();
}
Finder<ModelClass> & select(pt::TextStream & out_stream, ModelConnector * model_connector)
{
this->out_stream = &out_stream;
this->model_connector = model_connector;
return select();
}
Finder<ModelClass> & 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<ModelClass> & select(ModelData * model_data, bool call_prepare = true)
{
if( call_prepare )
{
prepare_to_select();
}
this->model_data = model_data;
return select(false);
}
Finder<ModelClass> & 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<ModelClass> 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<ModelClass> 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<ModelClass> & result, bool clear_list = true)
{
Cursor<ModelClass> 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<ModelClass> & result, bool clear_vector = true)
{
Cursor<ModelClass> 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<typename Container>
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

View File

@ -100,6 +100,18 @@ public:
return *this;
}
FT & operator=(FieldType type)
{
this->type = static_cast<int>(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;

View File

@ -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()
{

View File

@ -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<typename ModelClass> friend class Finder;
template<typename ModelClass> friend class Cursor;
friend class BaseExpression;
friend class DbExpression;
friend class DbConnector;
friend class FlatConnector;

View File

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

View File

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

124
src/select.h Normal file
View File

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