669 lines
11 KiB
C++
Executable File
669 lines
11 KiB
C++
Executable File
/*
|
|
* 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<char*>( 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<char*>( 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<rows ; ++i)
|
|
{
|
|
item.id = atol( AssertValue(r, i, cid) );
|
|
item.subject = AssertValue(r, i, csubject);
|
|
|
|
CheckAllUrlSubjectModifyItem(item);
|
|
}
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
}
|
|
|
|
|
|
|
|
long Db::AssertCurrval(const char * table)
|
|
{
|
|
PGresult * r;
|
|
std::ostringstream query;
|
|
|
|
query << "select currval('" << table << "');";
|
|
|
|
r = AssertQuery(query.str());
|
|
AssertResultStatus(r, PGRES_TUPLES_OK);
|
|
|
|
if( PQntuples(r) != 1 )
|
|
{
|
|
log << log1 << "Db: error (currval) for table: " << table << ", " << PQerrorMessage(pg_conn) << logend;
|
|
throw Error(Error::db_err_currval);
|
|
}
|
|
|
|
long res = strtol( AssertValue(r, 0, 0), 0, 10 );
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
Error Db::AddItem(Item & item)
|
|
{
|
|
PGresult * r = 0;
|
|
Error result = Error::ok;
|
|
bool url_without_id = false;
|
|
|
|
try
|
|
{
|
|
AssertConnection();
|
|
std::ostringstream query;
|
|
query << "insert into core.item (subject, content, url_subject, type, parent_id) values (";
|
|
query << '\'' << Escape(item.subject) << "', ";
|
|
query << '\'' << Escape(item.content) << "', ";
|
|
|
|
// url_subject
|
|
url_without_id = AddItemCreateUrlSubject(item);
|
|
|
|
if( url_without_id )
|
|
{
|
|
query << '\'' << Escape(item.url_subject) << "', ";
|
|
}
|
|
else
|
|
{
|
|
query << "currval('core.item_id_seq')" << ", ";
|
|
}
|
|
|
|
|
|
query << '\'' << static_cast<int>(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<int>(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> & 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<rows ; ++i)
|
|
{
|
|
col.SetItem(r, i, item);
|
|
item_table.push_back(item);
|
|
}
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
}
|
|
|
|
|
|
|
|
void Db::GetItem(std::vector<Item> & 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<rows ; ++i)
|
|
{
|
|
col.SetItem(r, i, item);
|
|
item_table.push_back(item);
|
|
}
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
}
|
|
|
|
|
|
bool Db::DelItem(long id)
|
|
{
|
|
long rows = 0;
|
|
PGresult * r = 0;
|
|
|
|
try
|
|
{
|
|
AssertConnection();
|
|
|
|
std::ostringstream query;
|
|
query << "delete from core.item where type='1' and id='" << 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;
|
|
}
|
|
}
|
|
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<rows ; ++i)
|
|
{
|
|
col.SetItem(r, i, item);
|
|
dir_table.PushBack( item );
|
|
}
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|