/* * This file is a part of Winix * and is not publicly distributed * * Copyright (c) 2010-2012, Tomasz Sowa * All rights reserved. * */ #include #include #include #include "dbbase.h" #include "core/log.h" #include "core/error.h" #include "core/misc.h" #include "utf8/utf8.h" DbBase::DbBase() { db_conn = 0; log_queries = false; } void DbBase::SetConn(DbConn * conn) { db_conn = conn; } void DbBase::SetConn(DbConn & conn) { db_conn = &conn; } DbConn * DbBase::GetConn() { return db_conn; } void DbBase::LogQueries(bool log_q) { log_queries = log_q; } PGresult * DbBase::AssertQuery(const char * q) { if( log_queries ) log << log1 << "Db: executing query: " << q << logend; bool bad_query = false; PGresult * r = PQexec(db_conn->GetPgConn(), q); if( !r ) { bad_query = true; if( PQstatus(db_conn->GetPgConn()) != CONNECTION_OK ) { db_conn->AssertConnection(); r = PQexec(db_conn->GetPgConn(), q); if( r ) bad_query = false; } } if( bad_query ) { log << log1 << "Db: Problem with this query: \"" << q << '\"' << logend; log << log1 << "Db: " << PQerrorMessage(db_conn->GetPgConn()) << logend; throw Error(WINIX_ERR_DB_INCORRECT_QUERY); } return r; } PGresult * DbBase::AssertQuery(const DbTextStream & query) { return AssertQuery(query.CStr()); } PGresult * DbBase::AssertQuery(const char * q, ExecStatusType t) { PGresult * r = AssertQuery(q); AssertResult(r, t); return r; } PGresult * DbBase::AssertQuery(const DbTextStream & query, ExecStatusType t) { return AssertQuery(query.CStr(), t); } void DbBase::AssertResult(PGresult * r, ExecStatusType t) { if( PQresultStatus(r) != t ) { log << log1 << "Db: Incorrect result status: " << PQerrorMessage(db_conn->GetPgConn()) << logend; throw Error(WINIX_ERR_DB_INCORRENT_RESULT_STATUS); } } int DbBase::AssertColumn(PGresult * r, const char * column_name) { int c = PQfnumber(r, column_name); if( c == -1 ) { log << log1 << "Db: there is no column: " << column_name << logend; throw Error(WINIX_ERR_DB_NO_COLUMN); } return c; } const char * DbBase::AssertValue(PGresult * r, int row, int col) { const char * res = PQgetvalue(r, row, col); if( !res ) { log << log1 << "Db: there is no such an item in the result, row:" << row << ", col:" << col << logend; throw Error(WINIX_ERR_NO_ITEM); } return res; } const std::wstring & DbBase::AssertValueWide(PGresult * r, int row, int col) { const char * res = AssertValue(r, row, col); static std::wstring temp_wide_value; // !! IMPROVE ME add as a class field (nonstatic) PT::UTF8ToWide(res, temp_wide_value); return temp_wide_value; } void DbBase::AssertValueBin(PGresult * r, int row, int col, std::string & result) { result.clear(); const char * res = AssertValue(r, row, col); int len = PQgetlength(r, row, col); if( len <= 0 ) return; UnescapeBin(res, len, result); } void DbBase::AssertValueWide(PGresult * r, int row, int col, std::wstring & result) { const char * res = AssertValue(r, row, col); PT::UTF8ToWide(res, result); } long DbBase::AssertValueLong(PGresult * r, int row, int col) { return strtol( AssertValue(r, row, col), 0, 10 ); } int DbBase::AssertValueInt(PGresult * r, int row, int col) { return (int)strtol( AssertValue(r, row, col), 0, 10 ); } bool DbBase::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 DbBase::AssertValueULong(PGresult * r, int row, int col) { return strtoul( AssertValue(r, row, col), 0, 10 ); } unsigned int DbBase::AssertValueUInt(PGresult * r, int row, int col) { return (unsigned int)strtoul( AssertValue(r, row, col), 0, 10 ); } PT::Date DbBase::AssertValueDate(PGresult * r, int row, int col) { PT::Date date = AssertValue(r, row, col); return date; } bool DbBase::AssertValueSpace(PGresult * r, int row, int col, PT::Space & space, bool split_single) { const char * res = AssertValue(r, row, col); conf_parser.UTF8(true); conf_parser.SplitSingle(split_single); 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 DbBase::ClearResult(PGresult * r) { if( r ) PQclear(r); } bool DbBase::IsNull(PGresult * r, int row, int col) { return PQgetisnull(r, row, col) == 1; } int DbBase::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 DbBase::Cols(PGresult * r) { // PQnfields - Returns the number of columns (fields) in each row of the query result. return PQnfields(r); } long DbBase::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 DbBase::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 DbBase::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 DbBase::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 DbBase::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 DbBase::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 DbBase::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); } } /* end of converting from bytea */