/* * This file is a part of morm * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * 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 & 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='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; //} }