X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fextra%2Fm_pgsql.cpp;h=bb727b623ea504dd4d3dbd4a874fd59e61531702;hb=77730fd5f09f8fc193205654c8bba84d34365670;hp=735ca2f5aedced6f1d0b5d5535f2964c4381504a;hpb=1efedf9743fefe8269c2bcd292306de06a1b615e;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp index 735ca2f5a..bb727b623 100644 --- a/src/modules/extra/m_pgsql.cpp +++ b/src/modules/extra/m_pgsql.cpp @@ -1,26 +1,39 @@ -/* +------------------------------------+ - * | 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 + * Copyright (C) 2006-2007, 2009 Dennis Friis + * Copyright (C) 2006-2007, 2009 Craig Edwards + * Copyright (C) 2008 Robin Burchell + * Copyright (C) 2008 Thomas Stagner + * Copyright (C) 2006 Oliver Lupton * - * 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 . */ +/// $CompilerFlags: -Iexecute("pg_config --includedir" "POSTGRESQL_INCLUDE_DIR") +/// $LinkerFlags: -Lexecute("pg_config --libdir" "POSTGRESQL_LIBRARY_DIR") -lpq + +/// $PackageInfo: require_system("centos") postgresql-devel +/// $PackageInfo: require_system("darwin") postgresql +/// $PackageInfo: require_system("debian") libpq-dev +/// $PackageInfo: require_system("ubuntu") libpq-dev + + #include "inspircd.h" #include -#include #include -#include "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 */ +#include "modules/sql.h" /* SQLConn rewritten by peavey to * use EventHandler instead of @@ -33,7 +46,7 @@ class SQLConn; class ModulePgSQL; -typedef std::map ConnMap; +typedef insp::flat_map ConnMap; /* CREAD, Connecting and wants read event * CWRITE, Connecting and wants write event @@ -49,12 +62,18 @@ class ReconnectTimer : public Timer private: ModulePgSQL* mod; public: - ReconnectTimer(ModulePgSQL* m) : Timer(5, ServerInstance->Time(), false), mod(m) + ReconnectTimer(ModulePgSQL* m) : Timer(5, false), mod(m) { } - virtual void Tick(time_t TIME); + bool Tick(time_t TIME) CXX11_OVERRIDE; }; +struct QueueItem +{ + SQL::Query* c; + std::string q; + QueueItem(SQL::Query* 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 @@ -63,11 +82,21 @@ class ReconnectTimer : public Timer * data is passes to the module nearly as directly as if it was using the API directly itself. */ -class PgSQLresult : public SQLResult +class PgSQLresult : public SQL::Result { PGresult* res; int currentrow; int rows; + std::vector colnames; + + void getColNames() + { + colnames.resize(PQnfields(res)); + for(unsigned int i=0; i < colnames.size(); i++) + { + colnames[i] = PQfname(res, i); + } + } public: PgSQLresult(PGresult* result) : res(result), currentrow(0) { @@ -81,30 +110,44 @@ class PgSQLresult : public SQLResult PQclear(res); } - virtual int Rows() + int Rows() CXX11_OVERRIDE { return rows; } - virtual void GetCols(std::vector& result) + void GetCols(std::vector& result) CXX11_OVERRIDE + { + if (colnames.empty()) + getColNames(); + result = colnames; + } + + bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE { - result.resize(PQnfields(res)); - for(unsigned int i=0; i < result.size(); i++) + if (colnames.empty()) + getColNames(); + + for (size_t i = 0; i < colnames.size(); ++i) { - result[i] = PQfname(res, i); + if (colnames[i] == column) + { + index = i; + return true; + } } + return false; } - virtual SQLEntry GetValue(int row, int column) + SQL::Field GetValue(int row, int column) { char* v = PQgetvalue(res, row, column); if (!v || PQgetisnull(res, row, column)) - return SQLEntry(); + return SQL::Field(); - return SQLEntry(std::string(v, PQgetlength(res, row, column))); + return SQL::Field(std::string(v, PQgetlength(res, row, column))); } - virtual bool GetRow(SQLEntries& result) + bool GetRow(SQL::Row& result) CXX11_OVERRIDE { if (currentrow >= PQntuples(res)) return false; @@ -122,60 +165,61 @@ class PgSQLresult : public SQLResult /** SQLConn represents one SQL session. */ -class SQLConn : public SQLProvider, public EventHandler +class SQLConn : public SQL::Provider, public EventHandler { public: reference conf; /* The entry */ - std::deque queue; + std::deque queue; PGconn* sql; /* PgSQL database connection handle */ SQLstatus status; /* PgSQL database connection status */ - SQLQuery* qinprog; /* If there is currently a query in progress */ + QueueItem qinprog; /* If there is currently a query in progress */ SQLConn(Module* Creator, ConfigTag* tag) - : SQLProvider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL) + : SQL::Provider(Creator, "SQL/" + tag->getString("id")), conf(tag), sql(NULL), status(CWRITE), qinprog(NULL, "") { if (!DoConnect()) { - ServerInstance->Logs->Log("m_pgsql",DEFAULT, "WARNING: Could not connect to database " + tag->getString("id")); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id")); DelayReconnect(); } } - CullResult cull() + CullResult cull() CXX11_OVERRIDE { - this->SQLProvider::cull(); + this->SQL::Provider::cull(); ServerInstance->Modules->DelService(*this); return this->EventHandler::cull(); } ~SQLConn() { - SQLerror err(SQL_BAD_DBID); - if (qinprog) + SQL::Error err(SQL::BAD_DBID); + if (qinprog.c) { - qinprog->OnError(err); - delete qinprog; + qinprog.c->OnError(err); + delete qinprog.c; } - for(std::deque::iterator i = queue.begin(); i != queue.end(); i++) + for(std::deque::iterator i = queue.begin(); i != queue.end(); i++) { - SQLQuery* q = *i; + SQL::Query* q = i->c; q->OnError(err); delete q; } } - virtual void HandleEvent(EventType et, int errornum) + void OnEventHandlerRead() CXX11_OVERRIDE { - switch (et) - { - case EVENT_READ: - case EVENT_WRITE: - DoEvent(); - break; + DoEvent(); + } - case EVENT_ERROR: - DelayReconnect(); - } + void OnEventHandlerWrite() CXX11_OVERRIDE + { + DoEvent(); + } + + void OnEventHandlerError(int errornum) CXX11_OVERRIDE + { + DelayReconnect(); } std::string GetDSN() @@ -226,9 +270,9 @@ class SQLConn : public SQLProvider, public EventHandler if(this->fd <= -1) return false; - if (!ServerInstance->SE->AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ)) + if (!SocketEngine::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(MODNAME, LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine"); return false; } @@ -241,17 +285,17 @@ class SQLConn : public SQLProvider, public EventHandler switch(PQconnectPoll(sql)) { case PGRES_POLLING_WRITING: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ); status = CWRITE; return true; case PGRES_POLLING_READING: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); status = CREAD; return true; case PGRES_POLLING_FAILED: return false; case PGRES_POLLING_OK: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); status = WWRITE; DoConnectedPoll(); default: @@ -262,7 +306,7 @@ class SQLConn : public SQLProvider, public EventHandler 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()); @@ -275,7 +319,7 @@ restart: { /* Nothing happens here */ } - else if (qinprog) + else if (qinprog.c) { /* Fetch the result.. */ PGresult* result = PQgetResult(sql); @@ -300,19 +344,23 @@ restart: case PGRES_BAD_RESPONSE: case PGRES_FATAL_ERROR: { - SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result)); - qinprog->OnError(err); + SQL::Error err(SQL::QREPLY_FAIL, PQresultErrorMessage(result)); + 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 { @@ -330,17 +378,17 @@ restart: switch(PQresetPoll(sql)) { case PGRES_POLLING_WRITING: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ); status = CWRITE; return DoPoll(); case PGRES_POLLING_READING: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); status = CREAD; return true; case PGRES_POLLING_FAILED: return false; case PGRES_POLLING_OK: - ServerInstance->SE->ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); + SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); status = WWRITE; DoConnectedPoll(); default: @@ -366,7 +414,20 @@ restart: } } - virtual std::string FormatQuery(const std::string& q, const ParamL& p) + void Submit(SQL::Query *req, const std::string& q) CXX11_OVERRIDE + { + if (qinprog.q.empty()) + { + DoQuery(QueueItem(req,q)); + } + else + { + // wait your turn. + queue.push_back(QueueItem(req,q)); + } + } + + void Submit(SQL::Query *req, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE { std::string res; unsigned int param = 0; @@ -376,27 +437,22 @@ restart: res.push_back(q[i]); else { - // TODO numbered parameter support ('?1') if (param < p.size()) { std::string parm = p[param++]; - char buffer[MAXBUF]; -#ifdef PGSQL_HAS_ESCAPECONN + std::vector buffer(parm.length() * 2 + 1); 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"); -#else - PQescapeString (buffer, parm.c_str(), parm.length()); -#endif - res.append(buffer); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed"); + res.append(&buffer[0], escapedsize); } } } - return res; + Submit(req, res); } - std::string FormatQuery(const std::string& q, const ParamM& p) + void Submit(SQL::Query *req, const std::string& q, const SQL::ParamMap& p) CXX11_OVERRIDE { std::string res; for(std::string::size_type i = 0; i < q.length(); i++) @@ -407,69 +463,52 @@ restart: { std::string field; i++; - while (i < q.length() && isalpha(q[i])) + while (i < q.length() && isalnum(q[i])) field.push_back(q[i++]); i--; - ParamM::const_iterator it = p.find(field); + SQL::ParamMap::const_iterator it = p.find(field); if (it != p.end()) { std::string parm = it->second; - char buffer[MAXBUF]; -#ifdef PGSQL_HAS_ESCAPECONN + std::vector buffer(parm.length() * 2 + 1); 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"); -#else - PQescapeString (buffer, parm.c_str(), parm.length()); -#endif - res.append(buffer); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Apparently PQescapeStringConn() failed"); + 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; + SQL::Error err(SQL::BAD_CONN); + 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; + SQL::Error err(SQL::QSEND_FAIL, PQerrorMessage(sql)); + req.c->OnError(err); + delete req.c; } } void Close() { - ServerInstance->SE->DelFd(this); + SocketEngine::DelFd(this); if(sql) { @@ -479,13 +518,6 @@ restart: } }; -class DummyQuery : public SQLQuery -{ - public: - DummyQuery(Module* me) : SQLQuery(me, "") {} - void OnResult(SQLResult& result) {} -}; - class ModulePgSQL : public Module { public: @@ -493,25 +525,17 @@ class ModulePgSQL : public Module ReconnectTimer* retimer; ModulePgSQL() + : retimer(NULL) { } - void init() - { - ReadConf(); - - Implementation eventlist[] = { I_OnUnloadModule, I_OnRehash }; - ServerInstance->Modules->Attach(eventlist, this, 2); - } - - virtual ~ModulePgSQL() + ~ModulePgSQL() { - if (retimer) - ServerInstance->Timers->DelTimer(retimer); + delete retimer; ClearAllConnections(); } - virtual void OnRehash(User* user) + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { ReadConf(); } @@ -522,7 +546,7 @@ class ModulePgSQL : public Module ConfigTagList tags = ServerInstance->Config->ConfTags("database"); for(ConfigIter i = tags.first; i != tags.second; i++) { - if (i->second->getString("module", "pgsql") != "pgsql") + if (!stdalgo::string::equalsci(i->second->getString("module"), "pgsql")) continue; std::string id = i->second->getString("id"); ConnMap::iterator curr = connections.find(id); @@ -552,22 +576,22 @@ class ModulePgSQL : public Module connections.clear(); } - void OnUnloadModule(Module* mod) + void OnUnloadModule(Module* mod) CXX11_OVERRIDE { - SQLerror err(SQL_BAD_DBID); + SQL::Error err(SQL::BAD_DBID); for(ConnMap::iterator i = connections.begin(); i != connections.end(); i++) { SQLConn* conn = i->second; - if (conn->qinprog && conn->qinprog->creator == mod) + if (conn->qinprog.c && conn->qinprog.c->creator == mod) { - conn->qinprog->OnError(err); - delete conn->qinprog; - conn->qinprog = new DummyQuery(this); + conn->qinprog.c->OnError(err); + delete conn->qinprog.c; + conn->qinprog.c = NULL; } - std::deque::iterator j = conn->queue.begin(); + std::deque::iterator j = conn->queue.begin(); while (j != conn->queue.end()) { - SQLQuery* q = *j; + SQL::Query* q = j->c; if (q->creator == mod) { q->OnError(err); @@ -580,16 +604,18 @@ class ModulePgSQL : public Module } } - 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(); + delete this; + return false; } void SQLConn::DelayReconnect() @@ -603,7 +629,7 @@ void SQLConn::DelayReconnect() if (!mod->retimer) { mod->retimer = new ReconnectTimer(mod); - ServerInstance->Timers->AddTimer(mod->retimer); + ServerInstance->Timers.AddTimer(mod->retimer); } } }