winix/db/dbbase.cpp

432 lines
7.5 KiB
C++
Executable File

/*
* This file is a part of Winix
* and is not publicly distributed
*
* Copyright (c) 2010, Tomasz Sowa
* All rights reserved.
*
*/
#include <stdlib.h>
#include <limits.h>
#include <limits>
#include "dbbase.h"
#include "core/log.h"
#include "core/error.h"
#include "core/misc.h"
#include "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 std::wstring & q)
{
return AssertQuery(q.c_str());
}
PGresult * DbBase::AssertQuery(const DbTextStream & query)
{
return AssertQuery(query.CStr());
}
void DbBase::AssertResult(PGresult * r, ExecStatusType t)
{
if( PQresultStatus(r) != t )
{
log << "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;
Ezc::UTF8ToWide(res, temp_wide_value);
return temp_wide_value;
}
void DbBase::AssertValueWide(PGresult * r, int row, int col, std::wstring & result)
{
const char * res = AssertValue(r, row, col);
Ezc::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)
{
return strtol( AssertValue(r, row, col), 0, 10 ) != 0;
}
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 );
}
tm DbBase::AssertValueTm(PGresult * r, int row, int col)
{
return ConvertTime(AssertValue(r, row, col));
}
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);
}
tm DbBase::ConvertTime(const char * str)
{
tm t;
memset(&t, 0, sizeof(t));
if( !str )
return t;
size_t len = strlen(str);
if( len != 19 )
{
// the format must be like this: 2008-12-31 22:30:00
log << log1 << "DbBase: ConvertTime: unknown time format: \"" << str << "\"" << logend;
return t;
}
t.tm_year = Toi(str + 0) - 1900; /* year - 1900 */
t.tm_mon = Toi(str + 5) - 1; /* month of year (0 - 11) */
t.tm_mday = Toi(str + 8); /* day of month (1 - 31) */
t.tm_hour = Toi(str + 11); /* hours (0 - 23) */
t.tm_min = Toi(str + 14); /* minutes (0 - 59) */
t.tm_sec = Toi(str + 17); /* seconds (0 - 60) */
// t.tm_wday = 0; /* day of week (Sunday = 0) */
// t.tm_yday = 0; /* day of year (0 - 365) */
// t.tm_isdst = 0; /* is summer time in effect? */
// t.tm_zone = 0; // const_cast<char*>(""); /* abbreviation of timezone name */
return t;
}
const char * DbBase::ConvertTime(const tm & t)
{
// not thread safe
static char buffer[100];
sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d",
t.tm_year + 1900,
t.tm_mon + 1,
t.tm_mday,
t.tm_hour,
t.tm_min,
t.tm_sec);
return buffer;
}
void DbBase::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 DbBase::DoCommand(const DbTextStream & 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;
}
Error DbBase::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;
}
Error DbBase::BeginTrans()
{
return DoCommand("BEGIN;");
}
Error DbBase::RollbackTrans()
{
return DoCommand("ROLLBACK;");
}
Error DbBase::CommitTrans()
{
return DoCommand("COMMIT;");
}
Error DbBase::EndTrans(Error err)
{
if( err == WINIX_ERR_OK )
{
err = CommitTrans();
}
else
{
// we returned the old err code
RollbackTrans();
}
return err;
}