winix/core/db.cpp

1454 lines
27 KiB
C++
Raw Normal View History

/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008-2009, Tomasz Sowa
* All rights reserved.
*
*/
#include "db.h"
#include "log.h"
#include "misc.h"
Db::Db(bool close_at_end_)
{
pg_conn = 0;
close_at_end = close_at_end_;
}
Db::~Db()
{
if( close_at_end )
Close();
}
PGconn * Db::GetPGconn()
{
return pg_conn;
}
void Db::Init(const std::string & d, const std::string & u, const std::string & p)
{
db_database = d;
db_user = u;
db_pass = p;
Connect();
}
void Db::Connect()
{
Close();
std::ostringstream buf;
buf << "dbname=" << db_database << " user=" << db_user << " password=" << db_pass;
pg_conn = PQconnectdb(buf.str().c_str());
if( pg_conn )
log << log3 << "Db: Socket: " << PQsocket(pg_conn) << logend;
// 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 Db::SetDbParameters()
{
if( PQsetClientEncoding(pg_conn, "LATIN2") == -1 )
log << log1 << "Db: Can't set the proper client encoding" << logend;
}
void Db::Close()
{
if( pg_conn )
{
PQfinish(pg_conn);
pg_conn = 0;
}
}
void Db::AssertConnection()
{
bool was_connection = true;
if( !pg_conn )
{
was_connection = false;
Connect();
}
else
if( PQstatus(pg_conn) != CONNECTION_OK )
{
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 == false )
log << log2 << "Db: Connection to the database works fine" << logend;
SetDbParameters();
}
else
{
log << log1 << "Db: Connection to db server cannot be established" << logend;
throw Error(Error::db_fatal_error_during_connecting);
}
}
std::string Db::Escape(const std::string & s)
{
std::string result;
result.resize(s.length() * 2 + 1);
size_t len = PQescapeStringConn(pg_conn, const_cast<char*>( result.c_str() ), s.c_str(), s.length(), 0);
result.resize(len);
return result;
}
std::string Db::Escape(const char * s)
{
std::string result;
int len;
for(len=0 ; s[len] != 0 ; ++len);
result.resize(len * 2 + 1);
size_t len_new = PQescapeStringConn(pg_conn, const_cast<char*>( result.c_str() ), s, len, 0);
result.resize(len_new);
return result;
}
// ------------------
PGresult * Db::AssertQuery(const std::string & q)
{
PGresult * r = PQexec(pg_conn, q.c_str());
if( !r )
{
log << log1 << "Db: Problem with query: \"" << q << '\"' << logend;
log << log1 << "Db: " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_incorrect_query);
}
return r;
}
void Db::AssertResultStatus(PGresult * r, ExecStatusType t)
{
if( PQresultStatus(r) != t )
{
log << "Db: Incorrect result status: " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_incorrent_result_status);
}
}
int Db::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(Error::db_no_column);
}
return c;
}
const char * Db::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(Error::db_no_item);
}
return res;
}
void Db::ClearResult(PGresult * r)
{
if( r )
PQclear(r);
}
bool Db::CheckUser(std::string & login, std::string & password, long & user_id)
{
PGresult * r = 0;
bool user_ok = false;
try
{
AssertConnection();
std::ostringstream query;
query << "select id from core.user where login='" << Escape(login) << "' and password='" << Escape(password) << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows == 0 )
throw Error(Error::db_incorrect_login);
if( rows > 1 )
{
log << log1 << "Db: there is more than one user: " << login << " (with the same password)" << logend;
throw Error(Error::db_more_than_one_login);
}
int cuser_id = AssertColumn(r, "id");
const char * fuser_id = AssertValue(r, 0, cuser_id);
user_id = atol( fuser_id );
user_ok = true;
}
catch(const Error &)
{
}
ClearResult(r);
return user_ok;
}
bool Db::AddItemCreateUrlSubject(Item & item)
{
bool is_that_url;
PGresult * r = 0;
int index = 1;
const int max_index = 100;
char appendix[20];
appendix[0] = 0;
try
{
do
{
std::ostringstream query;
// this Escape can be put at the beginning (performance)
query << "select id from core.item where url='" << Escape(item.url) << appendix << "' and parent_id='" << item.parent_id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 0 )
{
sprintf(appendix, "_(%d)", ++index);
is_that_url = true;
}
else
{
item.url += appendix;
is_that_url = false;
}
ClearResult(r);
r = 0;
}
while( is_that_url && index <= max_index );
}
catch(const Error &)
{
is_that_url = true; // for return false
}
ClearResult(r);
return !is_that_url;
}
// for testing consistency
void Db::CheckAllUrlSubjectModifyItem(Item & item)
{
PGresult * r = 0;
try
{
std::ostringstream query;
query << "update core.item set url=";
// url
if( AddItemCreateUrlSubject(item) )
query << '\'' << Escape(item.url) << '\'';
else
{
query << '\'' << item.id << '\'';
item.url.clear();
}
query << " where id='" << item.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error &)
{
}
ClearResult(r);
}
// for checking consistency
void Db::CheckAllUrlSubject()
{
PGresult * r = 0;
Item item;
try
{
AssertConnection();
std::ostringstream query, query2;
query << "select item.id, subject from core.item left join core.content on item.content_id = content.id where url is null or url=''";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
int cid = AssertColumn(r, "id");
int csubject = AssertColumn(r, "subject");
for(int i = 0 ; i<rows ; ++i)
{
item.id = atol( AssertValue(r, i, cid) );
item.subject = AssertValue(r, i, csubject);
CheckAllUrlSubjectModifyItem(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
long Db::AssertCurrval(const char * table)
{
PGresult * r;
std::ostringstream query;
query << "select currval('" << table << "');";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 1 )
{
log << log1 << "Db: error (currval) for table: " << table << ", " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_err_currval);
}
long res = strtol( AssertValue(r, 0, 0), 0, 10 );
return res;
}
Error Db::AddItemIntoItem(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.item (user_id, group_id, privileges, date_creation, date_modification, type, parent_id, content_id, default_item, url) values (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.group_id << "', ";
query << '\'' << item.privileges << "', ";
query << '\'' << ConvertTime(item.date_creation) << "', ";
query << '\'' << ConvertTime(item.date_modification) << "', ";
query << '\'' << static_cast<int>(item.type) << "', ";
query << '\'' << item.parent_id << "', ";
query << '\'' << item.content_id << "', ";
query << '\'' << item.default_item << "', ";
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << '\'' << Escape(item.url) << "');";
else
query << "currval('core.item_id_seq')" << ");";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
item.id = AssertCurrval("core.item_id_seq");
if( !url_without_id )
ToString(item.url, item.id);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::AddItemIntoContent(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.content (subject, content, content_type) values (";
query << '\'' << Escape(item.subject) << "', ";
query << '\'' << Escape(item.content) << "', ";
query << '\'' << item.content_type << "');";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
item.content_id = AssertCurrval("core.content_id_seq");
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::AddItem(Item & item)
{
Error result = Error::ok;
if( item.type == Item::file )
result = AddItemIntoContent(item);
else
item.content_id = -1;
if( result == Error::ok )
result = AddItemIntoItem(item);
return result;
}
// !! with_subject zamienic na with_url
Error Db::EditItemInItem(Item & item, bool with_subject)
{
PGresult * r = 0;
Error result = Error::ok;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (user_id, group_id, privileges, date_creation, date_modification, type, default_item, parent_id";
if( with_subject )
query << ", url";
query << ") = (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.group_id << "', ";
query << '\'' << item.privileges << "', ";
query << '\'' << ConvertTime(item.date_creation) << "', ";
query << '\'' << ConvertTime(item.date_modification) << "', ";
query << '\'' << static_cast<int>(item.type) << "', ";
query << '\'' << item.default_item << "', ";
query << '\'' << item.parent_id << "' ";
if( with_subject )
{
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << ", '" << Escape(item.url) << "'";
else
query << ", '" << item.id << "'";
}
query << ") where id='" << item.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
if( with_subject && !url_without_id )
ToString(item.url, item.id);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditItemInContent(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.content set (subject, content, content_type) = (";
query << '\'' << Escape(item.subject) << "', ";
query << '\'' << Escape(item.content) << "', ";
query << '\'' << item.content_type << "' ";
query << ") where id='" << item.content_id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditItemGetId(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "select item.id, content.id from core.item left join core.content on item.content_id = content.id where item.parent_id='";
query << item.parent_id << "' and item.url='" << Escape(item.url) << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 1 || PQnfields(r) != 2 )
throw Error(Error::db_no_item);
// we cannot use AssertColumn() with a name because both columns are called 'id'
item.id = atol( AssertValue(r, 0, 0) );
item.content_id = atol( AssertValue(r, 0, 1) );
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditItemGetContentId(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
// !! tutaj chyba nie ma potrzeby robic left join z core.content (nie uzywamy nic z tamtej tabeli)
query << "select content_id from core.item left join core.content on item.content_id = content.id where item.id='";
query << item.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 1 || PQnfields(r) != 1 )
throw Error(Error::db_no_item);
item.content_id = atol( AssertValue(r, 0, 0) );
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
// item.id must be set
// !! with_subject zamienic na with_url
// !! moze nazwa poprostu EditItem (nie trzeba tego ById) ? (sprawdzic czy nie koliduje z inna nazwa)
Error Db::EditItemById(Item & item, bool with_subject)
{
Error result = Error::ok;
// !! dla katalogow nie testowane jeszcze
if( item.type == Item::file )
result = EditItemGetContentId(item);
if( result == Error::ok )
{
if( item.type == Item::file )
result = EditItemInContent(item);
if( result == Error::ok )
result = EditItemInItem(item, with_subject);
}
return result;
}
// item.url and item.parent_id must be set
// doesn't work with directiories
Error Db::EditItemByUrl(Item & item, bool with_subject)
{
Error result = EditItemGetId(item);
if( result == Error::ok )
{
result = EditItemInContent(item);
if( result == Error::ok )
result = EditItemInItem(item, with_subject);
}
return result;
}
Error Db::EditDefaultItem(long id, long new_default_item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (default_item) = ('" << new_default_item << "') where id='" << id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
char * rows_str = PQcmdTuples(r);
long rows = 0;
if( rows_str )
rows = atol(rows_str);
if( rows == 0 )
{
result = Error::db_no_item;
log << log1 << "Db: EditDefaultItem: no such item, id: " << id << logend;
}
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
PGresult * Db::GetItemsQuery(Item & item_ref, bool asc)
{
std::ostringstream query;
query << "select * from core.item left join core.content on item.content_id = content.id where type!='0' and parent_id='" << item_ref.parent_id << "'";
if( item_ref.id != -1 )
query << " and item.id='" << item_ref.id << "'";
if( !item_ref.url.empty() )
query << " and url='" << Escape(item_ref.url) << "'";
query << " order by item.date_modification";
if( asc )
query << " asc";
else
query << " desc";
query << ';';
return AssertQuery(query.str());
}
void Db::GetItems(std::vector<Item> & item_table, Item & item_ref, bool asc)
{
item_table.clear();
PGresult * r = 0;
try
{
AssertConnection();
r = GetItemsQuery(item_ref, asc);
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
item_table.push_back(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetItem(std::vector<Item> & item_table, long id)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item left join core.content on item.content_id = content.id where type='1' and item.id='" << id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
if( rows > 1 )
log << log1 << "Db: we have more than one item with id: " << id << logend;
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
item_table.push_back(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
// !! nowy interfejs
Error Db::GetItem(long parent_id, const std::string & url, Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item left join core.content on item.content_id = content.id where type='1' and item.parent_id='";
query << parent_id << "' and item.url='" << Escape(url) << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows == 0 )
throw Error(Error::db_no_item);
ItemColumns col;
col.SetColumns(r);
col.SetItem(r, 0, item);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
long Db::GetItemId(long parent_id, const std::string & url, Item::Type type)
{
PGresult * r = 0;
long result = -1;
try
{
AssertConnection();
std::ostringstream query;
query << "select id from core.item where type='" << static_cast<int>(type) << "' and item.parent_id='";
query << parent_id << "' and item.url='" << Escape(url) << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows == 1 )
result = atol( AssertValue(r, 0, 0) );
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
long Db::GetFileId(long parent_id, const std::string & url)
{
return GetItemId(parent_id, url, Item::file);
}
long Db::GetDirId(long parent_id, const std::string & url)
{
return GetItemId(parent_id, url, Item::dir);
}
bool Db::GetPriv(Item & item, long id)
{
bool result = false;
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select user_id, group_id, privileges, subject, content from core.item left join core.content on item.content_id = content.id where item.id='" << id << "';"; // !! tymczasowo odczytujemy z content i subject
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows != 1 )
throw Error();
ItemColumns col;
col.SetColumns(r);
col.SetItem(r, 0, item);
result = true;
}
catch(const Error &)
{
}
ClearResult(r);
return result;
}
// !! to jest nowy interfejs, z jawnym podaniem id
Error Db::EditPrivById(Item & item, long id)
{
PGresult * r = 0;
Error result = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (user_id, group_id, privileges) = (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.group_id << "', ";
query << '\'' << item.privileges << "' ";
query << ") where id='" << id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::DelDirById(long id)
{
Error result = Error::ok;
PGresult * r = 0;
const char * crows;
try
{
AssertConnection();
std::ostringstream query, query2;
query << "delete from core.content where content.id in (select content_id from core.item where parent_id='" << id << "');";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_COMMAND_OK);
crows = PQcmdTuples(r);
if( crows )
log << log2 << "Db: deleted " << atol(crows) << " rows from core.content" << logend;
query2 << "delete from core.item where id='" << id << "' or parent_id='" << id << "';";
r = AssertQuery( query2.str() );
AssertResultStatus(r, PGRES_COMMAND_OK);
crows = PQcmdTuples(r);
if( crows )
log << log1 << "Db: deleted dir: " << id << " (deleted: " << atol(crows) << " rows)" << logend;
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
bool Db::DelItemDelItem(const Item & item)
{
long rows = 0;
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "delete from core.item where id='" << item.id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_COMMAND_OK);
const char * crows = PQcmdTuples(r);
if( crows )
{
rows = atol(crows);
if( rows > 1 )
log << log1 << "Db: more than one item were deleted" << logend;
else
if( rows == 0 )
log << log1 << "Db: no item has been deleted" << logend;
}
}
catch(const Error &)
{
}
ClearResult(r);
return rows != 0;
}
void Db::DelItemDelContent(const Item & item)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "delete from core.content where id='" << item.content_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_COMMAND_OK);
const char * crows = PQcmdTuples(r);
if( crows )
{
long rows = atol(crows);
if( rows > 1 )
log << log1 << "Db: more than one content were deleted" << logend;
else
if( rows == 0 )
log << log1 << "Db: no content has been deleted" << logend;
}
}
catch(const Error &)
{
}
ClearResult(r);
}
Error Db::DelItemCountContents(const Item & item, long & contents)
{
Error result = Error::ok;
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select count('id') from core.item where content_id='" << item.content_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
contents = atol( AssertValue(r, 0, 0) );
log << log1 << "counters: " << contents << logend; // !!
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
bool Db::DelItem(const Item & item)
{
long contents;
Error result = DelItemCountContents(item, contents);
if( result == Error::ok && contents == 1 )
DelItemDelContent(item);
return DelItemDelItem(item);
}
void Db::GetDirs(DirContainer & dir_table)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item where type='0';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
dir_table.PushBack( item );
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetUsers(UGContainer<User> & user_table)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select id, login, super_user, group_id from core.user left outer join core.group_mem on core.user.id = core.group_mem.user_id order by id asc;";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
int cid = AssertColumn(r, "id");
int cname = AssertColumn(r, "login");
int csuper_user = AssertColumn(r, "super_user");
int cgroup_id = AssertColumn(r, "group_id");
User u;
long last_id = -1;
UGContainer<User>::Iterator iter;
for(int i = 0 ; i<rows ; ++i)
{
u.id = atol( AssertValue(r, i, cid) );
if( u.id != last_id )
{
u.name = AssertValue(r, i, cname);
u.super_user = static_cast<bool>( atoi( AssertValue(r, i, csuper_user) ) );
log << log1 << "Db: get user: id:" << u.id << ", name:" << u.name << ", super_user:" << u.super_user << logend;
iter = user_table.PushBack( u );
last_id = u.id;
}
long group_id = atol( AssertValue(r, i, cgroup_id) );
if( !PQgetisnull(r, i, cgroup_id) && group_id!=-1 && !user_table.Empty() )
{
iter->groups.push_back(group_id);
log << log3 << "Db: user:" << iter->name << " is a member of group_id:" << group_id << logend;
}
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetGroups(UGContainer<Group> & group_table)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select id, core.group.group, user_id from core.group left outer join core.group_mem on core.group.id = core.group_mem.group_id order by id asc;";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
int cid = AssertColumn(r, "id");
int cname = AssertColumn(r, "group");
int cuser_id = AssertColumn(r, "user_id");
Group g;
long last_id = -1;
UGContainer<Group>::Iterator iter;
for(int i = 0 ; i<rows ; ++i)
{
g.id = atol( AssertValue(r, i, cid) );
if( g.id != last_id )
{
g.name = AssertValue(r, i, cname);
log << log3 << "Db: get group, id:" << g.id << ", group:" << g.name << logend;
iter = group_table.PushBack( g );
last_id = g.id;
}
long user_id = atol( AssertValue(r, i, cuser_id) );
if( !PQgetisnull(r, i, cuser_id) && user_id!=-1 && !group_table.Empty() )
{
iter->members.push_back(user_id);
log << log3 << "Db: get group member: user_id:" << user_id << logend;
}
}
}
catch(const Error &)
{
}
ClearResult(r);
}
tm Db::ConvertTime(const char * str)
{
tm t;
memset(&t, 0, sizeof(t));
if( !str )
return t;
size_t len = strlen(str);
if( len != 19 )
{
// unknown format
// the format must be like this: 2008-12-31 22:30:00
return t;
}
t.tm_year = atoi(str + 0) - 1900; /* year - 1900 */
t.tm_mon = atoi(str + 5) - 1; /* month of year (0 - 11) */
t.tm_mday = atoi(str + 8); /* day of month (1 - 31) */
t.tm_hour = atoi(str + 11); /* hours (0 - 23) */
t.tm_min = atoi(str + 14); /* minutes (0 - 59) */
t.tm_sec = atoi(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 mktime(&t);
return t;
}
const char * Db::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;
}
Error Db::GetThreadByDirId(long dir_id, Thread & thread)
{
PGresult * r = 0;
Error status = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "select id, dir_id, subject, closed from core.thread where thread.dir_id = " << dir_id << ";";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows > 1 )
log << log1 << "Db: there is more than one thread with dir_id: " << dir_id << logend;
else
if( rows == 0 )
{
log << log1 << "Db: there is no a thread with dir_id: " << dir_id << logend;
throw Error(Error::no_thread);
}
int cid = AssertColumn(r, "id");
int cdir_id = AssertColumn(r, "dir_id");
int csubject = AssertColumn(r, "subject");
int cclosed = AssertColumn(r, "closed");
thread.id = atol( AssertValue(r, 0, cid) );
thread.dir_id = atol( AssertValue(r, 0, cdir_id) );
thread.subject = AssertValue(r, 0, csubject);
thread.closed = atol( AssertValue(r, 0, cclosed) ) == 0 ? false : true;
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::AddThread(Thread & thread)
{
PGresult * r = 0;
Error status = Error::ok;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.thread (dir_id, subject, closed) values (";
query << '\'' << thread.dir_id << "', ";
query << '\'' << Escape(thread.subject) << "', ";
query << '\'' << (thread.closed ? 1 : 0 ) << "'); ";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
thread.id = AssertCurrval("core.thread_id_seq");
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}