472 lines
8.7 KiB
C++
Executable File
472 lines
8.7 KiB
C++
Executable File
/*
|
|
* This file is a part of Winix
|
|
* and is not publicly distributed
|
|
*
|
|
* Copyright (c) 2010-2012, Tomasz Sowa
|
|
* All rights reserved.
|
|
*
|
|
*/
|
|
|
|
#include "tdb.h"
|
|
#include "core/log.h"
|
|
|
|
|
|
|
|
namespace Thread
|
|
{
|
|
|
|
|
|
|
|
Error TDb::AddThread(const Thread & thread)
|
|
{
|
|
query.Clear();
|
|
query << R("insert into plugins.thread (file_id, replies, last_item, closed) values (")
|
|
<< thread.file_id
|
|
<< thread.replies
|
|
<< thread.last_item_id
|
|
<< (thread.closed ? 1 : 0 )
|
|
<< R(");");
|
|
|
|
return DoCommand(query);
|
|
}
|
|
|
|
|
|
|
|
void TDb::SetThreadColumns(PGresult * r)
|
|
{
|
|
cfile_id = AssertColumn(r, "file_id");
|
|
creplies = AssertColumn(r, "replies");
|
|
cclosed = AssertColumn(r, "closed");
|
|
clast_item = AssertColumn(r, "last_item");
|
|
cdate_modification = AssertColumn(r, "date_modification");
|
|
cuser_id = AssertColumn(r, "user_id");
|
|
cguest = AssertColumn(r, "guest_name");
|
|
}
|
|
|
|
|
|
void TDb::SetThread(PGresult * r, int col, Thread & thread)
|
|
{
|
|
thread.file_id = AssertValueLong(r, col, cfile_id);
|
|
thread.replies = AssertValueLong(r, col, creplies);
|
|
thread.closed = AssertValueBool(r, col, cclosed);
|
|
thread.last_item_id = AssertValueLong(r, col, clast_item);
|
|
thread.last_item_date_modification = AssertValueDate(r, col, cdate_modification);
|
|
thread.last_item_user_id = AssertValueLong(r, col, cuser_id);
|
|
AssertValueWide(r, col, cguest, thread.last_item_guest_name);
|
|
}
|
|
|
|
|
|
|
|
|
|
Error TDb::GetThread(long file_id, Thread & thread)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("select thread.file_id, thread.replies, thread.closed, thread.last_item, "
|
|
"item.date_modification, item.user_id, item.guest_name "
|
|
"from plugins.thread left join core.item on thread.last_item = item.id "
|
|
"where thread.file_id = ") << file_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_TUPLES_OK);
|
|
|
|
int rows = Rows(r);
|
|
|
|
if( rows > 1 )
|
|
log << log1 << "ThreadDb: there is more than one thread with file_id: " << file_id << logend;
|
|
else
|
|
if( rows == 0 )
|
|
throw Error(WINIX_ERR_NO_THREAD);
|
|
|
|
SetThreadColumns(r);
|
|
SetThread(r, 0, thread);
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
Error TDb::GetThreads(const std::vector<long> & file_id_tab, std::vector<Thread> & thread_tab)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
thread_tab.clear();
|
|
|
|
if( file_id_tab.empty() )
|
|
return status;
|
|
|
|
try
|
|
{
|
|
CreateIdList(file_id_tab, list_id);
|
|
|
|
// they should be sorted by file_id (they are used in a binary search later)
|
|
query.Clear();
|
|
query << R("select thread.file_id, thread.replies, thread.closed, thread.last_item, "
|
|
"item.date_modification, item.user_id, item.guest_name "
|
|
"from plugins.thread left join core.item on thread.last_item = item.id "
|
|
"where thread.file_id in ") << R(list_id) << R(" order by file_id asc;");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_TUPLES_OK);
|
|
|
|
int rows = Rows(r);
|
|
SetThreadColumns(r);
|
|
|
|
for(int i=0 ; i<rows ; ++i)
|
|
{
|
|
SetThread(r, i, thread_temp);
|
|
thread_tab.push_back(thread_temp);
|
|
}
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
Error TDb::GetAnswers(long file_id, std::vector<long> & answer_id_tab)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
answer_id_tab.clear();
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("select answer_id from plugins.thread_files "
|
|
"where file_id = ") << file_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_TUPLES_OK);
|
|
|
|
int rows = Rows(r);
|
|
answer_id_tab.resize(rows);
|
|
|
|
for(int i=0 ; i<rows ; ++i)
|
|
answer_id_tab[i] = AssertValueLong(r, i, 0);
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
Error TDb::AddAnswer(long file_id, long answer_id)
|
|
{
|
|
query.Clear();
|
|
query << R("insert into plugins.thread_files (file_id, answer_id) values (")
|
|
<< file_id
|
|
<< answer_id
|
|
<< R(");");
|
|
|
|
Error status = DoCommand(query);
|
|
|
|
if( status != WINIX_ERR_OK )
|
|
return status;
|
|
|
|
query.Clear();
|
|
query << R("update plugins.thread set (last_item, replies) = (")
|
|
<< answer_id
|
|
<< R(", replies+1) where file_id=")
|
|
<< file_id
|
|
<< R(";");
|
|
|
|
return DoCommand(query);
|
|
}
|
|
|
|
|
|
|
|
long TDb::FindLastAnswer(long file_id)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
long last_item_id = -1;
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("select id from plugins.thread_files left join core.item on item.id=answer_id where file_id=")
|
|
<< file_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);
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return last_item_id;
|
|
}
|
|
|
|
|
|
|
|
Error TDb::RemoveAnswer(long answer_id)
|
|
{
|
|
PGresult * r1 = 0;
|
|
PGresult * r2 = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
|
|
try
|
|
{
|
|
// selecting files which have answer_id as an answer
|
|
query.Clear();
|
|
query << R("select file_id from plugins.thread_files where answer_id = ")
|
|
<< answer_id
|
|
<< R(";");
|
|
|
|
r1 = AssertQuery(query);
|
|
AssertResult(r1, PGRES_TUPLES_OK);
|
|
|
|
int rows = Rows(r1);
|
|
file_id_tab.resize(rows);
|
|
|
|
for(int i=0 ; i<rows ; ++i)
|
|
file_id_tab[i] = AssertValueLong(r1, i, 0);
|
|
|
|
|
|
// deleting those answers
|
|
query.Clear();
|
|
query << R("delete from plugins.thread_files where answer_id = ")
|
|
<< answer_id
|
|
<< R(";");
|
|
|
|
r2 = AssertQuery(query);
|
|
AssertResult(r2, PGRES_COMMAND_OK);
|
|
|
|
rows = AffectedRows(r2);
|
|
|
|
if( rows > 0 )
|
|
log << log2 << "ThreadDb: deleted " << rows << " rows from plugins.thread_files" << logend;
|
|
|
|
// setting new last_items to the files
|
|
status = RemoveAnswerRecalcLast(file_id_tab);
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r1);
|
|
ClearResult(r2);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
Error TDb::RemoveAnswerRecalcLast(long file_id)
|
|
{
|
|
long last_item_id = FindLastAnswer(file_id);
|
|
|
|
query.Clear();
|
|
query << R("update plugins.thread set (replies, last_item) = (replies-1,")
|
|
<< last_item_id
|
|
<< R(") where file_id=")
|
|
<< file_id
|
|
<< R(";");
|
|
|
|
return DoCommand(query);
|
|
}
|
|
|
|
|
|
|
|
Error TDb::RemoveAnswerRecalcLast(const std::vector<long> & file_id_tab)
|
|
{
|
|
for(size_t i=0 ; i<file_id_tab.size() ; ++i)
|
|
{
|
|
long file_id = file_id_tab[i];
|
|
Error status = RemoveAnswerRecalcLast(file_id);
|
|
|
|
if( status != WINIX_ERR_OK )
|
|
return status;
|
|
}
|
|
|
|
return WINIX_ERR_OK;
|
|
}
|
|
|
|
|
|
Error TDb::RecalcThread(long file_id)
|
|
{
|
|
long replies = CalcAnswers(file_id);
|
|
long last_item = FindLastAnswer(file_id);
|
|
|
|
query.Clear();
|
|
query << R("update plugins.thread set (replies, last_item) = (")
|
|
<< replies
|
|
<< last_item
|
|
<< R(") where file_id=")
|
|
<< file_id
|
|
<< R(";");
|
|
|
|
return DoCommand(query);
|
|
}
|
|
|
|
|
|
long TDb::CalcAnswers(long file_id)
|
|
{
|
|
PGresult * r = 0;
|
|
long answers = 0;
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("select count(file_id) from plugins.thread_files where file_id=") << file_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_TUPLES_OK);
|
|
|
|
if( Rows(r) == 1 )
|
|
answers = AssertValueLong(r, 0, 0);
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return answers;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Error TDb::RemoveThread(long file_id)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("delete from plugins.thread where file_id=") << file_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_COMMAND_OK);
|
|
|
|
long rows = AffectedRows(r);
|
|
|
|
if( rows > 0 )
|
|
log << log2 << "ThreadDb: deleted " << rows << " rows from plugins.thread" << logend;
|
|
|
|
ClearResult(r);
|
|
|
|
query.Clear();
|
|
query << R("delete from plugins.thread_files where file_id=") << file_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_COMMAND_OK);
|
|
|
|
rows = AffectedRows(r);
|
|
|
|
if( rows > 0 )
|
|
log << log2 << "ThreadDb: deleted " << rows << " rows from plugins.thread_files" << logend;
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
Error TDb::RemoveAnswerOnly(long answer_id)
|
|
{
|
|
PGresult * r = 0;
|
|
Error status = WINIX_ERR_OK;
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("delete from plugins.thread_files where answer_id=") << answer_id << R(";");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_COMMAND_OK);
|
|
|
|
long rows = AffectedRows(r);
|
|
|
|
if( rows > 0 )
|
|
log << log2 << "ThreadDb: deleted " << rows << " rows from plugins.thread_files" << logend;
|
|
}
|
|
catch(const Error & e)
|
|
{
|
|
status = e;
|
|
}
|
|
|
|
ClearResult(r);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TDb::GetAllThreadsId(std::vector<long> & file_id)
|
|
{
|
|
PGresult * r = 0;
|
|
file_id.clear();
|
|
|
|
try
|
|
{
|
|
query.Clear();
|
|
query << R("select file_id from plugins.thread;");
|
|
|
|
r = AssertQuery(query);
|
|
AssertResult(r, PGRES_TUPLES_OK);
|
|
|
|
int rows = Rows(r);
|
|
file_id.resize(rows);
|
|
|
|
for(int i=0 ; i<rows ; ++i)
|
|
file_id[i] = AssertValueLong(r, i, 0);
|
|
}
|
|
catch(const Error &)
|
|
{
|
|
}
|
|
|
|
ClearResult(r);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|