/* * 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; // only root dir may not have url if( item.parent_id != -1 && item.url.empty() ) item.url = L"empty"; 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; } 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, " "link_to, link_redirect, 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(item.type) << item.parent_id << item.content_id << item.link_to << item.link_redirect << 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; int first_ref = 1; try { query.Clear(); query << R("insert into core.content (content, content_type, file_path, file_fs, " "file_type, has_thumb, ref, modify_index) values (") << item.content << static_cast(item.content_type) << item.file_path << item.file_fs << item.file_type << static_cast(item.has_thumb) << first_ref << item.modify_index << 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) { BeginTrans(); 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 EndTrans(result); } Error Db::IncrementContentRef(long content_id) { query.Clear(); query << R("update core.content set (ref) = (ref + 1) where id = ") << content_id << R(";"); return DoCommand(query); } Error Db::DecrementContentRef(long content_id) { query.Clear(); query << R("update core.content set (ref) = (ref - 1) where id = ") << content_id << R(";"); return DoCommand(query); } // item.id must be set (it's used by GetContentId) Error Db::AddHardLink(Item & item) { if( item.type != Item::file || item.content_id == -1 ) return WINIX_ERR_FILE_EXPECTED; BeginTrans(); Error result = IncrementContentRef(item.content_id); if( result == WINIX_ERR_OK ) result = AddItemIntoItem(item); return EndTrans(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, link_to, link_redirect, parent_id, subject, " "guest_name, 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.link_to << item.link_redirect << item.parent_id << item.subject << item.guest_name << 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 { // we don't change 'ref' here query.Clear(); query << R("update core.content set (content, content_type, file_path, file_fs, " "file_type, has_thumb, modify_index) = (") << item.content << static_cast(item.content_type) << item.file_path << item.file_fs << item.file_type << static_cast(item.has_thumb) << item.modify_index << 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; } long Db::GetContentId(long item_id) { PGresult * r = 0; long result = -1; try { query.Clear(); query << R("select content_id from core.item where item.id=") << item_id << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_TUPLES_OK); if( Rows(r) == 1 && Cols(r) == 1 ) result = 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) { BeginTrans(); Error result = WINIX_ERR_OK; if( item.type == Item::file ) { item.content_id = GetContentId(item.id); result = EditItemInContent(item); } if( result == WINIX_ERR_OK ) result = EditItemInItem(item, with_url); return EndTrans(result); } Error Db::EditItemGetIdsByUrl(Item & item) { PGresult * r = 0; Error result = WINIX_ERR_OK; try { query.Clear(); query << R("select id, content_id from core.item where parent_id=") << item.parent_id << R(" and item.url=") << item.url << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_TUPLES_OK); if( Rows(r) != 1 ) throw Error(WINIX_ERR_NO_ITEM); int cid = AssertColumn(r, "id"); int cc_id = AssertColumn(r, "content_id"); item.id = AssertValueLong(r, 0, cid); item.content_id = AssertValueLong(r, 0, cc_id); } catch(const Error & e) { result = e; } ClearResult(r); return result; } // item.url and item.parent_id must be set Error Db::EditItemByUrl(Item & item, bool with_url) { BeginTrans(); Error result = EditItemGetIdsByUrl(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 EndTrans(result); } Error Db::EditLinkItem(long id, const std::wstring & link_to, int link_redirect) { PGresult * r = 0; Error result = WINIX_ERR_OK; try { query.Clear(); query << R("update core.item set (link_to, link_redirect) = (") << link_to << link_redirect << 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: EditLinkItem: 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, content_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, ref, modify_index"); if( iq.sel_url ) query << R(", url"); if( iq.sel_type ) query << R(", type"); if( iq.sel_link ) query << R(", link_to, link_redirect"); 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 || iq.sel_file || iq.where_file_type ) 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("item.id=") << iq.id ; if_and = add_and; } if( iq.id_tab && !iq.id_tab->empty() ) { CreateIdList(*iq.id_tab, iq_id_list); query << R(if_and) << R("item.id in ") << R(iq_id_list); 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"); if( iq.type_equal ) query << R("="); else query << R("!="); query << 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.has_thumb) << R(") where id=") << content_id << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); } catch(const Error & e) { result = e; } ClearResult(r); return EndTrans(result); } Error Db::EditHasThumbById(bool has_thumb, long id) { PGresult * r = 0; Error result = WINIX_ERR_OK; BeginTrans(); try { long content_id = GetContentId(id); if( content_id == -1 ) throw Error(WINIX_ERR_NO_ITEM); query.Clear(); query << R("update core.content set (has_thumb) = (") << static_cast(has_thumb) << R(") where id=") << content_id << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); } catch(const Error & e) { result = e; } ClearResult(r); return EndTrans(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; } Error Db::DelDirById(long id) { Error result = WINIX_ERR_OK; PGresult * r = 0; BeginTrans(); try { // decrementing ref in core.content query.Clear(); query << R("update core.content set (ref) = (ref - 1) where content.id in " "(select content_id from core.item where type=1 and parent_id=") << id << R(");"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); ClearResult(r); // deleting in core.content where ref is zero query.Clear(); query << R("delete from core.content where ref=0;"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); log << log2 << "Db: deleted " << AffectedRows(r) << " rows from core.content" << logend; ClearResult(r); // deleting from core.item 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 << log2 << "Db: deleted dir: " << id << " (deleted: " << AffectedRows(r) << " rows from core.item)" << logend; } catch(const Error & e) { result = e; } ClearResult(r); return EndTrans(result); } Error Db::DelItemDelItem(const Item & item) { PGresult * r = 0; Error result = WINIX_ERR_OK; try { query.Clear(); query << R("delete from core.item where id=") << item.id << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); log << log2 << "Db: deleted " << AffectedRows(r) << " from core.item" << logend; } catch(const Error & e) { result = e; } ClearResult(r); return result; } Error Db::DelItemDelContent(const Item & item) { PGresult * r = 0; Error result = WINIX_ERR_OK; try { result = DecrementContentRef(item.content_id); if( result == WINIX_ERR_OK ) { query.Clear(); query << R("delete from core.content where ref=0 and id=") << item.content_id << R(";"); r = AssertQuery(query); AssertResult(r, PGRES_COMMAND_OK); log << log2 << "Db: deleted " << AffectedRows(r) << " rows from core.content" << logend; } } catch(const Error & e) { result = e; } ClearResult(r); return result; } Error Db::DelItem(const Item & item) { Error result = WINIX_ERR_NO_ITEM; if( item.type == Item::file ) { BeginTrans(); result = DelItemDelContent(item); if( result == WINIX_ERR_OK ) result = DelItemDelItem(item); result = EndTrans(result); } else if( item.type == Item::symlink ) { result = DelItemDelItem(item); } else if( item.type == Item::dir ) { result = DelDirById(item.id); } return result; } 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); dir_temp.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 << log2 << "Db: 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); }