winix/winixd/plugins/thread/tdb.cpp

503 lines
10 KiB
C++

/*
* This file is a part of Winix
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2010-2014, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "tdb.h"
#include "core/log.h"
namespace Winix
{
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
} // namespace Winix