]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_sqlv2.h
Remove calls to deprecated InspIRCd::Log(), thanks danieldg for reporting.
[user/henk/code/inspircd.git] / src / modules / extra / m_sqlv2.h
index decac4b57d878a1b334886ecdda0d78b4160c5ef..2e0ab2a1931eaa60777a6026f184667da382a26a 100644 (file)
@@ -1 +1,599 @@
-/*       +------------------------------------+\r *       | Inspire Internet Relay Chat Daemon |\r *       +------------------------------------+\r *\r *  InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r *          the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#ifndef INSPIRCD_SQLAPI_2\r#define INSPIRCD_SQLAPI_2\r\r#include <string>\r#include <deque>\r#include <map>\r#include "modules.h"\r\r/** SQLreq define.\r * This is the voodoo magic which lets us pass multiple\r * parameters to the SQLrequest constructor... voodoo...\r */\r#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))\r\r/** Identifiers used to identify Request types\r */\r#define SQLREQID "SQLv2 Request"\r#define SQLRESID "SQLv2 Result"\r#define SQLSUCCESS "You shouldn't be reading this (success)"\r\r/** Defines the error types which SQLerror may be set to\r */\renum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };\r\r/** A list of format parameters for an SQLquery object.\r */\rtypedef std::deque<std::string> ParamL;\r\r/** The base class of SQL exceptions\r */\rclass SQLexception : public ModuleException\r{\r public:\r     SQLexception(const std::string &reason) : ModuleException(reason)\r      {\r      }\r\r     SQLexception() : ModuleException("SQLv2: Undefined exception")\r {\r      }\r};\r\r/** An exception thrown when a bad column or row name or id is requested\r */\rclass SQLbadColName : public SQLexception\r{\rpublic:\r SQLbadColName() : SQLexception("SQLv2: Bad column name")\r       {\r      }\r};\r\r/** SQLerror holds the error state of any SQLrequest or SQLresult.\r * The error string varies from database software to database software\r * and should be used to display informational error messages to users.\r */\rclass SQLerror : public classbase\r{\r        /** The error id\r        */\r    SQLerrorNum id;\r        /** The error string\r    */\r    std::string str;\rpublic:\r       /** Initialize an SQLerror\r      * @param i The error ID to set\r         * @param s The (optional) error string to set\r  */\r    SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")\r  : id(i), str(s)\r        {\r      }\r\r     /** Return the ID of the error\r  */\r    SQLerrorNum Id()\r       {\r              return id;\r     }\r\r     /** Set the ID of an error\r      * @param i The new error ID to set\r     * @return the ID which was set\r         */\r    SQLerrorNum Id(SQLerrorNum i)\r  {\r              id = i;\r                return id;\r     }\r\r     /** Set the error string for an error\r   * @param s The new error string to set\r         */\r    void Str(const std::string &s)\r {\r              str = s;\r       }\r\r     /** Return the error string for an error\r        */\r    const char* Str()\r      {\r              if(str.length())\r                       return str.c_str();\r\r           switch(id)\r             {\r                      case NO_ERROR:\r                         return "No error";\r                     case BAD_DBID:\r                         return "Invalid database ID";\r                  case BAD_CONN:\r                         return "Invalid connection";\r                   case QSEND_FAIL:\r                               return "Sending query failed";\r                 case QREPLY_FAIL:\r                              return "Getting query result failed";\r                  default:\r                               return "Unknown error";\r                }\r      }\r};\r\r/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.\r * C++ has no native type-safe way of having a variable number of arguments to a function,\r * the workaround for this isn't easy to describe simply, but in a nutshell what's really\r * happening when - from the above example - you do this:\r *\r * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");\r *\r * what's actually happening is functionally this:\r *\r * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));\r *\r * with 'query()' returning a reference to an object with a 'addparam()' member function which\r * in turn returns a reference to that object. There are actually four ways you can create a\r * SQLrequest..all have their disadvantages and advantages. In the real implementations the\r * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds\r * the query string and a ParamL (std::deque<std::string>) of query parameters.\r * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:\r *\r * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));\r */\rclass SQLquery : public classbase\r{\rpublic:\r  /** The query 'format string'\r   */\r    std::string q;\r /** The query parameter list\r    * There should be one parameter for every ? character\r  * within the format string shown above.\r        */\r    ParamL p;\r\r     /** Initialize an SQLquery with a given format string only\r      */\r    SQLquery(const std::string &query)\r     : q(query)\r     {\r      }\r\r     /** Initialize an SQLquery with a format string and parameters.\r         * If you provide parameters, you must initialize the list yourself\r     * if you choose to do it via this method, using std::deque::push_back().\r       */\r    SQLquery(const std::string &query, const ParamL &params)\r       : q(query), p(params)\r  {\r      }\r\r     /** An overloaded operator for pushing parameters onto the parameter list\r       */\r    template<typename T> SQLquery& operator,(const T &foo)\r {\r              p.push_back(ConvToStr(foo));\r           return *this;\r  }\r\r     /** An overloaded operator for pushing parameters onto the parameter list.\r      * This has higher precedence than 'operator,' and can save on parenthesis.\r     */\r    template<typename T> SQLquery& operator%(const T &foo)\r {\r              p.push_back(ConvToStr(foo));\r           return *this;\r  }\r};\r\r/** SQLrequest is sent to the SQL API to command it to run a query and return the result.\r * You must instantiate this object with a valid SQLquery object and its parameters, then\r * send it using its Send() method to the module providing the 'SQL' feature. To find this\r * module, use Server::FindFeature().\r */\rclass SQLrequest : public Request\r{\rpublic:\r     /** The fully parsed and expanded query string\r  * This is initialized from the SQLquery parameter given in the constructor.\r    */\r    SQLquery query;\r        /** The database ID to apply the request to\r     */\r    std::string dbid;\r      /** True if this is a priority query.\r   * Priority queries may 'queue jump' in the request queue.\r      */\r    bool pri;\r      /** The query ID, assigned by the SQL api.\r      * After your request is processed, this will\r   * be initialized for you by the API to a valid request ID,\r     * except in the case of an error.\r      */\r    unsigned long id;\r      /** If an error occured, error.id will be any other value than NO_ERROR.\r        */\r    SQLerror error;\r\r       /** Initialize an SQLrequest.\r   * For example:\r         *\r      * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);\r         *\r      * @param s A pointer to the sending module, where the result should be routed\r  * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature\r     * @param databaseid The database ID to perform the query on. This must match a valid\r   * database ID from the configuration of the SQL module.\r        * @param q A properly initialized SQLquery object.\r     */\r    SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)\r     : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)\r       {\r      }\r\r     /** Set the priority of a request.\r      */\r    void Priority(bool p = true)\r   {\r              pri = p;\r       }\r\r     /** Set the source of a request. You should not need to use this method.\r        */\r    void SetSource(Module* mod)\r    {\r              source = mod;\r  }\r};\r\r/**\r * This class contains a field's data plus a way to determine if the field\r * is NULL or not without having to mess around with NULL pointers.\r */\rclass SQLfield\r{\rpublic:\r  /**\r     * The data itself\r      */\r    std::string d;\r\r        /**\r     * If the field was null\r        */\r    bool null;\r\r    /** Initialize an SQLfield\r      */\r    SQLfield(const std::string &data = "", bool n = false)\r : d(data), null(n)\r     {\r\r     }\r};\r\r/** A list of items which make up a row of a result or table (tuple)\r * This does not include field names.\r */\rtypedef std::vector<SQLfield> SQLfieldList;\r/** A list of items which make up a row of a result or table (tuple)\r * This also includes the field names.\r */\rtypedef std::map<std::string, SQLfield> SQLfieldMap;\r\r/** SQLresult is a reply to a previous query.\r * If you send a query to the SQL api, the response will arrive at your\r * OnRequest method of your module at some later time, depending on the\r * congestion of the SQL server and complexity of the query. The ID of\r * this result will match the ID assigned to your original request.\r * SQLresult contains its own internal cursor (row counter) which is\r * incremented with each method call which retrieves a single row.\r */\rclass SQLresult : public Request\r{\rpublic:\r /** The original query string passed initially to the SQL API\r   */\r    std::string query;\r     /** The database ID the query was executed on\r   */\r    std::string dbid;\r      /**\r     * The error (if any) which occured.\r    * If an error occured the value of error.id will be any\r        * other value than NO_ERROR.\r   */\r    SQLerror error;\r        /**\r     * This will match  query ID you were given when sending\r        * the request at an earlier time.\r      */\r    unsigned long id;\r\r     /** Used by the SQL API to instantiate an SQLrequest\r    */\r    SQLresult(Module* s, Module* d, unsigned long i)\r       : Request(s, d, SQLRESID), id(i)\r       {\r      }\r\r     /**\r     * Return the number of rows in the result\r      * Note that if you have perfomed an INSERT\r     * or UPDATE query or other query which will\r    * not return rows, this will return the\r        * number of affected rows, and SQLresult::Cols()\r       * will contain 0. In this case you SHOULD NEVER\r        * access any of the result set rows, as there arent any!\r       * @returns Number of rows in the result set.\r   */\r    virtual int Rows() = 0;\r\r       /**\r     * Return the number of columns in the result.\r  * If you performed an UPDATE or INSERT which\r   * does not return a dataset, this value will\r   * be 0.\r        * @returns Number of columns in the result set.\r        */\r    virtual int Cols() = 0;\r\r       /**\r     * Get a string name of the column by an index number\r   * @param column The id number of a column\r      * @returns The column name associated with the given ID\r        */\r    virtual std::string ColName(int column) = 0;\r\r  /**\r     * Get an index number for a column from a string name.\r         * An exception of type SQLbadColName will be thrown if\r         * the name given is invalid.\r   * @param column The column name to get the ID of\r       * @returns The ID number of the column provided\r        */\r    virtual int ColNum(const std::string &column) = 0;\r\r    /**\r     * Get a string value in a given row and column\r         * This does not effect the internal cursor.\r    * @returns The value stored at [row,column] in the table\r       */\r    virtual SQLfield GetValue(int row, int column) = 0;\r\r   /**\r     * Return a list of values in a row, this should\r        * increment an internal counter so you can repeatedly\r  * call it until it returns an empty vector.\r    * This returns a reference to an internal object,\r      * the same object is used for all calls to this function\r       * and therefore the return value is only valid until\r   * you call this function again. It is also invalid if\r  * the SQLresult object is destroyed.\r   * The internal cursor (row counter) is incremented by one.\r     * @returns A reference to the current row's SQLfieldList\r       */\r    virtual SQLfieldList& GetRow() = 0;\r\r   /**\r     * As above, but return a map indexed by key name.\r      * The internal cursor (row counter) is incremented by one.\r     * @returns A reference to the current row's SQLfieldMap\r        */\r    virtual SQLfieldMap& GetRowMap() = 0;\r\r /**\r     * Like GetRow(), but returns a pointer to a dynamically\r        * allocated object which must be explicitly freed. For\r         * portability reasons this must be freed with SQLresult::Free()\r        * The internal cursor (row counter) is incremented by one.\r     * @returns A newly-allocated SQLfieldList\r      */\r    virtual SQLfieldList* GetRowPtr() = 0;\r\r        /**\r     * As above, but return a map indexed by key name\r       * The internal cursor (row counter) is incremented by one.\r     * @returns A newly-allocated SQLfieldMap\r       */\r    virtual SQLfieldMap* GetRowMapPtr() = 0;\r\r      /**\r     * Overloaded function for freeing the lists and maps\r   * returned by GetRowPtr or GetRowMapPtr.\r       * @param fm The SQLfieldMap to free\r    */\r    virtual void Free(SQLfieldMap* fm) = 0;\r\r       /**\r     * Overloaded function for freeing the lists and maps\r   * returned by GetRowPtr or GetRowMapPtr.\r       * @param fl The SQLfieldList to free\r   */\r    virtual void Free(SQLfieldList* fl) = 0;\r};\r\r\r/** SQLHost represents a <database> config line and is useful\r * for storing in a map and iterating on rehash to see which\r * <database> tags was added/removed/unchanged.\r */\rclass SQLhost\r{\r public:\r  std::string             id;             /* Database handle id */\r       std::string             host;   /* Database server hostname */\r std::string             ip;             /* resolved IP, needed for at least pgsql.so */\r        unsigned int    port;   /* Database server port */\r     std::string             name;   /* Database name */\r    std::string             user;   /* Database username */\r        std::string             pass;   /* Database password */\r        bool                    ssl;    /* If we should require SSL */\r\r        SQLhost()\r      {\r      }\r\r     SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s)\r : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s)\r  {\r      }\r\r     /** Overload this to return a correct Data source Name (DSN) for\r        * the current SQL module.\r      */\r    std::string GetDSN();\r};\r\r/** Overload operator== for two SQLhost objects for easy comparison.\r */\rbool operator== (const SQLhost& l, const SQLhost& r)\r{\r      return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl);\r}\r\r\r/** QueryQueue, a queue of queries waiting to be executed.\r * This maintains two queues internally, one for 'priority'\r * queries and one for less important ones. Each queue has\r * new queries appended to it and ones to execute are popped\r * off the front. This keeps them flowing round nicely and no\r * query should ever get 'stuck' for too long. If there are\r * queries in the priority queue they will be executed first,\r * 'unimportant' queries will only be executed when the\r * priority queue is empty.\r *\r * We store lists of SQLrequest's here, by value as we want to avoid storing\r * any data allocated inside the client module (in case that module is unloaded\r * while the query is in progress).\r *\r * Because we want to work on the current SQLrequest in-situ, we need a way\r * of accessing the request we are currently processing, QueryQueue::front(),\r * but that call needs to always return the same request until that request\r * is removed from the queue, this is what the 'which' variable is. New queries are\r * always added to the back of one of the two queues, but if when front()\r * is first called then the priority queue is empty then front() will return\r * a query from the normal queue, but if a query is then added to the priority\r * queue then front() must continue to return the front of the *normal* queue\r * until pop() is called.\r */\r\rclass QueryQueue : public classbase\r{\rprivate:\r typedef std::deque<SQLrequest> ReqDeque;\r\r      ReqDeque priority;      /* The priority queue */\r       ReqDeque normal;        /* The 'normal' queue */\r       enum { PRI, NOR, NON } which;   /* Which queue the currently active element is at the front of */\r\rpublic:\r     QueryQueue()\r   : which(NON)\r   {\r      }\r\r     void push(const SQLrequest &q)\r {\r              if(q.pri)\r                      priority.push_back(q);\r         else\r                   normal.push_back(q);\r   }\r\r     void pop()\r     {\r              if((which == PRI) && priority.size())\r          {\r                      priority.pop_front();\r          }\r              else if((which == NOR) && normal.size())\r               {\r                      normal.pop_front();\r            }\r\r             /* Reset this */\r               which = NON;\r\r          /* Silently do nothing if there was no element to pop() */\r     }\r\r     SQLrequest& front()\r    {\r              switch(which)\r          {\r                      case PRI:\r                              return priority.front();\r                       case NOR:\r                              return normal.front();\r                 default:\r                               if(priority.size())\r                            {\r                                      which = PRI;\r                                   return priority.front();\r                               }\r\r                             if(normal.size())\r                              {\r                                      which = NOR;\r                                   return normal.front();\r                         }\r\r                             /* This will probably result in a segfault,\r                             * but the caller should have checked totalsize()\r                               * first so..meh - moron :p\r                             */\r\r                           return priority.front();\r               }\r      }\r\r     std::pair<int, int> size()\r     {\r              return std::make_pair(priority.size(), normal.size());\r }\r\r     int totalsize()\r        {\r              return priority.size() + normal.size();\r        }\r\r     void PurgeModule(Module* mod)\r  {\r              DoPurgeModule(mod, priority);\r          DoPurgeModule(mod, normal);\r    }\r\rprivate:\r    void DoPurgeModule(Module* mod, ReqDeque& q)\r   {\r              for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++)\r              {\r                      if(iter->GetSource() == mod)\r                   {\r                              if(iter->id == front().id)\r                             {\r                                      /* It's the currently active query.. :x */\r                                     iter->SetSource(NULL);\r                         }\r                              else\r                           {\r                                      /* It hasn't been executed yet..just remove it */\r                                      iter = q.erase(iter);\r                          }\r                      }\r              }\r      }\r};\r\r\r#endif\r
\ No newline at end of file
+/*       +------------------------------------+
+ *       | Inspire Internet Relay Chat Daemon |
+ *       +------------------------------------+
+ *
+ *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
+ * See: http://www.inspircd.org/wiki/index.php/Credits
+ *
+ * This program is free but copyrighted software; see
+ *          the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
+
+#ifndef INSPIRCD_SQLAPI_2
+#define INSPIRCD_SQLAPI_2
+
+#include <string>
+#include <deque>
+#include <map>
+#include "modules.h"
+
+/** Identifiers used to identify Request types
+ */
+#define SQLREQID "SQLv2 Request"
+#define SQLRESID "SQLv2 Result"
+#define SQLSUCCESS "You shouldn't be reading this (success)"
+
+/** Defines the error types which SQLerror may be set to
+ */
+enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };
+
+/** A list of format parameters for an SQLquery object.
+ */
+typedef std::deque<std::string> ParamL;
+
+/** The base class of SQL exceptions
+ */
+class SQLexception : public ModuleException
+{
+ public:
+       SQLexception(const std::string &reason) : ModuleException(reason)
+       {
+       }
+
+       SQLexception() : ModuleException("SQLv2: Undefined exception")
+       {
+       }
+};
+
+/** An exception thrown when a bad column or row name or id is requested
+ */
+class SQLbadColName : public SQLexception
+{
+public:
+       SQLbadColName() : SQLexception("SQLv2: Bad column name")
+       {
+       }
+};
+
+/** SQLerror holds the error state of any SQLrequest or SQLresult.
+ * The error string varies from database software to database software
+ * and should be used to display informational error messages to users.
+ */
+class SQLerror : public classbase
+{
+       /** The error id
+        */
+       SQLerrorNum id;
+       /** The error string
+        */
+       std::string str;
+public:
+       /** Initialize an SQLerror
+        * @param i The error ID to set
+        * @param s The (optional) error string to set
+        */
+       SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
+       : id(i), str(s)
+       {
+       }
+
+       /** Return the ID of the error
+        */
+       SQLerrorNum Id()
+       {
+               return id;
+       }
+
+       /** Set the ID of an error
+        * @param i The new error ID to set
+        * @return the ID which was set
+        */
+       SQLerrorNum Id(SQLerrorNum i)
+       {
+               id = i;
+               return id;
+       }
+
+       /** Set the error string for an error
+        * @param s The new error string to set
+        */
+       void Str(const std::string &s)
+       {
+               str = s;
+       }
+
+       /** Return the error string for an error
+        */
+       const char* Str()
+       {
+               if(str.length())
+                       return str.c_str();
+
+               switch(id)
+               {
+                       case NO_ERROR:
+                               return "No error";
+                       case BAD_DBID:
+                               return "Invalid database ID";
+                       case BAD_CONN:
+                               return "Invalid connection";
+                       case QSEND_FAIL:
+                               return "Sending query failed";
+                       case QREPLY_FAIL:
+                               return "Getting query result failed";
+                       default:
+                               return "Unknown error";
+               }
+       }
+};
+
+/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
+ * C++ has no native type-safe way of having a variable number of arguments to a function,
+ * the workaround for this isn't easy to describe simply, but in a nutshell what's really
+ * happening when - from the above example - you do this:
+ *
+ * SQLrequest foo = SQLrequest(this, target, "databaseid", SQLquery("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42"));
+ *
+ * what's actually happening is functionally this:
+ *
+ * SQLrequest foo = SQLrequest(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
+ *
+ * with 'query()' returning a reference to an object with a 'addparam()' member function which
+ * in turn returns a reference to that object. There are actually four ways you can create a
+ * SQLrequest..all have their disadvantages and advantages. In the real implementations the
+ * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
+ * the query string and a ParamL (std::deque<std::string>) of query parameters.
+ * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
+ *
+ * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
+ */
+class SQLquery : public classbase
+{
+public:
+       /** The query 'format string'
+        */
+       std::string q;
+       /** The query parameter list
+        * There should be one parameter for every ? character
+        * within the format string shown above.
+        */
+       ParamL p;
+
+       /** Initialize an SQLquery with a given format string only
+        */
+       SQLquery(const std::string &query)
+       : q(query)
+       {
+       }
+
+       /** Initialize an SQLquery with a format string and parameters.
+        * If you provide parameters, you must initialize the list yourself
+        * if you choose to do it via this method, using std::deque::push_back().
+        */
+       SQLquery(const std::string &query, const ParamL &params)
+       : q(query), p(params)
+       {
+       }
+
+       /** An overloaded operator for pushing parameters onto the parameter list
+        */
+       template<typename T> SQLquery& operator,(const T &foo)
+       {
+               p.push_back(ConvToStr(foo));
+               return *this;
+       }
+
+       /** An overloaded operator for pushing parameters onto the parameter list.
+        * This has higher precedence than 'operator,' and can save on parenthesis.
+        */
+       template<typename T> SQLquery& operator%(const T &foo)
+       {
+               p.push_back(ConvToStr(foo));
+               return *this;
+       }
+};
+
+/** SQLrequest is sent to the SQL API to command it to run a query and return the result.
+ * You must instantiate this object with a valid SQLquery object and its parameters, then
+ * send it using its Send() method to the module providing the 'SQL' feature. To find this
+ * module, use Server::FindFeature().
+ */
+class SQLrequest : public Request
+{
+public:
+       /** The fully parsed and expanded query string
+        * This is initialized from the SQLquery parameter given in the constructor.
+        */
+       SQLquery query;
+       /** The database ID to apply the request to
+        */
+       std::string dbid;
+       /** True if this is a priority query.
+        * Priority queries may 'queue jump' in the request queue.
+        */
+       bool pri;
+       /** The query ID, assigned by the SQL api.
+        * After your request is processed, this will
+        * be initialized for you by the API to a valid request ID,
+        * except in the case of an error.
+        */
+       unsigned long id;
+       /** If an error occured, error.id will be any other value than NO_ERROR.
+        */
+       SQLerror error;
+
+       /** Initialize an SQLrequest.
+        * For example:
+        *
+        * SQLrequest req = SQLrequest(MyMod, SQLModule, dbid, SQLquery("INSERT INTO ircd_log_actors VALUES('','?')" % nick));
+        *
+        * @param s A pointer to the sending module, where the result should be routed
+        * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
+        * @param databaseid The database ID to perform the query on. This must match a valid
+        * database ID from the configuration of the SQL module.
+        * @param q A properly initialized SQLquery object.
+        */
+       SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
+       : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
+       {
+       }
+
+       /** Set the priority of a request.
+        */
+       void Priority(bool p = true)
+       {
+               pri = p;
+       }
+
+       /** Set the source of a request. You should not need to use this method.
+        */
+       void SetSource(Module* mod)
+       {
+               source = mod;
+       }
+};
+
+/**
+ * This class contains a field's data plus a way to determine if the field
+ * is NULL or not without having to mess around with NULL pointers.
+ */
+class SQLfield
+{
+public:
+       /**
+        * The data itself
+        */
+       std::string d;
+
+       /**
+        * If the field was null
+        */
+       bool null;
+
+       /** Initialize an SQLfield
+        */
+       SQLfield(const std::string &data = "", bool n = false)
+       : d(data), null(n)
+       {
+
+       }
+};
+
+/** A list of items which make up a row of a result or table (tuple)
+ * This does not include field names.
+ */
+typedef std::vector<SQLfield> SQLfieldList;
+/** A list of items which make up a row of a result or table (tuple)
+ * This also includes the field names.
+ */
+typedef std::map<std::string, SQLfield> SQLfieldMap;
+
+/** SQLresult is a reply to a previous query.
+ * If you send a query to the SQL api, the response will arrive at your
+ * OnRequest method of your module at some later time, depending on the
+ * congestion of the SQL server and complexity of the query. The ID of
+ * this result will match the ID assigned to your original request.
+ * SQLresult contains its own internal cursor (row counter) which is
+ * incremented with each method call which retrieves a single row.
+ */
+class SQLresult : public Request
+{
+public:
+       /** The original query string passed initially to the SQL API
+        */
+       std::string query;
+       /** The database ID the query was executed on
+        */
+       std::string dbid;
+       /**
+        * The error (if any) which occured.
+        * If an error occured the value of error.id will be any
+        * other value than NO_ERROR.
+        */
+       SQLerror error;
+       /**
+        * This will match  query ID you were given when sending
+        * the request at an earlier time.
+        */
+       unsigned long id;
+
+       /** Used by the SQL API to instantiate an SQLrequest
+        */
+       SQLresult(Module* s, Module* d, unsigned long i)
+       : Request(s, d, SQLRESID), id(i)
+       {
+       }
+
+       /**
+        * Return the number of rows in the result
+        * Note that if you have perfomed an INSERT
+        * or UPDATE query or other query which will
+        * not return rows, this will return the
+        * number of affected rows, and SQLresult::Cols()
+        * will contain 0. In this case you SHOULD NEVER
+        * access any of the result set rows, as there arent any!
+        * @returns Number of rows in the result set.
+        */
+       virtual int Rows() = 0;
+
+       /**
+        * Return the number of columns in the result.
+        * If you performed an UPDATE or INSERT which
+        * does not return a dataset, this value will
+        * be 0.
+        * @returns Number of columns in the result set.
+        */
+       virtual int Cols() = 0;
+
+       /**
+        * Get a string name of the column by an index number
+        * @param column The id number of a column
+        * @returns The column name associated with the given ID
+        */
+       virtual std::string ColName(int column) = 0;
+
+       /**
+        * Get an index number for a column from a string name.
+        * An exception of type SQLbadColName will be thrown if
+        * the name given is invalid.
+        * @param column The column name to get the ID of
+        * @returns The ID number of the column provided
+        */
+       virtual int ColNum(const std::string &column) = 0;
+
+       /**
+        * Get a string value in a given row and column
+        * This does not effect the internal cursor.
+        * @returns The value stored at [row,column] in the table
+        */
+       virtual SQLfield GetValue(int row, int column) = 0;
+
+       /**
+        * Return a list of values in a row, this should
+        * increment an internal counter so you can repeatedly
+        * call it until it returns an empty vector.
+        * This returns a reference to an internal object,
+        * the same object is used for all calls to this function
+        * and therefore the return value is only valid until
+        * you call this function again. It is also invalid if
+        * the SQLresult object is destroyed.
+        * The internal cursor (row counter) is incremented by one.
+        * @returns A reference to the current row's SQLfieldList
+        */
+       virtual SQLfieldList& GetRow() = 0;
+
+       /**
+        * As above, but return a map indexed by key name.
+        * The internal cursor (row counter) is incremented by one.
+        * @returns A reference to the current row's SQLfieldMap
+        */
+       virtual SQLfieldMap& GetRowMap() = 0;
+
+       /**
+        * Like GetRow(), but returns a pointer to a dynamically
+        * allocated object which must be explicitly freed. For
+        * portability reasons this must be freed with SQLresult::Free()
+        * The internal cursor (row counter) is incremented by one.
+        * @returns A newly-allocated SQLfieldList
+        */
+       virtual SQLfieldList* GetRowPtr() = 0;
+
+       /**
+        * As above, but return a map indexed by key name
+        * The internal cursor (row counter) is incremented by one.
+        * @returns A newly-allocated SQLfieldMap
+        */
+       virtual SQLfieldMap* GetRowMapPtr() = 0;
+
+       /**
+        * Overloaded function for freeing the lists and maps
+        * returned by GetRowPtr or GetRowMapPtr.
+        * @param fm The SQLfieldMap to free
+        */
+       virtual void Free(SQLfieldMap* fm) = 0;
+
+       /**
+        * Overloaded function for freeing the lists and maps
+        * returned by GetRowPtr or GetRowMapPtr.
+        * @param fl The SQLfieldList to free
+        */
+       virtual void Free(SQLfieldList* fl) = 0;
+};
+
+
+/** SQLHost represents a <database> config line and is useful
+ * for storing in a map and iterating on rehash to see which
+ * <database> tags was added/removed/unchanged.
+ */
+class SQLhost
+{
+ public:
+       std::string             id;             /* Database handle id */
+       std::string             host;   /* Database server hostname */
+       std::string             ip;             /* resolved IP, needed for at least pgsql.so */
+       unsigned int    port;   /* Database server port */
+       std::string             name;   /* Database name */
+       std::string             user;   /* Database username */
+       std::string             pass;   /* Database password */
+       bool                    ssl;    /* If we should require SSL */
+
+       SQLhost()
+       {
+       }
+
+       SQLhost(const std::string& i, const std::string& h, unsigned int p, const std::string& n, const std::string& u, const std::string& pa, bool s)
+       : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s)
+       {
+       }
+
+       /** Overload this to return a correct Data source Name (DSN) for
+        * the current SQL module.
+        */
+       std::string GetDSN();
+};
+
+/** Overload operator== for two SQLhost objects for easy comparison.
+ */
+bool operator== (const SQLhost& l, const SQLhost& r)
+{
+       return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == l.user && l.pass == r.pass && l.ssl == r.ssl);
+}
+
+
+/** QueryQueue, a queue of queries waiting to be executed.
+ * This maintains two queues internally, one for 'priority'
+ * queries and one for less important ones. Each queue has
+ * new queries appended to it and ones to execute are popped
+ * off the front. This keeps them flowing round nicely and no
+ * query should ever get 'stuck' for too long. If there are
+ * queries in the priority queue they will be executed first,
+ * 'unimportant' queries will only be executed when the
+ * priority queue is empty.
+ *
+ * We store lists of SQLrequest's here, by value as we want to avoid storing
+ * any data allocated inside the client module (in case that module is unloaded
+ * while the query is in progress).
+ *
+ * Because we want to work on the current SQLrequest in-situ, we need a way
+ * of accessing the request we are currently processing, QueryQueue::front(),
+ * but that call needs to always return the same request until that request
+ * is removed from the queue, this is what the 'which' variable is. New queries are
+ * always added to the back of one of the two queues, but if when front()
+ * is first called then the priority queue is empty then front() will return
+ * a query from the normal queue, but if a query is then added to the priority
+ * queue then front() must continue to return the front of the *normal* queue
+ * until pop() is called.
+ */
+
+class QueryQueue : public classbase
+{
+private:
+       typedef std::deque<SQLrequest> ReqDeque;
+
+       ReqDeque priority;      /* The priority queue */
+       ReqDeque normal;        /* The 'normal' queue */
+       enum { PRI, NOR, NON } which;   /* Which queue the currently active element is at the front of */
+
+public:
+       QueryQueue()
+       : which(NON)
+       {
+       }
+
+       void push(const SQLrequest &q)
+       {
+               if(q.pri)
+                       priority.push_back(q);
+               else
+                       normal.push_back(q);
+       }
+
+       void pop()
+       {
+               if((which == PRI) && priority.size())
+               {
+                       priority.pop_front();
+               }
+               else if((which == NOR) && normal.size())
+               {
+                       normal.pop_front();
+               }
+
+               /* Reset this */
+               which = NON;
+
+               /* Silently do nothing if there was no element to pop() */
+       }
+
+       SQLrequest& front()
+       {
+               switch(which)
+               {
+                       case PRI:
+                               return priority.front();
+                       case NOR:
+                               return normal.front();
+                       default:
+                               if(priority.size())
+                               {
+                                       which = PRI;
+                                       return priority.front();
+                               }
+
+                               if(normal.size())
+                               {
+                                       which = NOR;
+                                       return normal.front();
+                               }
+
+                               /* This will probably result in a segfault,
+                                * but the caller should have checked totalsize()
+                                * first so..meh - moron :p
+                                */
+
+                               return priority.front();
+               }
+       }
+
+       std::pair<int, int> size()
+       {
+               return std::make_pair(priority.size(), normal.size());
+       }
+
+       int totalsize()
+       {
+               return priority.size() + normal.size();
+       }
+
+       void PurgeModule(Module* mod)
+       {
+               DoPurgeModule(mod, priority);
+               DoPurgeModule(mod, normal);
+       }
+
+private:
+       void DoPurgeModule(Module* mod, ReqDeque& q)
+       {
+               for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++)
+               {
+                       if(iter->GetSource() == mod)
+                       {
+                               if(iter->id == front().id)
+                               {
+                                       /* It's the currently active query.. :x */
+                                       iter->SetSource(NULL);
+                               }
+                               else
+                               {
+                                       /* It hasn't been executed yet..just remove it */
+                                       iter = q.erase(iter);
+                               }
+                       }
+               }
+       }
+};
+
+
+#endif