]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/extra/m_sqlv2.h
The rest of the server protocol interface and fix a warning in m_rline
[user/henk/code/inspircd.git] / src / modules / extra / m_sqlv2.h
index bce43785858f7fd8f3ac4a3c1a6e3d143b57cd2a..2e0ab2a1931eaa60777a6026f184667da382a26a 100644 (file)
@@ -1,3 +1,16 @@
+/*       +------------------------------------+
+ *       | 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
 
@@ -6,12 +19,6 @@
 #include <map>
 #include "modules.h"
 
-/** SQLreq define.
- * This is the voodoo magic which lets us pass multiple
- * parameters to the SQLrequest constructor... voodoo...
- */
-#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))
-
 /** Identifiers used to identify Request types
  */
 #define SQLREQID "SQLv2 Request"
@@ -30,6 +37,14 @@ typedef std::deque<std::string> ParamL;
  */
 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
@@ -37,7 +52,9 @@ class SQLexception : public ModuleException
 class SQLbadColName : public SQLexception
 {
 public:
-       SQLbadColName() { }
+       SQLbadColName() : SQLexception("SQLv2: Bad column name")
+       {
+       }
 };
 
 /** SQLerror holds the error state of any SQLrequest or SQLresult.
@@ -59,16 +76,16 @@ public:
         */
        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
@@ -78,7 +95,7 @@ public:
                id = i;
                return id;
        }
-       
+
        /** Set the error string for an error
         * @param s The new error string to set
         */
@@ -86,14 +103,14 @@ public:
        {
                str = s;
        }
-       
+
        /** Return the error string for an error
         */
        const char* Str()
        {
                if(str.length())
                        return str.c_str();
-               
+
                switch(id)
                {
                        case NO_ERROR:
@@ -107,7 +124,7 @@ public:
                        case QREPLY_FAIL:
                                return "Getting query result failed";
                        default:
-                               return "Unknown error";                         
+                               return "Unknown error";
                }
        }
 };
@@ -117,11 +134,11 @@ public:
  * 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 = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");
+ * 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 = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
+ * 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
@@ -132,7 +149,7 @@ public:
  *
  * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
  */
-class SQLquery
+class SQLquery : public classbase
 {
 public:
        /** The query 'format string'
@@ -158,22 +175,22 @@ public:
        SQLquery(const std::string &query, const ParamL &params)
        : q(query), p(params)
        {
-       }       
-       
+       }
+
        /** An overloaded operator for pushing parameters onto the parameter list
         */
-       SQLquery& operator,(const std::string &foo)
+       template<typename T> SQLquery& operator,(const T &foo)
        {
-               p.push_back(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.
         */
-       SQLquery& operator%(const std::string &foo)
+       template<typename T> SQLquery& operator%(const T &foo)
        {
-               p.push_back(foo);
+               p.push_back(ConvToStr(foo));
                return *this;
        }
 };
@@ -206,11 +223,11 @@ public:
        /** If an error occured, error.id will be any other value than NO_ERROR.
         */
        SQLerror error;
-       
+
        /** Initialize an SQLrequest.
         * For example:
         *
-        * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);
+        * 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
@@ -222,14 +239,14 @@ public:
        : 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)
@@ -260,7 +277,7 @@ public:
        SQLfield(const std::string &data = "", bool n = false)
        : d(data), null(n)
        {
-               
+
        }
 };
 
@@ -295,7 +312,7 @@ public:
         * If an error occured the value of error.id will be any
         * other value than NO_ERROR.
         */
-       SQLerror error; 
+       SQLerror error;
        /**
         * This will match  query ID you were given when sending
         * the request at an earlier time.
@@ -308,7 +325,7 @@ public:
        : Request(s, d, SQLRESID), id(i)
        {
        }
-       
+
        /**
         * Return the number of rows in the result
         * Note that if you have perfomed an INSERT
@@ -320,7 +337,7 @@ public:
         * @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
@@ -329,14 +346,14 @@ public:
         * @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
@@ -345,14 +362,14 @@ public:
         * @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
@@ -366,14 +383,14 @@ public:
         * @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
@@ -382,14 +399,14 @@ public:
         * @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.
@@ -405,4 +422,178 @@ public:
        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