/* * 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( 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( 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(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(item.type) << "', "; query << '\'' << item.parent_id << "', "; query << '\'' << item.content_id << "', "; query << '\'' << static_cast(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(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(item.type) << "', "; query << '\'' << item.default_item << "', "; query << '\'' << item.parent_id << "', "; query << '\'' << Escape(item.subject) << "', "; query << '\'' << Escape(item.guest_name) << "', "; query << '\'' << static_cast(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(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(iq.type) << "'" ; if_and = add_and; } if( iq.where_auth ) { query << if_and << "auth"; if(iq.auth_equal ) query << "="; else query << "!="; query << "'" << static_cast(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_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 & 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 & 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(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 & 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::Iterator iter = user_tab.End(); for(int i = 0 ; i( 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_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::Iterator iter; for(int i = 0 ; imembers.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(""); /* 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_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 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_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 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; }