/* * This file is a part of CMSLU -- Content Management System like Unix * and is not publicly distributed * * Copyright (c) 2008, Tomasz Sowa * All rights reserved. * */ #include "db.h" Db::Db() { pg_conn = 0; } Db::~Db() { Close(); } 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() { std::ostringstream buf; buf << "dbname=" << db_database << " user=" << db_user << " password=" << db_pass; pg_conn = PQconnectdb(buf.str().c_str()); if( !pg_conn ) log << log1 << "Db: Fatal error during connecting" << logend; 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() { if( !pg_conn ) { log << log1 << "Db: Trying to connect into the database..."; Connect(); if( !pg_conn ) // logend from Connect() throw Error(Error::db_fatal_error_during_connecting); log << log1 << "ok" << logend; } if( PQstatus(pg_conn) != CONNECTION_OK ) { log << log2 << "Db: connection into the database is lost, trying to recover..." << logend; PQreset(pg_conn); if( PQstatus(pg_conn) != CONNECTION_OK ) { log << log2 << "no" << logend; throw Error(Error::db_fatal_error_during_connecting); } else { log << log2 << "ok" << logend; } } } 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: " << PQerrorMessage(pg_conn) << logend; throw Error(Error::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(Error::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(Error::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(Error::db_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(Error::db_incorrect_login); if( rows > 1 ) { log << log1 << "Db: there is more than one user: " << login << " (with the same password)" << logend; throw Error(Error::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; } 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_subject='" << Escape(item.url_subject) << 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_subject += 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_subject="; // url_subject if( AddItemCreateUrlSubject(item) ) query << '\'' << Escape(item.url_subject) << '\''; else { query << '\'' << item.id << '\''; item.url_subject.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; query << "select id, subject from core.item where url_subject is null or url_subject=''"; 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 << ");"; r = AssertQuery(query.str()); AssertResultStatus(r, PGRES_COMMAND_OK); item.id = AssertCurrval("core.item_id_seq"); if( !url_without_id ) ToString(item.url_subject, item.id); } catch(const Error & e) { result = e; } ClearResult(r); return result; } Error Db::EditItem(Item & item, bool with_subject) { PGresult * r = 0; Error result = Error::ok; bool url_without_id = false; try { AssertConnection(); std::ostringstream query; query << "update core.item set ("; if( with_subject ) query << "subject, url_subject, "; query << "content, type, parent_id) = ("; if( with_subject ) { query << '\'' << Escape(item.subject) << "', "; // url_subject url_without_id = AddItemCreateUrlSubject(item); if( url_without_id ) { query << '\'' << Escape(item.url_subject) << "', "; } else { query << '\'' << item.id << "', "; } } query << '\'' << Escape(item.content) << "', "; query << '\'' << static_cast(item.type) << "', "; query << '\'' << item.parent_id << "' "; query << ") where id='" << item.id << "';"; r = AssertQuery(query.str()); AssertResultStatus(r, PGRES_COMMAND_OK); if( with_subject && !url_without_id ) ToString(item.url_subject, item.id); } catch(const Error & e) { result = e; } ClearResult(r); return result; } PGresult * Db::GetItemsQuery(Item & item_ref) { std::ostringstream query; query << "select * from core.item where type!='0' and parent_id='" << item_ref.parent_id << "'"; if( item_ref.id != -1 ) query << " and id='" << item_ref.id << "'"; if( !item_ref.url_subject.empty() ) query << " and url_subject='" << Escape(item_ref.url_subject) << "'"; query << ';'; return AssertQuery(query.str()); } void Db::GetItems(std::vector & item_table, Item & item_ref) { item_table.clear(); PGresult * r = 0; try { AssertConnection(); r = GetItemsQuery(item_ref); AssertResultStatus(r, PGRES_TUPLES_OK); Item item; int rows = PQntuples(r); ItemColumns col; col.SetColumns(r); for(int i = 0 ; i & item_table, long id) { PGresult * r = 0; try { AssertConnection(); std::ostringstream query; query << "select * from core.item where type='1' and 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 1 ) log << log1 << "Db: more than one item were deleted" << logend; } } catch(const Error &) { } ClearResult(r); return rows != 0; } void Db::GetDirs(DirContainer & dir_table) { 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