morm/src/postgresqlconnector.cpp

700 lines
14 KiB
C++

/*
* 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) 2018, 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.
*
*/
#include "postgresqlconnector.h"
#include "utf8/utf8.h"
#include "postgresqlexpression.h"
namespace morm
{
PostgreSQLConnector::PostgreSQLConnector()
{
pg_conn = nullptr;
log_queries = false;
last_status = PGRES_EMPTY_QUERY;
last_result = nullptr;
}
PostgreSQLConnector::~PostgreSQLConnector()
{
}
void PostgreSQLConnector::allocate_default_expression()
{
deallocate_expression();
db_expression = new PostgreSQLExpression();
expression_allocated = true;
}
void PostgreSQLConnector::set_log_queries(bool log_queries)
{
this->log_queries = log_queries;
}
bool PostgreSQLConnector::query(const char * query_str)
{
// if( log_queries )
// log << log1 << "Db: executing query: " << q << logend;
last_status = PGRES_EMPTY_QUERY;
last_result = PQexec(pg_conn, query_str);
if( !last_result )
{
if( PQstatus(pg_conn) != CONNECTION_OK )
{
//AssertConnection();
last_result = PQexec(pg_conn, query_str);
}
}
if( last_result )
{
last_status = PQresultStatus(last_result);
}
else
{
// log << log1 << "Db: Problem with this query: \"" << q << '\"' << logend;
// log << log1 << "Db: " << PQerrorMessage(db_conn->GetPgConn()) << logend;
}
return last_result != nullptr;
}
bool PostgreSQLConnector::query_select(const char * query_str)
{
return (query(query_str) && last_status == PGRES_TUPLES_OK);
}
bool PostgreSQLConnector::query_update(const char * query_str)
{
return (query(query_str) && last_status == PGRES_COMMAND_OK);
}
bool PostgreSQLConnector::query_insert(const char * query_str)
{
return (query(query_str) && last_status == PGRES_COMMAND_OK);
}
//Error PostgreSQLConnector::DoCommand(const char * command)
//{
// PGresult * r = 0;
// Error status = WINIX_ERR_OK;
//
// try
// {
// r = AssertQuery(command);
// AssertResult(r, PGRES_COMMAND_OK);
// }
// catch(const Error & e)
// {
// status = e;
// }
//
// ClearResult(r);
//
//return status;
//}
bool PostgreSQLConnector::is_last_result(ExecStatusType t)
{
return (last_result && PQresultStatus(last_result) == t);
}
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
return c;
}
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
return res;
}
bool PostgreSQLConnector::get_value(int row, int col, std::string & result, bool clear_string)
{
if( clear_string )
result.clear();
const char * raw_result = get_value(row, col);
if( raw_result )
{
result += raw_result;
}
return raw_result != nullptr;
}
bool PostgreSQLConnector::get_value(int row, int col, std::wstring & result, bool clear_string)
{
if( clear_string )
result.clear();
const char * raw_result = get_value(row, col);
if( raw_result )
{
PT::UTF8ToWide(raw_result, result);
}
return raw_result != nullptr;
}
//void PostgreSQLConnector::get_value_bin(int row, int col, std::string & result, bool clear_string)
//{
// if( clear_string )
// result.clear();
//
// const char * raw_result = get_value(row, col);
//
// if( raw_result )
// {
// int len = PQgetlength(last_result, row, col);
//
// if( len > 0 )
// {
// unescape_bin(raw_result, len, result);
// }
// }
//}
//long PostgreSQLConnector::AssertValueLong(PGresult * r, int row, int col)
//{
// return strtol( AssertValue(r, row, col), 0, 10 );
//}
//
//
//int PostgreSQLConnector::AssertValueInt(PGresult * r, int row, int col)
//{
// return (int)strtol( AssertValue(r, row, col), 0, 10 );
//}
//
//
//bool PostgreSQLConnector::AssertValueBool(PGresult * r, int row, int col)
//{
// const char * s = AssertValue(r, row, col);
// return (s[0]=='t' || s[0]=='y' || s[0]=='1');
//}
//
//
//unsigned long PostgreSQLConnector::AssertValueULong(PGresult * r, int row, int col)
//{
// return strtoul( AssertValue(r, row, col), 0, 10 );
//}
//
//
//unsigned int PostgreSQLConnector::AssertValueUInt(PGresult * r, int row, int col)
//{
// return (unsigned int)strtoul( AssertValue(r, row, col), 0, 10 );
//}
//
//
//
//PT::Date PostgreSQLConnector::AssertValueDate(PGresult * r, int row, int col)
//{
// PT::Date date = AssertValue(r, row, col);
//
//return date;
//}
//
//
//bool PostgreSQLConnector::AssertValueSpace(PGresult * r, int row, int col, PT::Space & space)
//{
// const char * res = AssertValue(r, row, col);
//
// conf_parser.SetSpace(space);
// space.Clear();
//
// PT::SpaceParser::Status status = conf_parser.ParseString(res);
//
// if( status != PT::SpaceParser::ok )
// {
// log << log1 << "Db: a problem with parsing a PT::Space";
//
// if( status == PT::SpaceParser::syntax_error )
// log << ", syntax error at line: " << conf_parser.line;
//
// log << logend;
//
// space.Clear();
// return false;
// }
//
//return true;
//}
//
//
void PostgreSQLConnector::clear_result()
{
if( last_result )
{
PQclear(last_result);
last_result = nullptr;
}
}
bool PostgreSQLConnector::is_null(int row, int col)
{
if( last_result )
{
return PQgetisnull(last_result, row, col) == 1;
}
return true;
}
//int PostgreSQLConnector::Rows(PGresult * r)
//{
// // PQntuples - Returns the number of rows (tuples) in the query result. Because it returns
// // an integer result, large result sets might overflow the return value on 32-bit operating systems.
// return PQntuples(r);
//}
//int PostgreSQLConnector::Cols(PGresult * r)
//{
// // PQnfields - Returns the number of columns (fields) in each row of the query result.
// return PQnfields(r);
//}
//long PostgreSQLConnector::AffectedRows(PGresult * r)
//{
// // PQcmdTuples - This function returns a string containing the number of rows affected by the SQL
// // statement that generated the PGresult. This function can only be used following the execution
// // of an INSERT, UPDATE, DELETE, MOVE, FETCH, or COPY statement, or [...]
// char * rows_str = PQcmdTuples(r); // can be an empty string
// long rows = 0;
//
// if( rows_str )
// {
// rows = strtol(rows_str, 0, 10);
// // strtol - If an overflow or underflow occurs, errno is set to ERANGE
// // and the function return value is clamped according to the following table:
// // Function underflow overflow
// // strtol() LONG_MIN LONG_MAX
//
// if( rows < 0 )
// rows = 0;
// }
//
//return rows;
//}
//long PostgreSQLConnector::AssertCurrval(const char * table)
//{
// PGresult * r;
//
// bquery.Clear();
// bquery << R("select currval(")
// << table
// << R(");");
//
// r = AssertQuery(bquery);
// AssertResult(r, PGRES_TUPLES_OK);
//
// if( Rows(r) != 1 )
// {
// log << log1 << "Db: error (currval) for table: " << table << ", " << PQerrorMessage(db_conn->GetPgConn()) << logend;
// throw Error(WINIX_ERR_DB_ERR_CURRVAL);
// }
//
//return AssertValueLong(r, 0, 0);
//}
//
//void PostgreSQLConnector::CreateIdList(const std::vector<long> & id_tab, std::wstring & list, bool add_parentheses)
//{
//wchar_t buffer[50];
//size_t buffer_len = sizeof(buffer) / sizeof(wchar_t);
//
// list.clear();
//
// if( add_parentheses )
// list += '(';
//
// for(size_t i=0 ; i<id_tab.size() ; ++i)
// {
// Toa((unsigned long)id_tab[i], buffer, buffer_len);
// list += buffer;
//
// if( i+1 < id_tab.size() )
// list += ',';
// }
//
// if( add_parentheses )
// list += ')';
//}
//Error PostgreSQLConnector::BeginTrans()
//{
// return DoCommand("BEGIN;");
//}
//
//
//
//Error PostgreSQLConnector::RollbackTrans()
//{
// return DoCommand("ROLLBACK;");
//}
//
//
//
//Error PostgreSQLConnector::CommitTrans()
//{
// return DoCommand("COMMIT;");
//}
//
//
//
//bool PostgreSQLConnector::EndTrans(bool everything_ok)
//{
//bool result;
//
// if( everything_ok )
// {
// result = (CommitTrans() == WINIX_ERR_OK);
// }
// else
// {
// RollbackTrans();
// // we return the old err code
// result = false;
// }
//
//return result;
//}
//
//
//Error PostgreSQLConnector::EndTrans(Error err)
//{
// if( err == WINIX_ERR_OK )
// {
// err = CommitTrans();
// }
// else
// {
// // we return the old err code
// RollbackTrans();
// }
//
//return err;
//}
/*
converting from a bytea
the old way (escape format)
*/
/*
int PostgreSQLConnector::CharToInt(char c)
{
return (int)(unsigned char)(c-'0');
}
bool PostgreSQLConnector::IsCorrectOctalDigit(char c)
{
return c>='0' && c<='7';
}
// moves 'i' at least once
// return -1 if there is en error
int PostgreSQLConnector::UnescapeBin(const char * str, size_t & i, size_t len)
{
if( str[i] != '\\' )
return str[i++];
i += 1;
if( i >= len )
return -1;
if( str[i] == '\\' )
return str[i++];
if( i+2 >= len )
{
i = len;
return -1;
}
if( !IsCorrectOctalDigit(str[i]) ||
!IsCorrectOctalDigit(str[i+1]) ||
!IsCorrectOctalDigit(str[i+2]) )
{
i += 3;
return -1;
}
int c = 8*8*CharToInt(str[i]) + 8*CharToInt(str[i+1]) + CharToInt(str[i+2]);
i += 3;
if( c<0 || c>255 )
return -1;
return c;
}
void PostgreSQLConnector::UnescapeBin(const char * str, size_t len, std::string & out, bool clear_out)
{
int c;
size_t i = 0;
if( clear_out )
out.clear();
while( i < len )
{
c = UnescapeBin(str, i, len);
if( c != -1 )
out += c;
}
}
*/
/*
converting from a bytea
the new way (hex format)
*/
//char PostgreSQLConnector::UnescapeBinHexToDigit(char hex)
//{
// if( hex>='0' && hex<='9' )
// return hex - '0';
//
// if( hex>='a' && hex<='z' )
// return hex - 'a' + 10;
//
// if( hex>='A' && hex<='Z' )
// return hex - 'A' + 10;
//
//return 0;
//}
//
//
//void PostgreSQLConnector::UnescapeBin(const char * str, size_t len, std::string & out, bool clear_out)
//{
// if( clear_out )
// out.clear();
//
// if( len < 2 || str[0]!='\\' || str[1]!='x' )
// {
// log << log1 << "Db: unsupported binary format (skipping)" << logend;
// return;
// }
//
// for(size_t i=2 ; i + 1 < len ; i+=2 )
// {
// int c1 = UnescapeBinHexToDigit(str[i]);
// int c2 = UnescapeBinHexToDigit(str[i+1]);
//
// out += ((c1 << 4) | c2);
// }
//}
//void PostgreSQLConnector::SetConnParam(const std::wstring & database_name, const std::wstring & user, const std::wstring & pass)
//{
// db_database = database_name;
// db_user = user;
// db_pass = pass;
//}
//
//
//
//void PostgreSQLConnector::Connect()
//{
// Close();
//
// conn_info.Clear();
// conn_info.SetExtented(false);
//
// conn_info << R("dbname=") << db_database
// << R(" user=") << db_user
// << R(" password=") << db_pass;
//
// pg_conn = PQconnectdb(conn_info.CStr());
//
// // warning! pg_conn can be not null but there cannnot be a connection established
// // use PQstatus(pg_conn) to check whether the connection works fine
//}
//
//
//
//void PostgreSQLConnector::LogConnectionSocket()
//{
// log << log2 << "Db: connection to the database works fine" << logend;
// log << log3 << "Db: connection socket: " << PQsocket(pg_conn) << logend;
//}
//
//
//
//void PostgreSQLConnector::WaitForConnection()
//{
// if( !pg_conn || PQstatus(pg_conn) != CONNECTION_OK )
// {
// log << log3 << "Db: waiting for the db to be ready...." << logend << logsave;
//
// while( !AssertConnection(false, false) )
// sleep(5);
//
// LogConnectionSocket();
// }
//}
//
//
//
//void PostgreSQLConnector::Close()
//{
// if( pg_conn )
// {
// PQfinish(pg_conn);
// pg_conn = 0;
// }
//}
//
//
//
//bool PostgreSQLConnector::AssertConnection(bool put_log, bool throw_if_no_connection)
//{
//bool was_connection = true;
//
//
// if( !pg_conn )
// {
// was_connection = false;
// Connect();
// }
// else
// if( PQstatus(pg_conn) != CONNECTION_OK )
// {
// if( put_log )
// log << log2 << "Db: connection to the database is lost, trying to recover" << logend;
//
// was_connection = false;
// PQreset(pg_conn);
// }
//
//
// if( pg_conn && PQstatus(pg_conn) == CONNECTION_OK )
// {
// if( !was_connection )
// {
// if( put_log )
// LogConnectionSocket();
//
// SetDbParameters();
// }
//
// return true;
// }
// else
// {
// if( put_log )
// log << log1 << "Db: connection to db server cannot be established" << logend;
//
// if( throw_if_no_connection )
// throw Error(WINIX_ERR_DB_FATAL_ERROR_DURING_CONNECTING);
//
// return false;
// }
//}
//
//
//
//void PostgreSQLConnector::SetDbParameters()
//{
// if( PQsetClientEncoding(pg_conn, "UTF8") == -1 )
// log << log1 << "Db: Can't set the proper client encoding" << logend;
//}
}