/* * 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(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(item.type) << item.parent_id << item.content_id << item.file_path << item.file_fs << item.file_type << static_cast(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(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(item.type) << item.default_item << item.parent_id << item.subject << item.guest_name << item.file_path << item.file_fs << item.file_type << static_cast(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(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_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 & 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 & 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(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(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 & 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::Iterator iter = user_tab.End(); for(int i = 0 ; i(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_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::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); } /* 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że nie być? 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_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 0 ) log << log2 << "Db: deleted " << rows << " rows from core.thread" << logend; } catch(const Error & e) { status = e; } ClearResult(r); return status; }