winix/core/db.cpp

2169 lines
44 KiB
C++
Executable File

/*
* This file is a part of Winix
* and is not publicly distributed
*
* Copyright (c) 2008-2010, 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(WINIX_ERR_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(WINIX_ERR_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(WINIX_ERR_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(WINIX_ERR_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(WINIX_ERR_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(WINIX_ERR_DB_INCORRECT_LOGIN);
if( rows > 1 )
{
log << log1 << "Db: there is more than one user: " << login << " (with the same password)" << logend;
throw Error(WINIX_ERR_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;
}
Error Db::AddUser(User & user, const std::string & password)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.user (login, password, super_user, email, cms_notify, thread_notify) values (";
query << '\'' << Escape(user.name) << "', ";
query << '\'' << Escape(password) << "', ";
query << '\'' << static_cast<int>(user.super_user) << "', ";
query << '\'' << Escape(user.email) << "', ";
query << '\'' << user.cms_notify << "', ";
query << '\'' << user.thread_notify << "');";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
user.id = AssertCurrval("core.user_id_seq");
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
//!! wywalic z nazwy 'Subject' nic nie jest robione z tytulem
// ta metoda uzywana tez jest w EditParentUrlById()
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;
// !! subject zostal wrzucony do tabeli item
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(WINIX_ERR_DB_ERR_CURRVAL);
}
long res = strtol( AssertValue(r, 0, 0), 0, 10 );
return res;
}
Error Db::AddItemIntoItem(Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.item (user_id, modification_user_id, group_id, privileges, date_creation, date_modification, type, "
"parent_id, content_id, auth, auth_path, default_item, subject, guest_name, url) values (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.modification_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 << '\'' << static_cast<int>(item.auth) << "', ";
query << '\'' << Escape(item.auth_path) << "', ";
query << '\'' << item.default_item << "', ";
query << '\'' << Escape(item.subject) << "', ";
query << '\'' << Escape(item.guest_name) << "', ";
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 = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.content (content, content_type) values (";
query << '\'' << Escape(item.content) << "', ";
query << '\'' << static_cast<int>(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 = WINIX_ERR_OK;
if( item.type == Item::file )
result = AddItemIntoContent(item);
else
item.content_id = -1;
if( result == WINIX_ERR_OK )
result = AddItemIntoItem(item);
return result;
}
Error Db::EditItemInItem(Item & item, bool with_url)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (user_id, modification_user_id, group_id, privileges, date_creation, date_modification, type, "
"default_item, parent_id, subject, guest_name, auth, auth_path";
if( with_url )
query << ", url";
query << ") = (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.modification_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 << "', ";
query << '\'' << Escape(item.subject) << "', ";
query << '\'' << Escape(item.guest_name) << "', ";
query << '\'' << static_cast<int>(item.auth) << "', ";
query << '\'' << Escape(item.auth_path) << "' ";
if( with_url )
{
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_url && !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 = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.content set (content, content_type) = (";
query << '\'' << Escape(item.content) << "', ";
query << '\'' << static_cast<int>(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 = WINIX_ERR_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(WINIX_ERR_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 = WINIX_ERR_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(WINIX_ERR_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
// !! moze nazwa poprostu EditItem (nie trzeba tego ById) ? (sprawdzic czy nie koliduje z inna nazwa)
Error Db::EditItemById(Item & item, bool with_url)
{
Error result = WINIX_ERR_OK;
// !! dla katalogow nie testowane jeszcze
if( item.type == Item::file )
result = EditItemGetContentId(item);
if( result == WINIX_ERR_OK )
{
if( item.type == Item::file )
result = EditItemInContent(item);
if( result == WINIX_ERR_OK )
result = EditItemInItem(item, with_url);
}
return result;
}
// item.url and item.parent_id must be set
// doesn't work with directiories
Error Db::EditItemByUrl(Item & item, bool with_url)
{
Error result = EditItemGetId(item);
if( result == WINIX_ERR_OK )
{
result = EditItemInContent(item);
if( result == WINIX_ERR_OK )
result = EditItemInItem(item, with_url);
}
return result;
}
Error Db::EditDefaultItem(long id, long new_default_item)
{
PGresult * r = 0;
Error result = WINIX_ERR_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 = WINIX_ERR_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(const ItemQuery & iq, bool skip_other_sel)
{
std::ostringstream query;
query << "select item.id";
if( !skip_other_sel )
{
if( iq.sel_parent_id ) query << " ,parent_id";
if( iq.sel_user_id ) query << " ,user_id, modification_user_id";
if( iq.sel_group_id ) query << " ,group_id";
if( iq.sel_guest_name) query << " ,guest_name";
if( iq.sel_privileges ) query << " ,privileges";
if( iq.sel_date ) query << " ,date_creation, date_modification";
if( iq.sel_subject ) query << " ,subject";
if( iq.sel_content ) query << " ,content, content_type, content_id";
if( iq.sel_url ) query << " ,url";
if( iq.sel_type ) query << " ,type";
if( iq.sel_default_item ) query << " ,default_item";
if( iq.sel_auth ) query << " ,auth, auth_path";
}
query << " from core.item";
if( iq.sel_content ) query << " left join core.content on item.content_id = content.id";
if( iq.where_id || iq.where_parent_id || iq.where_type || iq.where_auth )
{
query << " where ";
const char * add_and = " and ";
const char * if_and = "";
if( iq.where_id ) { query << if_and << "id='" << iq.id << "'" ; if_and = add_and; }
if( iq.where_parent_id ) { query << if_and << "parent_id='" << iq.parent_id << "'" ; if_and = add_and; }
if( iq.where_type ) { query << if_and << "type='" << static_cast<int>(iq.type) << "'" ; if_and = add_and; }
if( iq.where_auth )
{
query << if_and << "auth";
if(iq.auth_equal )
query << "=";
else
query << "!=";
query << "'" << static_cast<int>(iq.auth) << "'";
if_and = add_and;
}
}
query << " order by item.date_creation";
if( iq.sort_asc )
query << " asc;";
else
query << " desc;";
return AssertQuery(query.str());
}
void Db::GetItems(std::vector<Item> & item_tab, const ItemQuery & item_query)
{
item_tab.clear();
PGresult * r = 0;
try
{
AssertConnection();
r = GetItemsQuery(item_query);
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_tab.push_back(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetItems(std::vector<long> & item_tab, const ItemQuery & item_query)
{
item_tab.clear();
PGresult * r = 0;
try
{
AssertConnection();
r = GetItemsQuery(item_query, true);
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
for(int i = 0 ; i<rows ; ++i)
{
long id = atol( AssertValue(r, i, 0) );
item_tab.push_back(id);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
// how many items there are in a 'parent_id' directory
long Db::Size(long parent_id, Item::Type type)
{
PGresult * r = 0;
int res = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select count(id) from core.item where ";
if( type != Item::none )
query << "type='" << (int)type << "' and ";
query << "parent_id='" << parent_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
res = atol(AssertValue(r, 0, 0));
}
catch(const Error &)
{
}
ClearResult(r);
return res;
}
// !! zamienic nazwe na GetFile?
// !! cos tu pomyslec innego, ta metoda nie musi pobierac tablicy za argument
// i tak istnieje tylko jedna pozycja o okreslonym id
// mozna zwracac bool i pobierac referencje na item
void Db::GetItem(std::vector<Item> & item_tab, 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_tab.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 = WINIX_ERR_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(WINIX_ERR_NO_ITEM);
ItemColumns col;
col.SetColumns(r);
col.SetItem(r, 0, item);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::GetItemById(long item_id, Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item left join core.content on item.content_id = content.id where item.id='" << item_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows == 0 )
throw Error(WINIX_ERR_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, modification_user_id, group_id, privileges, guest_name from core.item"
<< " where item.id='" << id << "';";
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 = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (user_id, modification_user_id, group_id, privileges, guest_name) = (";
query << '\'' << item.user_id << "', ";
query << '\'' << item.modification_user_id << "', ";
query << '\'' << item.group_id << "', ";
query << '\'' << item.privileges << "', ";
query << '\'' << Escape(item.guest_name) << "' ";
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::EditParentUrlById(Item & item, long id)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (parent_id, url) = (";
query << '\'' << item.parent_id << "', ";
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << '\'' << Escape(item.url) << "'";
else
query << '\'' << item.id << "'"; ;
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::EditAuthById(Item & item, long id)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (auth, auth_path) = (";
query << '\'' << static_cast<int>(item.auth) << "', ";
query << '\'' << Escape(item.auth_path) << "') ";
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 = WINIX_ERR_OK;
PGresult * r = 0;
const char * crows;
try
{
AssertConnection();
std::ostringstream query, query2;
// !! trzeba poprawic to usuwanie gdy beda hard linki
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;
}
Error Db::EditSubjectById(Item & item, long id)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (subject) = (";
query << '\'' << Escape(item.subject) << "') ";
query << " where id='" << id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
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 = WINIX_ERR_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) );
}
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 == WINIX_ERR_OK && contents == 1 )
DelItemDelContent(item);
return DelItemDelItem(item);
}
void Db::GetDirs(DirContainer & dir_tab)
{
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_tab.PushBack( item );
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetUsers(UGContainer<User> & user_tab)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select id, login, super_user, group_id, email, cms_notify, thread_notify 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");
int cemail = AssertColumn(r, "email");
int ccms_notify = AssertColumn(r, "cms_notify");
int cthread_notify = AssertColumn(r, "thread_notify");
User u;
long last_id = -1;
UGContainer<User>::Iterator iter = user_tab.End();
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) ) );
u.email = AssertValue(r, i, cemail);
u.cms_notify = atoi( AssertValue(r, i, ccms_notify) );
u.thread_notify = atoi( AssertValue(r, i, cthread_notify) );
log << log1 << "Db: get user: id:" << u.id << ", name:" << u.name << ", super_user:" << u.super_user << logend;
iter = user_tab.PushBack( u );
if( iter == user_tab.End() )
log << log1 << "Db: can't add a user: " << u.name << logend;
last_id = u.id;
}
long group_id = atol( AssertValue(r, i, cgroup_id) );
if( !PQgetisnull(r, i, cgroup_id) && group_id!=-1 && iter!=user_tab.End() )
{
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_tab)
{
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_tab.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_tab.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 = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "select thread.id, thread.parent_id, thread.dir_id, thread.closed, thread.items, "
"thread.last_item, item.date_modification, item.user_id "
"from core.thread left join core.item on thread.last_item = item.id "
"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 )
throw Error(WINIX_ERR_NO_THREAD);
int cid = AssertColumn(r, "id");
int cparent_id = AssertColumn(r, "parent_id");
int cdir_id = AssertColumn(r, "dir_id");
int cclosed = AssertColumn(r, "closed");
int citems = AssertColumn(r, "items");
int clast_item = AssertColumn(r, "last_item");
int cdate_modification = PQfnumber(r, "date_modification");
int cuser_id = PQfnumber(r, "user_id");
thread.id = atol( AssertValue(r, 0, cid) );
thread.parent_id = atol( AssertValue(r, 0, cparent_id) );
thread.dir_id = atol( AssertValue(r, 0, cdir_id) );
thread.closed = atol( AssertValue(r, 0, cclosed) ) == 0 ? false : true;
thread.items = atol( AssertValue(r, 0, citems) );
thread.last_item.id = atol( AssertValue(r, 0, clast_item) );
thread.last_item.date_modification = ConvertTime( Db::AssertValue(r, 0, cdate_modification) );
thread.last_item.user_id = atol( Db::AssertValue(r, 0, cuser_id) );
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::GetThreads(long parent_id, std::vector<Thread> & thread_tab)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "select thread.id, thread.parent_id, thread.dir_id, thread.closed, thread.items, thread.last_item, item.date_modification, item.user_id, item.guest_name "
"from core.thread left join core.item on thread.last_item = item.id "
"where thread.parent_id = '" << parent_id << "' order by date_modification asc;";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
Thread thread;
int cid = AssertColumn(r, "id");
int cparent_id = AssertColumn(r, "parent_id");
int cdir_id = AssertColumn(r, "dir_id");
int cclosed = AssertColumn(r, "closed");
int citems = AssertColumn(r, "items");
int clast_item = AssertColumn(r, "last_item");
int cdate_modification = PQfnumber(r, "date_modification"); // !! czemu tutaj jest pqfnumber zamiast assertcolumn?
int cuser_id = PQfnumber(r, "user_id");
int cguest_name = PQfnumber(r, "guest_name");
for(int i=0 ; i<rows ; ++i)
{
thread.id = atol( AssertValue(r, i, cid) );
thread.parent_id = atol( AssertValue(r, i, cparent_id) );
thread.dir_id = atol( AssertValue(r, i, cdir_id) );
thread.closed = atol( AssertValue(r, i, cclosed) ) == 0 ? false : true;
thread.items = atol( AssertValue(r, i, citems) );
thread.last_item.id = atol( AssertValue(r, i, clast_item) );
thread.last_item.date_modification = ConvertTime( Db::AssertValue(r, i, cdate_modification) );
thread.last_item.user_id = atol( Db::AssertValue(r, i, cuser_id) );
thread.last_item.guest_name = Db::AssertValue(r, i, cguest_name);
thread_tab.push_back(thread);
}
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::AddThread(Thread & thread)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.thread (parent_id, dir_id, closed, items, last_item) values (";
query << '\'' << thread.parent_id << "', ";
query << '\'' << thread.dir_id << "', ";
query << '\'' << (thread.closed ? 1 : 0 ) << "', ";
query << '\'' << thread.items << "', ";
query << '\'' << thread.last_item.id << "'); ";
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;
}
Error Db::EditThreadAddItem(long dir_id, long item_id)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.thread set (last_item, items) = ('" << item_id << "', items+1) where dir_id='" << dir_id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::EditThreadRemoveItem(long dir_id)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query, query2;
long last_item_id = -1;
query << "select id from core.item where parent_id='" << dir_id << "' order by date_creation desc limit 1;";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) == 1 )
last_item_id = atol( AssertValue(r, 0, 0) );
ClearResult(r);
query2 << "update core.thread set (items, last_item) = (items-1,'" << last_item_id
<< "') where dir_id='" << dir_id << "';";
r = AssertQuery(query2.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::RemoveThread(long dir_id)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "delete from core.thread where dir_id='" << dir_id << "';";
const char * crows = PQcmdTuples(r);
long rows = 0;
if( crows )
rows = atol(crows);
if( rows > 0 )
log << log2 << "Db: deleted " << rows << " rows from core.thread" << logend;
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::GetTicketByDirId(long dir_id, Ticket & ticket)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "select ticket.id, ticket.dir_id, ticket.parent_id, ticket.type, ticket.status, ticket.priority, "
"ticket.category, ticket.expected, ticket.progress, ticket.item_id "
"from core.ticket "
"where ticket.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 ticket with dir_id: " << dir_id << logend;
else
if( rows == 0 )
throw Error(WINIX_ERR_NO_TICKET);
TicketColumns tc;
tc.SetColumns(r);
tc.SetTicket(r, 0, ticket);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::GetTickets(long parent_id, std::vector<Ticket> & ticket_tab)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "select ticket.id, ticket.dir_id, ticket.parent_id, ticket.type, ticket.status, ticket.priority, "
"ticket.category, ticket.expected, ticket.progress, ticket.item_id "
"from core.ticket "
"where ticket.parent_id = '" << parent_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
Ticket ticket;
TicketColumns tc;
tc.SetColumns(r);
for(int i=0 ; i<rows ; ++i)
{
tc.SetTicket(r, i, ticket);
ticket_tab.push_back(ticket);
}
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
/*
bool Db::IsTicket(long dir_id)
{
PGresult * r = 0;
bool is_ticket = false;
try
{
AssertConnection();
std::ostringstream query;
query << "select ticket.id from core.ticket "
"where ticket.dir_id = '" << dir_id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
is_ticket = (PQntuples(r) == 1);
}
catch(const Error &)
{
}
ClearResult(r);
return is_ticket;
}
*/
Error Db::AddTicket(Ticket & ticket)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.ticket (dir_id, parent_id, type, status, priority, category, expected, progress, item_id) values (";
query << '\'' << ticket.dir_id << "', ";
query << '\'' << ticket.parent_id << "', ";
query << '\'' << ticket.type << "', ";
query << '\'' << ticket.status << "', ";
query << '\'' << ticket.priority << "', ";
query << '\'' << ticket.category << "', ";
query << '\'' << ticket.expected << "', ";
query << '\'' << ticket.progress << "', ";
query << '\'' << ticket.item_id << "');";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
ticket.id = AssertCurrval("core.ticket_id_seq");
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::EditTicketById(Ticket & ticket)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.ticket set (dir_id, parent_id, type, status, priority, category, expected, progress, item_id) = (";
query << '\'' << ticket.dir_id << "', ";
query << '\'' << ticket.parent_id << "', ";
query << '\'' << ticket.type << "', ";
query << '\'' << ticket.status << "', ";
query << '\'' << ticket.priority << "', ";
query << '\'' << ticket.category << "', ";
query << '\'' << ticket.expected << "', ";
query << '\'' << ticket.progress << "', ";
query << '\'' << ticket.item_id << "') ";
query << "where id='" << ticket.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::EditTicketRemoveItem(long item_id)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.ticket set item_id = '-1' where item_id='" << item_id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}
Error Db::RemoveTicket(long dir_id)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
AssertConnection();
std::ostringstream query;
query << "delete from core.ticket where dir_id='" << dir_id << "';";
const char * crows = PQcmdTuples(r);
long rows = 0;
if( crows )
rows = atol(crows);
if( rows > 0 )
log << log2 << "Db: deleted " << rows << " rows from core.ticket" << logend;
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}