]> 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 8beea1265ad011aada3c548153cd263228001e2c..e1db996c896d918867bd8677b6190a174df03daa 100644 (file)
@@ -1,12 +1,18 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   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) 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) 2009 Uli Schlachter <psychon@inspircd.org>
  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
- *   Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
+ *   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 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
@@ -24,6 +30,7 @@
 /// $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
@@ -48,14 +55,23 @@ class ModulePgSQL;
 
 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
 {
@@ -102,7 +118,7 @@ class PgSQLresult : public SQL::Result
        {
                rows = PQntuples(res);
                if (!rows)
-                       rows = atoi(PQcmdTuples(res));
+                       rows = ConvToNum<int>(PQcmdTuples(res));
        }
 
        ~PgSQLresult()
@@ -122,7 +138,7 @@ class PgSQLresult : public SQL::Result
                result = colnames;
        }
 
-       bool HasColumn(const std::string& column, size_t& index)
+       bool HasColumn(const std::string& column, size_t& index) CXX11_OVERRIDE
        {
                if (colnames.empty())
                        getColNames();
@@ -170,18 +186,19 @@ 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)
-       : SQL::Provider(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(MODNAME, LOG_DEFAULT, "WARNING: Could not connect to database " + tag->getString("id"));
                        DelayReconnect();
-               }
        }
 
        CullResult cull() CXX11_OVERRIDE
@@ -205,6 +222,7 @@ class SQLConn : public SQL::Provider, public EventHandler
                        q->OnError(err);
                        delete q;
                }
+               Close();
        }
 
        void OnEventHandlerRead() CXX11_OVERRIDE
@@ -250,34 +268,40 @@ class SQLConn : public SQL::Provider, 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 (!SocketEngine::AddFd(this, FD_WANT_NO_WRITE | FD_WANT_NO_READ))
-               {
-                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Couldn't add pgsql socket to socket engine");
-                       return false;
-               }
+                       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()
@@ -293,11 +317,14 @@ class SQLConn : public SQL::Provider, public EventHandler
                                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:
                                SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
                                status = WWRITE;
                                DoConnectedPoll();
+                               return true;
                        default:
                                return true;
                }
@@ -373,29 +400,6 @@ restart:
                }
        }
 
-       bool DoResetPoll()
-       {
-               switch(PQresetPoll(sql))
-               {
-                       case PGRES_POLLING_WRITING:
-                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_WRITE | FD_WANT_NO_READ);
-                               status = CWRITE;
-                               return DoPoll();
-                       case PGRES_POLLING_READING:
-                               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:
-                               SocketEngine::ChangeEventMask(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
-                               status = WWRITE;
-                               DoConnectedPoll();
-                       default:
-                               return true;
-               }
-       }
-
        void DelayReconnect();
 
        void DoEvent()
@@ -404,11 +408,7 @@ restart:
                {
                        DoPoll();
                }
-               else if((status == RREAD) || (status == RWRITE))
-               {
-                       DoResetPoll();
-               }
-               else
+               else if (status == WREAD || status == WWRITE)
                {
                        DoConnectedPoll();
                }
@@ -416,6 +416,7 @@ restart:
 
        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));
@@ -508,7 +509,10 @@ restart:
 
        void Close()
        {
-               SocketEngine::DelFd(this);
+               status = DEAD;
+
+               if (HasFd() && SocketEngine::HasFd(GetFd()))
+                       SocketEngine::DelFd(this);
 
                if(sql)
                {
@@ -546,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
                        {
@@ -606,7 +615,7 @@ class ModulePgSQL : public Module
 
        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);
        }
 };
 
@@ -620,17 +629,17 @@ bool ReconnectTimer::Tick(time_t time)
 
 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);
        }
 }