winix/db/db.cpp

1700 lines
32 KiB
C++
Executable File
Raw Blame History

/*
* 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 "core/log.h"
#include "core/misc.h"
bool Db::CheckUser(const std::wstring & login, const std::wstring & password, long & user_id)
{
PGresult * r = 0;
bool user_ok = false;
try
{
query.Clear();
query << R("select id from core.user where login=")
<< login
<< R(" and password=")
<< password
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(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");
user_id = AssertValueLong(r, 0, cuser_id);
user_ok = true;
}
catch(const Error &)
{
}
ClearResult(r);
return user_ok;
}
Error Db::AddUser(User & user, const std::wstring & password)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
query.Clear();
query << R("insert into core.user (login, password, super_user, email, notify) values (")
<< user.name
<< password
<< static_cast<int>(user.super_user)
<< user.email
<< user.notify
<< R(");");
r = AssertQuery(query);
AssertResult(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 = 99;
wchar_t appendix[20];
size_t appendix_len = sizeof(appendix) / sizeof(wchar_t);
appendix[0] = 0;
try
{
do
{
query_create_url.Clear();
temp_url = item.url;
temp_url += appendix;
query_create_url
<< R("select id from core.item where url=")
<< temp_url
<< R(" and parent_id=")
<< item.parent_id
<< R(";");
r = AssertQuery(query_create_url);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) != 0 )
{
swprintf(appendix, appendix_len, L"_(%d)", ++index);
is_that_url = true;
}
else
{
item.url = temp_url;
is_that_url = false;
}
ClearResult(r);
r = 0;
}
while( is_that_url && index <= max_index );
}
catch(const Error &)
{
is_that_url = true; // for returning false
}
ClearResult(r);
return !is_that_url;
}
// for testing consistency
// !! obecnie nie potrzebne? skasowac?
void Db::CheckAllUrlSubjectModifyItem(Item & item)
{
PGresult * r = 0;
try
{
query.Clear();
query << R("update core.item set url=");
// url
if( AddItemCreateUrlSubject(item) )
query << item.url;
else
{
query << item.id;
item.url.clear();
}
query << R(" where id=") << item.id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
}
catch(const Error &)
{
}
ClearResult(r);
}
// for checking consistency
// !! skasowac? nie potrzebne?
void Db::CheckAllUrlSubject()
{
PGresult * r = 0;
Item item;
try
{
// !! subject zostal wrzucony do tabeli item
query << R("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);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(r);
int cid = AssertColumn(r, "id");
int csubject = AssertColumn(r, "subject");
for(int i = 0 ; i<rows ; ++i)
{
item.id = AssertValueLong(r, i, cid);
item.subject = AssertValueWide(r, i, csubject);
CheckAllUrlSubjectModifyItem(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
Error Db::AddItemIntoItem(Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
bool url_without_id = false;
try
{
query.Clear();
query << R("insert into core.item (user_id, modification_user_id, group_id, privileges, "
"date_creation, date_modification, type, parent_id, content_id, file_path, file_fs, file_type, "
"has_thumb, default_item, subject, guest_name, template, url) values (")
<< item.user_id
<< item.modification_user_id
<< item.group_id
<< item.privileges
<< item.date_creation
<< item.date_modification
<< static_cast<int>(item.type)
<< item.parent_id
<< item.content_id
<< item.file_path
<< item.file_fs
<< item.file_type
<< static_cast<int>(item.has_thumb)
<< item.default_item
<< item.subject
<< item.guest_name
<< item.html_template;
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << item.url;
else
query << R(", currval('core.item_id_seq')"); // !! zrobic test czy to obecnie dziala dobrze
query << R(");");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
item.id = AssertCurrval("core.item_id_seq");
if( !url_without_id )
Toa(item.id, item.url);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::AddItemIntoContent(Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("insert into core.content (content, content_type) values (")
<< item.content
<< static_cast<int>(item.content_type)
<< R(");");
r = AssertQuery(query);
AssertResult(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
{
query.Clear();
query << R("update core.item set (user_id, modification_user_id, group_id, privileges, "
"date_creation, date_modification, type, default_item, parent_id, subject, "
"guest_name, file_path, file_fs, file_type, has_thumb, template");
if( with_url )
query << R(", url");
query << R(") = (")
<< item.user_id
<< item.modification_user_id
<< item.group_id
<< item.privileges
<< item.date_creation
<< item.date_modification
<< static_cast<int>(item.type)
<< item.default_item
<< item.parent_id
<< item.subject
<< item.guest_name
<< item.file_path
<< item.file_fs
<< item.file_type
<< static_cast<int>(item.has_thumb)
<< item.html_template;
if( with_url )
{
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << item.url;
else
query << item.id;
}
query << R(") where id=") << item.id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
if( with_url && !url_without_id )
Toa(item.id, item.url);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditItemInContent(Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("update core.content set (content, content_type) = (")
<< item.content
<< static_cast<int>(item.content_type)
<< R(") where id=")
<< item.content_id
<< R(";");
r = AssertQuery(query);
AssertResult(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
{
query.Clear();
query << R("select item.id, content.id from core.item left join core.content"
" on item.content_id = content.id where item.parent_id=")
<< item.parent_id
<< R(" and item.url=")
<< item.url
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) != 1 || Cols(r) != 2 )
throw Error(WINIX_ERR_NO_ITEM);
// we cannot use AssertColumn() with a name because both columns are called 'id'
item.id = AssertValueLong(r, 0, 0);
item.content_id = AssertValueLong(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
{
query.Clear();
// !! tutaj chyba nie ma potrzeby robic left join z core.content (nie uzywamy nic z tamtej tabeli)
query << R("select content_id from core.item left join core.content"
" on item.content_id = content.id where item.id=")
<< item.id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) != 1 || Cols(r) != 1 )
throw Error(WINIX_ERR_NO_ITEM);
item.content_id = AssertValueLong(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
{
query.Clear();
query << R("update core.item set (default_item) = (")
<< new_default_item
<< R(") where id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
if( AffectedRows(r) == 0 )
{
result = WINIX_ERR_NO_ITEM;
log << log1 << "Db: EditDefaultItem: no such an item, id: " << id << logend;
}
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditTemplateItemById(long id, const std::wstring & new_html_template)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("update core.item set (template) = (")
<< new_html_template
<< R(") where id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
if( AffectedRows(r) == 0 )
{
result = WINIX_ERR_NO_ITEM;
log << log1 << "Db: EditTemplateItemById: no such an item, id: " << id << logend;
}
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
PGresult * Db::GetItemsQuery(const DbItemQuery & iq, bool skip_other_sel)
{
query.Clear();
query << R("select item.id");
if( !skip_other_sel )
{
if( iq.sel_parent_id ) query << R(" ,parent_id");
if( iq.sel_user_id ) query << R(" ,user_id, modification_user_id");
if( iq.sel_group_id ) query << R(" ,group_id");
if( iq.sel_guest_name) query << R(" ,guest_name");
if( iq.sel_privileges ) query << R(" ,privileges");
if( iq.sel_date ) query << R(" ,date_creation, date_modification");
if( iq.sel_subject ) query << R(" ,subject");
if( iq.sel_content ) query << R(" ,content, content_type, content_id");
if( iq.sel_url ) query << R(" ,url");
if( iq.sel_type ) query << R(" ,type");
if( iq.sel_default_item ) query << R(" ,default_item");
if( iq.sel_file ) query << R(" ,file_path, file_fs, file_type, has_thumb");
if( iq.sel_html_template ) query << R(" ,template");
}
query << R(" from core.item");
if( iq.sel_content )
query << R(" left join core.content on item.content_id = content.id");
if( iq.where_id || iq.where_parent_id || iq.where_type || iq.where_file_type )
{
query << R(" where ");
const char * add_and = " and ";
const char * if_and = "";
if( iq.where_id ) { query << R(if_and) << R("id=") << iq.id ; if_and = add_and; }
if( iq.where_parent_id ){ query << R(if_and) << R("parent_id=") << iq.parent_id ; if_and = add_and; }
if( iq.where_type ) { query << R(if_and) << R("type=") << int(iq.type) ; if_and = add_and; }
if( iq.where_file_type )
{
query << R(if_and) << R("file_type");
if( iq.file_type_equal )
query << R("=");
else
query << R("!=");
query << iq.file_type;
if_and = add_and;
}
}
query << R(" order by item.date_creation");
if( iq.sort_asc )
query << R(" asc");
else
query << R(" desc");
if( iq.limit != 0 )
query << R(" limit ") << iq.limit;
if( iq.offset != 0 )
query << R(" offset ") << iq.offset;
query << R(";");
return AssertQuery(query);
}
void Db::GetItems(std::vector<Item> & item_tab, const DbItemQuery & item_query)
{
item_tab.clear();
PGresult * r = 0;
try
{
r = GetItemsQuery(item_query);
AssertResult(r, PGRES_TUPLES_OK);
Item item;
int rows = Rows(r);
DbItemColumns 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 DbItemQuery & item_query)
{
item_tab.clear();
PGresult * r = 0;
try
{
r = GetItemsQuery(item_query, true);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(r);
for(int i = 0 ; i<rows ; ++i)
{
long id = AssertValueLong(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;
long res = 0;
try
{
query.Clear();
query << R("select count(id) from core.item where ");
if( type != Item::none )
query << R("type=") << (int)type << R(" and ");
query << R("parent_id=") << parent_id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
res = AssertValueLong(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
{
query.Clear();
query << R("select * from core.item left join core.content on item.content_id = content.id"
" where type=1 and item.id=") << id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
Item item;
int rows = Rows(r);
if( rows > 1 )
log << log1 << "Db: we have more than one item with id: " << id << logend;
DbItemColumns 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::wstring & url, Item & item)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("select * from core.item left join core.content on item.content_id = content.id"
" where type=1 and item.parent_id=")
<< parent_id
<< R(" and item.url=")
<< url
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) == 0 )
throw Error(WINIX_ERR_NO_ITEM);
DbItemColumns 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
{
query.Clear();
query << R("select * from core.item left join core.content on item.content_id = content.id"
" where item.id=") << item_id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) == 0 )
throw Error(WINIX_ERR_NO_ITEM);
DbItemColumns 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::wstring & url, Item::Type type)
{
PGresult * r = 0;
long result = -1;
try
{
query.Clear();
query << R("select id from core.item where type=")
<< static_cast<int>(type)
<< R(" and item.parent_id=")
<< parent_id
<< R(" and item.url=")
<< url
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) == 1 )
result = AssertValueLong(r, 0, 0);
}
catch(const Error & e)
{
}
ClearResult(r);
return result;
}
long Db::GetFileId(long parent_id, const std::wstring & url)
{
return GetItemId(parent_id, url, Item::file);
}
long Db::GetDirId(long parent_id, const std::wstring & url)
{
return GetItemId(parent_id, url, Item::dir);
}
bool Db::GetPriv(Item & item, long id)
{
bool result = false;
PGresult * r = 0;
try
{
query.Clear();
query << R("select user_id, modification_user_id, group_id, privileges, guest_name from core.item"
" where item.id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) != 1 )
throw Error();
DbItemColumns 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
{
query.Clear();
query << R("update core.item set (user_id, modification_user_id, group_id, privileges, guest_name) = (")
<< item.user_id
<< item.modification_user_id
<< item.group_id
<< item.privileges
<< item.guest_name
<< R(") where id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(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
{
query.Clear();
query << R("update core.item set (parent_id, url) = (") << item.parent_id;
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
query << item.url;
else
query << item.id;
query << R(") where id=") << id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditFileById(const Item & item, long id)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("update core.item set (file_path, file_fs, file_type, has_thumb) = (")
<< item.file_path
<< item.file_fs
<< item.file_type
<< static_cast<int>(item.has_thumb)
<< R(") where id=") << id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditHasThumbById(bool has_thumb, long id)
{
PGresult * r = 0;
Error result = WINIX_ERR_OK;
try
{
query.Clear();
query << R("update core.item set (has_thumb) = (")
<< static_cast<int>(has_thumb)
<< R(") where id=") << id << R(";");
r = AssertQuery(query);
AssertResult(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;
try
{
query.Clear();
// !! trzeba poprawic to usuwanie gdy beda hard linki
query << R("delete from core.content where content.id in (select content_id from core.item where parent_id=")
<< id
<< R(");");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
log << log2 << "Db: deleted " << AffectedRows(r) << " rows from core.content" << logend;
// !! ClearResult(r) tutaj?
query.Clear();
query << R("delete from core.item where id=")
<< id
<< R(" or parent_id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
log << log1 << "Db: deleted dir: " << id << " (deleted: " << AffectedRows(r) << " 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
{
query.Clear();
query << R("update core.item set (subject) = (")
<< item.subject
<< R(") where id=")
<< id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
bool Db::DelItemDelItem(const Item & item)
{
long affected = 0;
PGresult * r = 0;
try
{
query.Clear();
query << R("delete from core.item where id=")
<< item.id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
affected = AffectedRows(r);
if( affected > 1 )
log << log1 << "Db: more than one item were deleted" << logend;
else
if( affected == 0 )
log << log1 << "Db: no item has been deleted" << logend;
}
catch(const Error &)
{
}
ClearResult(r);
return affected != 0;
}
void Db::DelItemDelContent(const Item & item)
{
PGresult * r = 0;
try
{
query.Clear();
query << R("delete from core.content where id=")
<< item.content_id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
long rows = AffectedRows(r);
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
{
query.Clear();
query << R("select count('id') from core.item where content_id=")
<< item.content_id
<< R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
contents = AssertValueLong(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
{
query.Clear();
query << R("select * from core.item where type=0;");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
Item item; // !! wrzucic jako skladowa klasy? a tutaj tylko dac item.clear()?
int rows = Rows(r);
DbItemColumns 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
{
query.Clear();
query << R("select id, login, super_user, group_id, email, 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);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(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 cnotify = AssertColumn(r, "notify");
User u;
long last_id = -1;
UGContainer<User>::Iterator iter = user_tab.End();
for(int i = 0 ; i<rows ; ++i)
{
u.id = AssertValueLong(r, i, cid);
if( u.id != last_id )
{
u.name = AssertValueWide(r, i, cname);
u.super_user = static_cast<bool>(AssertValueInt(r, i, csuper_user));
u.email = AssertValueWide(r, i, cemail);
u.notify = AssertValueInt(r, i, cnotify);
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 = AssertValueLong(r, i, cgroup_id);
if( !IsNull(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
{
query.Clear();
query << R("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);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(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 = AssertValueLong(r, i, cid);
if( g.id != last_id )
{
g.name = AssertValueWide(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 = AssertValueLong(r, i, cuser_id);
if( !IsNull(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);
}
/*
threads
*/
Error Db::GetThreadByDirId(long dir_id, Thread & thread)
{
PGresult * r = 0;
Error status = WINIX_ERR_OK;
try
{
query.Clear();
query << R("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(";");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(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"); // !! tych kolumn mo<6D>e nie by<62>? czemu PQfnumber a nie AssertColumn?
int cuser_id = PQfnumber(r, "user_id");
thread.id = AssertValueLong(r, 0, cid);
thread.parent_id = AssertValueLong(r, 0, cparent_id);
thread.dir_id = AssertValueLong(r, 0, cdir_id);
thread.closed = AssertValueLong(r, 0, cclosed) == 0 ? false : true;
thread.items = AssertValueLong(r, 0, citems);
thread.last_item.id = AssertValueLong(r, 0, clast_item);
thread.last_item.date_modification = AssertValueTm(r, 0, cdate_modification);
thread.last_item.user_id = AssertValueLong(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
{
query.Clear();
query << R("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 << R(" order by date_modification asc;");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
int rows = Rows(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 = AssertValueLong(r, i, cid);
thread.parent_id = AssertValueLong(r, i, cparent_id);
thread.dir_id = AssertValueLong(r, i, cdir_id);
thread.closed = AssertValueLong(r, i, cclosed) == 0 ? false : true;
thread.items = AssertValueLong(r, i, citems);
thread.last_item.id = AssertValueLong(r, i, clast_item);
thread.last_item.date_modification = AssertValueTm(r, i, cdate_modification);
thread.last_item.user_id = AssertValueLong(r, i, cuser_id);
thread.last_item.guest_name = AssertValueWide(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
{
query.Clear();
query << R("insert into core.thread (parent_id, dir_id, closed, items, last_item) values (")
<< thread.parent_id
<< thread.dir_id
<< (thread.closed ? 1 : 0 )
<< thread.items
<< thread.last_item.id
<< R(");");
r = AssertQuery(query);
AssertResult(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
{
query.Clear();
query << R("update core.thread set (last_item, items) = (")
<< item_id
<< R(", items+1) where dir_id=")
<< dir_id
<< R(";");
r = AssertQuery(query);
AssertResult(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
{
long last_item_id = -1;
query.Clear();
query << R("select id from core.item where parent_id=")
<< dir_id
<< R(" order by date_creation desc limit 1;");
r = AssertQuery(query);
AssertResult(r, PGRES_TUPLES_OK);
if( Rows(r) == 1 )
last_item_id = AssertValueLong(r, 0, 0);
ClearResult(r);
query.Clear();
query << R("update core.thread set (items, last_item) = (items-1,")
<< last_item_id
<< R(") where dir_id=")
<< dir_id
<< R(";");
r = AssertQuery(query);
AssertResult(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
{
query.Clear();
query << R("delete from core.thread where dir_id=") << dir_id << R(";");
r = AssertQuery(query);
AssertResult(r, PGRES_COMMAND_OK);
long rows = AffectedRows(r);
if( rows > 0 )
log << log2 << "Db: deleted " << rows << " rows from core.thread" << logend;
}
catch(const Error & e)
{
status = e;
}
ClearResult(r);
return status;
}