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
This commit is contained in:
Tomasz Sowa 2018-04-18 10:22:01 +00:00
parent 7bae5224e5
commit ffb7ac85a6
5 changed files with 312 additions and 133 deletions

View File

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

View File

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

View File

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

View File

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

View File

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