]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_pgsql.cpp
Fix a bunch of weird indentation and spacing issues.
[user/henk/code/inspircd.git] / src / modules / extra / m_pgsql.cpp
index 2d8b832644eee9be3c9e0c3126c1235f1330800e..e1db996c896d918867bd8677b6190a174df03daa 100644 (file)
@@ -1,25 +1,46 @@
-/*       +------------------------------------+
- *       | 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) 2016 Adam <Adam@anope.org>
+ *   Copyright (C) 2015 Daniel Vassdal <shutter@canternet.org>
+ *   Copyright (C) 2013, 2016-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2015 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007, 2009-2010 Craig Edwards <brain@inspircd.org>
+ *   Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2006 Oliver Lupton <om@inspircd.org>
  *
- * 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/>.
  */
 
+/// $CompilerFlags: -Iexecute("pg_config --includedir" "POSTGRESQL_INCLUDE_DIR")
+/// $LinkerFlags: -Lexecute("pg_config --libdir" "POSTGRESQL_LIBRARY_DIR") -lpq
+
+/// $PackageInfo: require_system("arch") postgresql-libs
+/// $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 <cstdlib>
-#include <sstream>
 #include <libpq-fe.h>
-#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 */
+#include "modules/sql.h"
 
 /* SQLConn rewritten by peavey to
  * use EventHandler instead of
 class SQLConn;
 class ModulePgSQL;
 
-typedef std::map<std::string, SQLConn*> ConnMap;
+typedef insp::flat_map<std::string, SQLConn*> ConnMap;
 
-/* CREAD,      Connecting and wants read event
- * CWRITE,     Connecting and wants write event
- * WREAD,      Connected/Working and wants read event
- * WWRITE,     Connected/Working and wants write event
- * RREAD,      Resetting and wants read event
- * RWRITE,     Resetting and wants write event
- */
-enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE, RREAD, RWRITE };
+enum SQLstatus
+{
+       // The connection has died.
+       DEAD,
+
+       // Connecting and wants read event.
+       CREAD,
+
+       // Connecting and wants write event.
+       CWRITE,
+
+       // Connected/working and wants read event.
+       WREAD,
+
+       // Connected/working and wants write event.
+       WWRITE
+};
 
 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
 {
-       SQLQuery* c;
+       SQL::Query* c;
        std::string q;
-       QueueItem(SQLQuery* C, const std::string& Q) : c(C), q(Q) {}
+       QueueItem(SQL::Query* C, const std::string& Q) : c(C), q(Q) {}
 };
 
 /** PgSQLresult is a subclass of the mostly-pure-virtual class SQLresult.
@@ -68,17 +98,27 @@ struct QueueItem
  * 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<std::string> 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)
        {
                rows = PQntuples(res);
                if (!rows)
-                       rows = atoi(PQcmdTuples(res));
+                       rows = ConvToNum<int>(PQcmdTuples(res));
        }
 
        ~PgSQLresult()
@@ -86,30 +126,44 @@ class PgSQLresult : public SQLResult
                PQclear(res);
        }
 
-       virtual int Rows()
+       int Rows() CXX11_OVERRIDE
        {
                return rows;
        }
 
-       virtual void GetCols(std::vector<std::string>& result)
+       void GetCols(std::vector<std::string>& 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;
@@ -127,35 +181,36 @@ class PgSQLresult : public SQLResult
 
 /** SQLConn represents one SQL session.
  */
-class SQLConn : public SQLProvider, public EventHandler
+class SQLConn : public SQL::Provider, public EventHandler
 {
  public:
        reference<ConfigTag> conf;      /* The <database> entry */
        std::deque<QueueItem> queue;
-       PGconn*                 sql;            /* PgSQL database connection handle */
+       PGconn*                 sql;            /* PgSQL database connection handle */
        SQLstatus               status;         /* PgSQL database connection status */
        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, 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")); 
                        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);
+               SQL::Error err(SQL::BAD_DBID);
                if (qinprog.c)
                {
                        qinprog.c->OnError(err);
@@ -163,24 +218,26 @@ class SQLConn : public SQLProvider, public EventHandler
                }
                for(std::deque<QueueItem>::iterator i = queue.begin(); i != queue.end(); i++)
                {
-                       SQLQuery* q = i->c;
+                       SQL::Query* q = i->c;
                        q->OnError(err);
                        delete q;
                }
+               Close();
        }
 
-       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()
@@ -211,34 +268,40 @@ class SQLConn : public SQLProvider, public EventHandler
                return conninfo.str();
        }
 
+       bool HandleConnectError(const char* reason)
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Could not connect to the \"%s\" database: %s",
+                       GetId().c_str(), reason);
+               return false;
+       }
+
        bool DoConnect()
        {
                sql = PQconnectStart(GetDSN().c_str());
                if (!sql)
-                       return false;
+                       return HandleConnectError("PQconnectStart returned NULL");
 
                if(PQstatus(sql) == CONNECTION_BAD)
-                       return false;
+                       return HandleConnectError("connection status is bad");
 
                if(PQsetnonblocking(sql, 1) == -1)
-                       return false;
+                       return HandleConnectError("unable to mark fd as non-blocking");
 
-               /* OK, we've initalised the connection, now to get it hooked into the socket engine
+               /* OK, we've initialised the connection, now to get it hooked into the socket engine
                * and then start polling it.
                */
-               this->fd = PQsocket(sql);
-
-               if(this->fd <= -1)
-                       return false;
+               SetFd(PQsocket(sql));
+               if(!HasFd())
+                       return HandleConnectError("PQsocket returned an invalid fd");
 
-               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");
-                       return false;
-               }
+               if (!SocketEngine::AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
+                       return HandleConnectError("could not add the pgsql socket to the socket engine");
 
                /* Socket all hooked into the engine, now to tell PgSQL to start connecting */
-               return DoPoll();
+               if (!DoPoll())
+                       return HandleConnectError("could not poll the connection state");
+
+               return true;
        }
 
        bool DoPoll()
@@ -246,19 +309,22 @@ 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:
+                               SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
+                               status = DEAD;
                                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();
+                               return true;
                        default:
                                return true;
                }
@@ -305,7 +371,7 @@ restart:
                                        case PGRES_BAD_RESPONSE:
                                        case PGRES_FATAL_ERROR:
                                        {
-                                               SQLerror err(SQL_QREPLY_FAIL, PQresultErrorMessage(result));
+                                               SQL::Error err(SQL::QREPLY_FAIL, PQresultErrorMessage(result));
                                                qinprog.c->OnError(err);
                                                break;
                                        }
@@ -320,7 +386,7 @@ restart:
                        }
                        else
                        {
-                               qinprog.q = "";
+                               qinprog.q.clear();
                        }
                }
                else
@@ -334,29 +400,6 @@ restart:
                }
        }
 
-       bool DoResetPoll()
-       {
-               switch(PQresetPoll(sql))
-               {
-                       case PGRES_POLLING_WRITING:
-                               ServerInstance->SE->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);
-                               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);
-                               status = WWRITE;
-                               DoConnectedPoll();
-                       default:
-                               return true;
-               }
-       }
-
        void DelayReconnect();
 
        void DoEvent()
@@ -365,18 +408,15 @@ restart:
                {
                        DoPoll();
                }
-               else if((status == RREAD) || (status == RWRITE))
-               {
-                       DoResetPoll();
-               }
-               else
+               else if (status == WREAD || status == WWRITE)
                {
                        DoConnectedPoll();
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q)
+       void Submit(SQL::Query *req, const std::string& q) CXX11_OVERRIDE
        {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Executing PostgreSQL query: " + q);
                if (qinprog.q.empty())
                {
                        DoQuery(QueueItem(req,q));
@@ -388,7 +428,7 @@ restart:
                }
        }
 
-       void submit(SQLQuery *req, const std::string& q, const ParamL& p)
+       void Submit(SQL::Query *req, const std::string& q, const SQL::ParamList& p) CXX11_OVERRIDE
        {
                std::string res;
                unsigned int param = 0;
@@ -401,23 +441,19 @@ restart:
                                if (param < p.size())
                                {
                                        std::string parm = p[param++];
-                                       char buffer[MAXBUF];
-#ifdef PGSQL_HAS_ESCAPECONN
+                                       std::vector<char> 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);
                                }
                        }
                }
-               submit(req, res);
+               Submit(req, res);
        }
 
-       void submit(SQLQuery *req, 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++)
@@ -432,24 +468,20 @@ restart:
                                        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<char> 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);
                                }
                        }
                }
-               submit(req, res);
+               Submit(req, res);
        }
 
        void DoQuery(const QueueItem& req)
@@ -457,7 +489,7 @@ restart:
                if (status != WREAD && status != WWRITE)
                {
                        // whoops, not connected...
-                       SQLerror err(SQL_BAD_CONN);
+                       SQL::Error err(SQL::BAD_CONN);
                        req.c->OnError(err);
                        delete req.c;
                        return;
@@ -469,7 +501,7 @@ restart:
                }
                else
                {
-                       SQLerror err(SQL_QSEND_FAIL, PQerrorMessage(sql));
+                       SQL::Error err(SQL::QSEND_FAIL, PQerrorMessage(sql));
                        req.c->OnError(err);
                        delete req.c;
                }
@@ -477,7 +509,10 @@ restart:
 
        void Close()
        {
-               ServerInstance->SE->DelFd(this);
+               status = DEAD;
+
+               if (HasFd() && SocketEngine::HasFd(GetFd()))
+                       SocketEngine::DelFd(this);
 
                if(sql)
                {
@@ -494,25 +529,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();
        }
@@ -523,15 +550,20 @@ 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);
                        if (curr == connections.end())
                        {
                                SQLConn* conn = new SQLConn(this, i->second);
-                               conns.insert(std::make_pair(id, conn));
-                               ServerInstance->Modules->AddService(*conn);
+                               if (conn->status != DEAD)
+                               {
+                                       conns.insert(std::make_pair(id, conn));
+                                       ServerInstance->Modules->AddService(*conn);
+                               }
+                               // If the connection is dead it has already been queued for culling
+                               // at the end of the main loop so we don't need to delete it here.
                        }
                        else
                        {
@@ -553,9 +585,9 @@ 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;
@@ -568,7 +600,7 @@ class ModulePgSQL : public Module
                        std::deque<QueueItem>::iterator j = conn->queue.begin();
                        while (j != conn->queue.end())
                        {
-                               SQLQuery* q = j->c;
+                               SQL::Query* q = j->c;
                                if (q->creator == mod)
                                {
                                        q->OnError(err);
@@ -581,31 +613,33 @@ 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);
+               return Version("Provides the ability for SQL modules to query a PostgreSQL database.", 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()
 {
+       status = DEAD;
        ModulePgSQL* mod = (ModulePgSQL*)(Module*)creator;
+
        ConnMap::iterator it = mod->connections.find(conf->getString("id"));
        if (it != mod->connections.end())
-       {
                mod->connections.erase(it);
-               ServerInstance->GlobalCulls.AddItem((EventHandler*)this);
-               if (!mod->retimer)
-               {
-                       mod->retimer = new ReconnectTimer(mod);
-                       ServerInstance->Timers->AddTimer(mod->retimer);
-               }
+       ServerInstance->GlobalCulls.AddItem((EventHandler*)this);
+       if (!mod->retimer)
+       {
+               mod->retimer = new ReconnectTimer(mod);
+               ServerInstance->Timers.AddTimer(mod->retimer);
        }
 }