From ffb7ac85a630d343a6e9cc95683a710dae2beda8 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Wed, 18 Apr 2018 10:22:01 +0000 Subject: [PATCH] added: QueryResult stack to PostgreSQLConnector this allowes us to call query() recursively (from after_select() callback) git-svn-id: svn://ttmath.org/publicrep/morm/trunk@1085 e52654a7-88a9-db11-a3e9-0013d4bc506e --- src/dbconnector.cpp | 9 ++ src/dbconnector.h | 4 + src/finder.h | 72 ++++++--- src/postgresqlconnector.cpp | 315 +++++++++++++++++++++++++----------- src/postgresqlconnector.h | 45 ++++-- 5 files changed, 312 insertions(+), 133 deletions(-) diff --git a/src/dbconnector.cpp b/src/dbconnector.cpp index bdc8c1a..5360c24 100644 --- a/src/dbconnector.cpp +++ b/src/dbconnector.cpp @@ -60,6 +60,10 @@ DbConnector::~DbConnector() } +void DbConnector::clear_last_query_result() +{ +} + bool DbConnector::query(const PT::TextStream & stream) { @@ -229,6 +233,11 @@ void DbConnector::allocate_default_expression_if_needed() } } +const char * DbConnector::get_field_string_value(const char * field_name) +{ + return nullptr; +} + const char * DbConnector::get_field_string_value(const wchar_t * field_name) { return nullptr; diff --git a/src/dbconnector.h b/src/dbconnector.h index 082a10c..24a70c1 100644 --- a/src/dbconnector.h +++ b/src/dbconnector.h @@ -55,6 +55,8 @@ public: DbExpression * get_expression(); + virtual void clear_last_query_result(); + 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); @@ -155,6 +157,8 @@ protected: virtual void allocate_default_expression() = 0; virtual void allocate_default_expression_if_needed(); virtual void deallocate_expression(); + + virtual const char * get_field_string_value(const char * field_name); virtual const char * get_field_string_value(const wchar_t * field_name); virtual const char * query_last_sequence(const wchar_t * sequence_table_name); diff --git a/src/finder.h b/src/finder.h index a91b463..0c4f03e 100644 --- a/src/finder.h +++ b/src/finder.h @@ -277,19 +277,29 @@ public: { res = db_connector->query_select(*out_stream); - if( res ) + try { - result.set_object_exists(true); - result.set_connector(*model_connector); - result.before_select(); - model_connector->map_values_from_query(result); - result.after_select(); + if( res ) + { + result.set_object_exists(true); + result.set_connector(*model_connector); + result.before_select(); + model_connector->map_values_from_query(result); + result.after_select(); + } + + if( !res ) + { + // put some log here? + } + } + catch(...) + { + // throw something? + // if yes then make sure to call db_connector->clear_last_query_result(); } - if( !res ) - { - // put some log here? - } + db_connector->clear_last_query_result(); } } @@ -321,28 +331,38 @@ public: { res = db_connector->query_select(*out_stream); - if( res ) + try { - size_t len = db_connector->last_select_size(); - db_connector->set_current_row_at_beginning(); - - for(size_t i = 0 ; i < len ; ++i) + if( res ) { - model.clear(); - model.set_object_exists(true); - model.set_connector(*model_connector); - model.before_select(); - model_connector->map_values_from_query(model); - model.after_select(); - result.push_back(model); - db_connector->advance_current_row(); + size_t len = db_connector->last_select_size(); + db_connector->set_current_row_at_beginning(); + + for(size_t i = 0 ; i < len ; ++i) + { + model.clear(); + model.set_object_exists(true); + model.set_connector(*model_connector); + model.before_select(); + model_connector->map_values_from_query(model); + model.after_select(); + result.push_back(model); + db_connector->advance_current_row(); + } + } + + if( !res ) + { + // put some log here? } } - - if( !res ) + catch(...) { - // put some log here? + // throw or something? + // make sure to call db_connector->clear_last_query_result() } + + db_connector->clear_last_query_result(); } } diff --git a/src/postgresqlconnector.cpp b/src/postgresqlconnector.cpp index 0bdc521..c3a2477 100644 --- a/src/postgresqlconnector.cpp +++ b/src/postgresqlconnector.cpp @@ -48,17 +48,92 @@ PostgreSQLConnector::PostgreSQLConnector() { pg_conn = nullptr; log_queries = false; - last_status = PGRES_EMPTY_QUERY; - last_result = nullptr; - last_result_rows = 0; - cur_row = 0; } + PostgreSQLConnector::~PostgreSQLConnector() { + close(); } +void PostgreSQLConnector::close() +{ + if( pg_conn ) + { + clear_all_query_results(); + PQfinish(pg_conn); + pg_conn = nullptr; + } +} + + +void PostgreSQLConnector::clear_all_query_results() +{ + while( !query_results.empty() ) + { + clear_last_query_result(); + } +} + +void PostgreSQLConnector::clear_last_query_result() +{ + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + + if( res.result ) + { + PQclear(res.result); + } + + res.result = nullptr; + res.result_rows = 0; + res.status = PGRES_EMPTY_QUERY; + res.cur_row = 0; + + query_results.pop_back(); + } +} + + +// to nie tylko dla selectow moze byc uzywane +size_t PostgreSQLConnector::last_select_size() +{ + if( !query_results.empty() ) + { + return query_results.back().result_rows; + } + + return 0; +} + + +ExecStatusType PostgreSQLConnector::last_query_status() +{ + if( !query_results.empty() ) + { + return query_results.back().status; + } + + return PGRES_EMPTY_QUERY; +} + + +bool PostgreSQLConnector::is_last_result(ExecStatusType t) +{ + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + return (res.result && PQresultStatus(res.result) == t); + } + + return false; +} + + + + void PostgreSQLConnector::allocate_default_expression() { deallocate_expression(); @@ -76,34 +151,40 @@ void PostgreSQLConnector::set_log_queries(bool log_queries) bool PostgreSQLConnector::query(const char * query_str) { -// if( log_queries ) -// log << log1 << "Db: executing query: " << q << logend; - - last_status = PGRES_EMPTY_QUERY; // or something else? - last_result_rows = 0; - last_result = PQexec(pg_conn, query_str); - - if( !last_result ) +// if( pg_conn ) { - if( PQstatus(pg_conn) != CONNECTION_OK ) +// if( log_queries ) +// { +// log << log1 << "Db: executing query: " << q << logend; +// } + + query_results.push_back(QueryResult()); + QueryResult & last_res = query_results.back(); + + last_res.result = PQexec(pg_conn, query_str); + + if( !last_res.result ) { - assert_connection(); - last_result = PQexec(pg_conn, query_str); + if( PQstatus(pg_conn) != CONNECTION_OK ) + { + assert_connection(); + last_res.result = PQexec(pg_conn, query_str); + } + } + + if( last_res.result ) + { + last_res.status = PQresultStatus(last_res.result); + last_res.result_rows = static_cast(PQntuples(last_res.result)); + } + else + { + // log << log1 << "Db: Problem with this query: \"" << q << '\"' << logend; + // log << log1 << "Db: " << PQerrorMessage(pg_conn) << logend; } } - if( last_result ) - { - last_status = PQresultStatus(last_result); - last_result_rows = static_cast(PQntuples(last_result)); - } - else - { -// log << log1 << "Db: Problem with this query: \"" << q << '\"' << logend; -// log << log1 << "Db: " << PQerrorMessage(db_conn->GetPgConn()) << logend; - } - -return last_result != nullptr; +return (!query_results.empty() && query_results.back().result != nullptr); } @@ -121,7 +202,7 @@ const char * PostgreSQLConnector::query_last_sequence(const wchar_t * sequence_t if( query_select(stream) ) { - if( last_result_rows == 1 ) + if( last_select_size() == 1 ) { return get_value(0, 0); } @@ -152,17 +233,17 @@ bool PostgreSQLConnector::query(const std::string & query_str) bool PostgreSQLConnector::query_select(const char * query_str) { - return (query(query_str) && last_status == PGRES_TUPLES_OK); + return (query(query_str) && last_query_status() == PGRES_TUPLES_OK); } bool PostgreSQLConnector::query_update(const char * query_str) { - return (query(query_str) && last_status == PGRES_COMMAND_OK); + return (query(query_str) && last_query_status() == PGRES_COMMAND_OK); } bool PostgreSQLConnector::query_insert(const char * query_str) { - return (query(query_str) && last_status == PGRES_COMMAND_OK); + return (query(query_str) && last_query_status() == PGRES_COMMAND_OK); } @@ -187,17 +268,8 @@ bool PostgreSQLConnector::query_insert(const PT::TextStream & stream) -bool PostgreSQLConnector::is_last_result(ExecStatusType t) -{ - return (last_result && PQresultStatus(last_result) == t); -} -// to nie tylko dla selectow moze byc uzywane -size_t PostgreSQLConnector::last_select_size() -{ - return last_result_rows; -} //int PostgreSQLConnector::Rows(PGresult * r) //{ @@ -241,47 +313,118 @@ size_t PostgreSQLConnector::last_select_size() void PostgreSQLConnector::set_current_row_at_beginning() { - cur_row = 0; + if( !query_results.empty() ) + { + query_results.back().cur_row = 0; + } } void PostgreSQLConnector::advance_current_row() { - cur_row += 1; + if( !query_results.empty() ) + { + query_results.back().cur_row += 1; + } } int PostgreSQLConnector::get_column_index(const char * column_name) { - int c = PQfnumber(last_result, column_name); - // returns -1 if there is no such a column + int col_index = -1; -return c; + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + + if( res.result ) + { + col_index = PQfnumber(res.result, column_name); + // returns -1 if there is no such a column + } + } + + return col_index; } int PostgreSQLConnector::get_column_index(const wchar_t * column_name) { - // temporary - std::string s; // move me somewhere? - PT::WideToUTF8(column_name, s); - return get_column_index(s.c_str()); + PT::WideToUTF8(column_name, temp_column_name); + return get_column_index(temp_column_name.c_str()); } + +//const char * PostgreSQLConnector::get_field_string_value(const wchar_t * field_name) +const char * PostgreSQLConnector::get_field_string_value(const char * column_name) +{ + const char * value_str = nullptr; + + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + + if( res.result ) + { + int col_index = PQfnumber(res.result, column_name); + + if( col_index != -1 ) + { + if( res.cur_row < res.result_rows ) + { + value_str = PQgetvalue(res.result, res.cur_row, col_index); + } + } + } + } + + return value_str; +} + + +const char * PostgreSQLConnector::get_field_string_value(const wchar_t * column_name) +{ + PT::WideToUTF8(column_name, temp_column_name); + return get_field_string_value(temp_column_name.c_str()); +} + + + const char * PostgreSQLConnector::get_value(int row, int col) { - const char * res = PQgetvalue(last_result, row, col); - // can return a null pointer if there is no such an item in the last result + const char * value_str = nullptr; -return res; + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + + if( res.result ) + { + value_str = PQgetvalue(res.result, row, col); + // can return a null pointer if there is no such an item in the last result + } + } + + return value_str; } int PostgreSQLConnector::get_value_length(int row, int col) { - int len = PQgetlength(last_result, row, col); + int len = 0; + + if( !query_results.empty() ) + { + QueryResult & res = query_results.back(); + + if( res.result ) + { + len = PQgetlength(res.result, row, col); + } + } + return len; } @@ -333,25 +476,23 @@ int PostgreSQLConnector::get_value_length(int row, int col) -void PostgreSQLConnector::clear_result() -{ - if( last_result ) - { - PQclear(last_result); - last_result = nullptr; - last_result_rows = 0; - } -} bool PostgreSQLConnector::is_null(int row, int col) { - if( last_result ) + bool is_null = false; + + if( !query_results.empty() ) { - return PQgetisnull(last_result, row, col) == 1; + QueryResult & res = query_results.back(); + + if( res.result ) + { + is_null = (PQgetisnull(res.result, row, col) == 1); + } } - return true; + return is_null; } @@ -595,6 +736,9 @@ void PostgreSQLConnector::overwrite(PT::TextStream & stream) void PostgreSQLConnector::connect() { + // IMPROVEME + // what about if reconnecting is made in the midle of queries? + // e.g. in after_select? (the whole stack query_results will be cleared) close(); allocate_default_expression_if_needed(); @@ -645,7 +789,7 @@ void PostgreSQLConnector::wait_for_connection() //log << log3 << "Db: waiting for the db to be ready...." << logend << logsave; //std::cout << "Db: waiting for the db to be ready...." << std::endl; - while( !assert_connection(false, false) ) + while( !assert_connection(false) ) { sleep(5); } @@ -656,19 +800,9 @@ void PostgreSQLConnector::wait_for_connection() -void PostgreSQLConnector::close() -{ - if( pg_conn ) - { - PQfinish(pg_conn); - pg_conn = nullptr; - last_result = nullptr; - last_result_rows = 0; - } -} - -bool PostgreSQLConnector::assert_connection(bool put_log, bool throw_if_no_connection) +// IMPROVE ME what about the exception now? +bool PostgreSQLConnector::assert_connection(bool put_log) { bool was_connection = true; @@ -712,11 +846,11 @@ bool was_connection = true; //std::cout << "Db: connection to db server cannot be established" << std::endl; } - if( throw_if_no_connection ) - { - //throw Error(WINIX_ERR_DB_FATAL_ERROR_DURING_CONNECTING); - throw int(10); - } +// if( throw_if_no_connection ) +// { +// //throw Error(WINIX_ERR_DB_FATAL_ERROR_DURING_CONNECTING); +// throw int(10); +// } return false; } @@ -732,21 +866,6 @@ void PostgreSQLConnector::set_db_parameters() } -const char * PostgreSQLConnector::get_field_string_value(const wchar_t * field_name) -{ - int c = get_column_index(field_name); - - if( c != -1 ) - { - if( cur_row < last_result_rows ) - { - return get_value(cur_row, c); - } - } - - return nullptr; -} - diff --git a/src/postgresqlconnector.h b/src/postgresqlconnector.h index b15d244..576138f 100644 --- a/src/postgresqlconnector.h +++ b/src/postgresqlconnector.h @@ -50,6 +50,15 @@ public: PostgreSQLConnector(); virtual ~PostgreSQLConnector(); + void clear_all_query_results(); + void clear_last_query_result(); + + // give me a better names + virtual size_t last_select_size(); // was: last_select_size + virtual ExecStatusType last_query_status(); + virtual bool is_last_result(ExecStatusType t); // was: is_last_result + + void set_log_queries(bool log_queries); bool query(const PT::TextStream & stream); @@ -64,15 +73,12 @@ public: bool query_update(const PT::TextStream & stream); bool query_insert(const PT::TextStream & stream); - // give me a better name - virtual size_t last_select_size(); // give me a better name virtual void set_current_row_at_beginning(); virtual void advance_current_row(); - bool is_last_result(ExecStatusType t); /* * get column index from the last query (select) @@ -93,40 +99,61 @@ public: int get_value_length(int row, int col); - void clear_result(); void set_conn_param(const std::wstring & database, const std::wstring & user, const std::wstring & pass); void connect(); void wait_for_connection(); void close(); - bool assert_connection(bool put_log = true, bool throw_if_no_connection = true); + //bool assert_connection(bool put_log = true, bool throw_if_no_connection = true); + bool assert_connection(bool put_log = true); void set_db_parameters(); void log_connection_socket(); //PGconn * GetPgConn(); + protected: PGconn * pg_conn; - PGresult * last_result; // can be null - size_t last_result_rows; // how many rows in the last result query - ExecStatusType last_status; +// PGresult * last_result; // can be null +// size_t last_result_rows; // how many rows in the last result query +// ExecStatusType last_status; bool log_queries; PT::TextStream stream; std::string query_str; - size_t cur_row; + std::string temp_column_name; std::wstring db_database; std::wstring db_user; std::wstring db_pass; + struct QueryResult + { + PGresult * result; // can be null + size_t result_rows; // how many rows in the result query + ExecStatusType status; + size_t cur_row; // used for reading + + QueryResult() + { + result = nullptr; + result_rows = 0; + status = PGRES_EMPTY_QUERY; + cur_row = 0; + } + }; + + std::vector query_results; + void allocate_default_expression(); void overwrite(PT::TextStream & stream); + virtual const char * get_field_string_value(const char * column_name); virtual const char * get_field_string_value(const wchar_t * field_name); + const char * query_last_sequence(const wchar_t * sequence_table_name); };