-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
*
- * InspIRCd: (C) 2002-2010 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006-2007, 2009 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006-2007, 2009 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
*
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
*
- * ---------------------------------------------------
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
#include "inspircd.h"
#include <cstdlib>
#include <sstream>
#include <libpq-fe.h>
-#include "sql.h"
+#include "modules/sql.h"
/* $ModDesc: PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API */
/* $CompileFlags: -Iexec("pg_config --includedir") eval("my $s = `pg_config --version`;$s =~ /^.*?(\d+)\.(\d+)\.(\d+).*?$/;my $v = hex(sprintf("0x%02x%02x%02x", $1, $2, $3));print "-DPGSQL_HAS_ESCAPECONN" if(($v >= 0x080104) || ($v >= 0x07030F && $v < 0x070400) || ($v >= 0x07040D && $v < 0x080000) || ($v >= 0x080008 && $v < 0x080100));") */
/* $LinkerFlags: -Lexec("pg_config --libdir") -lpq */
-/* $ModDep: m_sqlv2.h */
/* SQLConn rewritten by peavey to
* use EventHandler instead of
ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m)
{
}
- virtual void Tick(time_t TIME);
+ bool Tick(time_t TIME);
};
+struct QueueItem
+{
+ SQLQuery* c;
+ std::string q;
+ QueueItem(SQLQuery* C, const std::string& Q) : c(C), q(Q) {}
+};
/** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
* All SQL providers must create their own subclass and define it's methods using that
PQclear(res);
}
- virtual int Rows()
+ int Rows()
{
return rows;
}
- virtual void GetCols(std::vector<std::string>& result)
+ void GetCols(std::vector<std::string>& result)
{
result.resize(PQnfields(res));
for(unsigned int i=0; i < result.size(); i++)
}
}
- virtual SQLEntry GetValue(int row, int column)
+ SQLEntry GetValue(int row, int column)
{
char* v = PQgetvalue(res, row, column);
if (!v || PQgetisnull(res, row, column))
return SQLEntry(std::string(v, PQgetlength(res, row, column)));
}
- virtual bool GetRow(SQLEntries& result)
+ bool GetRow(SQLEntries& result)
{
if (currentrow >= PQntuples(res))
return false;
*/
class SQLConn : public SQLProvider, public EventHandler
{
- private:
+ public:
reference<ConfigTag> conf; /* The <database> entry */
- std::deque<SQLQuery*> queue;
+ std::deque<QueueItem> queue;
PGconn* sql; /* PgSQL database connection handle */
SQLstatus status; /* PgSQL database connection status */
- SQLQuery* qinprog; /* If there is currently a query in progress */
- time_t idle; /* Time we last heard from the database */
+ QueueItem qinprog; /* If there is currently a query in progress */
- public:
SQLConn(Module* Creator, ConfigTag* tag)
- : SQLProvider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL)
+ : SQLProvider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "")
{
- idle = ServerInstance->Time();
if (!DoConnect())
{
- ServerInstance->Logs->Log("m_pgsql",DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
+ ServerInstance->Logs->Log("m_pgsql", LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
DelayReconnect();
}
}
~SQLConn()
{
SQLerror err(SQL_BAD_DBID);
- if (qinprog)
+ if (qinprog.c)
{
- qinprog->OnError(err);
- delete qinprog;
+ qinprog.c->OnError(err);
+ delete qinprog.c;
}
- for(std::deque<SQLQuery*>::iterator i = queue.begin(); i != queue.end(); i++)
+ for(std::deque<QueueItem>::iterator i = queue.begin(); i != queue.end(); i++)
{
- SQLQuery* q = *i;
+ SQLQuery* q = i->c;
q->OnError(err);
delete q;
}
}
- virtual void HandleEvent(EventType et, int errornum)
+ void HandleEvent(EventType et, int errornum)
{
switch (et)
{
if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
{
- ServerInstance->Logs->Log("m_pgsql",DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
+ ServerInstance->Logs->Log("m_pgsql", LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
return false;
}
void DoConnectedPoll()
{
restart:
- while (!qinprog && !queue.empty())
+ while (qinprog.q.empty() && !queue.empty())
{
/* There's no query currently in progress, and there's queries in the queue. */
DoQuery(queue.front());
if (PQconsumeInput(sql))
{
- /* We just read stuff from the server, that counts as it being alive
- * so update the idle-since time :p
- */
- idle = ServerInstance->Time();
-
if (PQisBusy(sql))
{
/* Nothing happens here */
}
- else if (qinprog)
+ else if (qinprog.c)
{
/* Fetch the result.. */
PGresult* result = PQgetResult(sql);
case PGRES_FATAL_ERROR:
{
SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result));
- qinprog->OnError(err);
+ qinprog.c->OnError(err);
break;
}
default:
/* Other values are not errors */
- qinprog->OnResult(reply);
+ qinprog.c->OnResult(reply);
}
- delete qinprog;
- qinprog = NULL;
+ delete qinprog.c;
+ qinprog = QueueItem(NULL, "");
goto restart;
}
+ else
+ {
+ qinprog.q.clear();
+ }
}
else
{
}
}
- virtual std::string FormatQuery(const std::string& q, const ParamL& p)
+ void submit(SQLQuery *req, const std::string& q)
+ {
+ if (qinprog.q.empty())
+ {
+ DoQuery(QueueItem(req,q));
+ }
+ else
+ {
+ // wait your turn.
+ queue.push_back(QueueItem(req,q));
+ }
+ }
+
+ void submit(SQLQuery *req, const std::string& q, const ParamL& p)
{
std::string res;
unsigned int param = 0;
res.push_back(q[i]);
else
{
- // TODO numbered parameter support ('?1')
if (param < p.size())
{
std::string parm = p[param++];
- char buffer[MAXBUF];
+ std::vector<char> buffer(parm.length() * 2 + 1);
#ifdef PGSQL_HAS_ESCAPECONN
int error;
- PQescapeStringConn(sql, buffer, parm.c_str(), parm.length(), &error);
+ size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
+ ServerInstance->Logs->Log("m_pgsql", LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
#else
- PQescapeString (buffer, parm.c_str(), parm.length());
+ size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
#endif
- res.append(buffer);
+ res.append(&buffer[0], escapedsize);
}
}
}
- return res;
+ submit(req, res);
}
- std::string FormatQuery(const std::string& q, const ParamM& p)
+ void submit(SQLQuery *req, const std::string& q, const ParamM& p)
{
std::string res;
for(std::string::size_type i = 0; i < q.length(); i++)
{
std::string field;
i++;
- while (i < q.length() && isalpha(q[i]))
+ while (i < q.length() && isalnum(q[i]))
field.push_back(q[i++]);
i--;
if (it != p.end())
{
std::string parm = it->second;
- char buffer[MAXBUF];
+ std::vector<char> buffer(parm.length() * 2 + 1);
#ifdef PGSQL_HAS_ESCAPECONN
int error;
- PQescapeStringConn(sql, buffer, parm.c_str(), parm.length(), &error);
+ size_t escapedsize = PQescapeStringConn(sql, &buffer[0], parm.data(), parm.length(), &error);
if (error)
- ServerInstance->Logs->Log("m_pgsql", DEBUG, "BUG: Apparently PQescapeStringConn() failed");
+ ServerInstance->Logs->Log("m_pgsql", LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed");
#else
- PQescapeString (buffer, parm.c_str(), parm.length());
+ size_t escapedsize = PQescapeString(&buffer[0], parm.data(), parm.length());
#endif
- res.append(buffer);
+ res.append(&buffer[0], escapedsize);
}
}
}
- return res;
- }
-
- virtual void submit(SQLQuery *req)
- {
- if (qinprog)
- {
- // wait your turn.
- queue.push_back(req);
- }
- else
- {
- DoQuery(req);
- }
+ submit(req, res);
}
- void DoQuery(SQLQuery* req)
+ void DoQuery(const QueueItem& req)
{
if (status != WREAD && status != WWRITE)
{
// whoops, not connected...
SQLerror err(SQL_BAD_CONN);
- req->OnError(err);
- delete req;
+ req.c->OnError(err);
+ delete req.c;
return;
}
- if(PQsendQuery(sql, req->query.c_str()))
+ if(PQsendQuery(sql, req.q.c_str()))
{
qinprog = req;
}
else
{
SQLerror err(SQL_QSEND_FAIL, PQerrorMessage(sql));
- req->OnError(err);
- delete req;
+ req.c->OnError(err);
+ delete req.c;
}
}
ReconnectTimer* retimer;
ModulePgSQL()
+ : retimer(NULL)
{
}
- void init()
+ void init() CXX11_OVERRIDE
{
ReadConf();
Implementation eventlist[] = { I_OnUnloadModule, I_OnRehash };
- ServerInstance->Modules->Attach(eventlist, this, 2);
+ ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
- virtual ~ModulePgSQL()
+ ~ModulePgSQL()
{
- if (retimer)
- ServerInstance->Timers->DelTimer(retimer);
+ delete retimer;
ClearAllConnections();
}
- virtual void OnRehash(User* user)
+ void OnRehash(User* user) CXX11_OVERRIDE
{
ReadConf();
}
connections.clear();
}
- void OnUnloadModule(Module* mod)
+ void OnUnloadModule(Module* mod) CXX11_OVERRIDE
{
- // TODO cancel queries that will have a bad vtable
+ SQLerror err(SQL_BAD_DBID);
+ for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++)
+ {
+ SQLConn* conn = i->second;
+ if (conn->qinprog.c && conn->qinprog.c->creator == mod)
+ {
+ conn->qinprog.c->OnError(err);
+ delete conn->qinprog.c;
+ conn->qinprog.c = NULL;
+ }
+ std::deque<QueueItem>::iterator j = conn->queue.begin();
+ while (j != conn->queue.end())
+ {
+ SQLQuery* q = j->c;
+ if (q->creator == mod)
+ {
+ q->OnError(err);
+ delete q;
+ j = conn->queue.erase(j);
+ }
+ else
+ j++;
+ }
+ }
}
- Version GetVersion()
+ Version GetVersion() CXX11_OVERRIDE
{
return Version("PostgreSQL Service Provider module for all other m_sql* modules, uses v2 of the SQL API", VF_VENDOR);
}
};
-void ReconnectTimer::Tick(time_t time)
+bool ReconnectTimer::Tick(time_t time)
{
mod->retimer = NULL;
mod->ReadConf();
+ return false;
}
void SQLConn::DelayReconnect()