/*
* 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 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * 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
/// $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
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
{
{
rows = PQntuples(res);
if (!rows)
- rows = atoi(PQcmdTuples(res));
+ rows = ConvToNum<int>(PQcmdTuples(res));
}
~PgSQLresult()
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
q->OnError(err);
delete q;
}
+ Close();
}
void OnEventHandlerRead() CXX11_OVERRIDE
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()
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);
}
}
- 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();
- return true;
- default:
- return true;
- }
- }
-
void DelayReconnect();
void DoEvent()
{
DoPoll();
}
- else if((status == RREAD) || (status == RWRITE))
- {
- DoResetPoll();
- }
- else
+ else if (status == WREAD || status == WWRITE)
{
DoConnectedPoll();
}
void Close()
{
- SocketEngine::DelFd(this);
+ status = DEAD;
+
+ if (HasFd() && SocketEngine::HasFd(GetFd()))
+ SocketEngine::DelFd(this);
if(sql)
{
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
{
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 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);
}
}