]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sqlv2.h
bcdcb6546fe6e2b3a7e018c32379e84aef158855
[user/henk/code/inspircd.git] / src / modules / m_sqlv2.h
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #ifndef INSPIRCD_SQLAPI_2
15 #define INSPIRCD_SQLAPI_2
16
17 #include <string>
18 #include <deque>
19 #include <map>
20 #include "modules.h"
21
22 /** Identifiers used to identify Request types
23  */
24 #define SQLREQID "SQLv2 Request"
25 #define SQLRESID "SQLv2 Result"
26 #define SQLSUCCESS "You shouldn't be reading this (success)"
27
28 /** Defines the error types which SQLerror may be set to
29  */
30 enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
31
32 /** A list of format parameters for an SQLquery object.
33  */
34 typedef std::deque<std::string> ParamL;
35
36 /** The base class of SQL exceptions
37  */
38 class SQLexception : public ModuleException
39 {
40  public:
41         SQLexception(const std::string &reason) : ModuleException(reason)
42         {
43         }
44
45         SQLexception() : ModuleException("SQLv2: Undefined exception")
46         {
47         }
48 };
49
50 /** An exception thrown when a bad column or row name or id is requested
51  */
52 class SQLbadColName : public SQLexception
53 {
54 public:
55         SQLbadColName() : SQLexception("SQLv2: Bad column name")
56         {
57         }
58 };
59
60 /** SQLerror holds the error state of any SQLrequest or SQLresult.
61  * The error string varies from database software to database software
62  * and should be used to display informational error messages to users.
63  */
64 class SQLerror : public classbase
65 {
66         /** The error id
67          */
68         SQLerrorNum id;
69         /** The error string
70          */
71         std::string str;
72 public:
73         /** Initialize an SQLerror
74          * @param i The error ID to set
75          * @param s The (optional) error string to set
76          */
77         SQLerror(SQLerrorNum i = SQL_NO_ERROR, const std::string &s = "")
78         : id(i), str(s)
79         {
80         }
81
82         /** Return the ID of the error
83          */
84         SQLerrorNum Id()
85         {
86                 return id;
87         }
88
89         /** Set the ID of an error
90          * @param i The new error ID to set
91          * @return the ID which was set
92          */
93         SQLerrorNum Id(SQLerrorNum i)
94         {
95                 id = i;
96                 return id;
97         }
98
99         /** Set the error string for an error
100          * @param s The new error string to set
101          */
102         void Str(const std::string &s)
103         {
104                 str = s;
105         }
106
107         /** Return the error string for an error
108          */
109         const char* Str()
110         {
111                 if(str.length())
112                         return str.c_str();
113
114                 switch(id)
115                 {
116                         case SQL_NO_ERROR:
117                                 return "No error";
118                         case SQL_BAD_DBID:
119                                 return "Invalid database ID";
120                         case SQL_BAD_CONN:
121                                 return "Invalid connection";
122                         case SQL_QSEND_FAIL:
123                                 return "Sending query failed";
124                         case SQL_QREPLY_FAIL:
125                                 return "Getting query result failed";
126                         default:
127                                 return "Unknown error";
128                 }
129         }
130 };
131
132 /** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
133  * C++ has no native type-safe way of having a variable number of arguments to a function,
134  * the workaround for this isn't easy to describe simply, but in a nutshell what's really
135  * happening when - from the above example - you do this:
136  *
137  * SQLrequest foo = SQLrequest(this, target, "databaseid", SQLquery("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42"));
138  *
139  * what's actually happening is functionally this:
140  *
141  * SQLrequest foo = SQLrequest(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
142  *
143  * with 'query()' returning a reference to an object with a 'addparam()' member function which
144  * in turn returns a reference to that object. There are actually four ways you can create a
145  * SQLrequest..all have their disadvantages and advantages. In the real implementations the
146  * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
147  * the query string and a ParamL (std::deque<std::string>) of query parameters.
148  * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
149  *
150  * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
151  */
152 class SQLquery : public classbase
153 {
154 public:
155         /** The query 'format string'
156          */
157         std::string q;
158         /** The query parameter list
159          * There should be one parameter for every ? character
160          * within the format string shown above.
161          */
162         ParamL p;
163
164         /** Initialize an SQLquery with a given format string only
165          */
166         SQLquery(const std::string &query)
167         : q(query)
168         {
169         }
170
171         /** Initialize an SQLquery with a format string and parameters.
172          * If you provide parameters, you must initialize the list yourself
173          * if you choose to do it via this method, using std::deque::push_back().
174          */
175         SQLquery(const std::string &query, const ParamL &params)
176         : q(query), p(params)
177         {
178         }
179
180         /** An overloaded operator for pushing parameters onto the parameter list
181          */
182         template<typename T> SQLquery& operator,(const T &foo)
183         {
184                 p.push_back(ConvToStr(foo));
185                 return *this;
186         }
187
188         /** An overloaded operator for pushing parameters onto the parameter list.
189          * This has higher precedence than 'operator,' and can save on parenthesis.
190          */
191         template<typename T> SQLquery& operator%(const T &foo)
192         {
193                 p.push_back(ConvToStr(foo));
194                 return *this;
195         }
196 };
197
198 /** SQLrequest is sent to the SQL API to command it to run a query and return the result.
199  * You must instantiate this object with a valid SQLquery object and its parameters, then
200  * send it using its Send() method to the module providing the 'SQL' feature. To find this
201  * module, use Server::FindFeature().
202  */
203 class SQLrequest : public Request
204 {
205 public:
206         /** The fully parsed and expanded query string
207          * This is initialized from the SQLquery parameter given in the constructor.
208          */
209         SQLquery query;
210         /** The database ID to apply the request to
211          */
212         std::string dbid;
213         /** True if this is a priority query.
214          * Priority queries may 'queue jump' in the request queue.
215          */
216         bool pri;
217         /** True if this query has been cancelled; send no response */
218         bool cancel;
219         /** The query ID, assigned by the SQL api.
220          * After your request is processed, this will
221          * be initialized for you by the API to a valid request ID,
222          * except in the case of an error.
223          */
224         unsigned long id;
225         /** If an error occured, error.id will be any other value than SQL_NO_ERROR.
226          */
227         SQLerror error;
228
229         /** Initialize an SQLrequest.
230          * For example:
231          *
232          * SQLrequest req = SQLrequest(MyMod, SQLModule, dbid, SQLquery("INSERT INTO ircd_log_actors VALUES('','?')" % nick));
233          *
234          * @param s A pointer to the sending module, where the result should be routed
235          * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
236          * @param databaseid The database ID to perform the query on. This must match a valid
237          * database ID from the configuration of the SQL module.
238          * @param q A properly initialized SQLquery object.
239          */
240         SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
241         : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
242         {
243         }
244
245         /** Set the priority of a request.
246          */
247         void Priority(bool p = true)
248         {
249                 pri = p;
250         }
251 };
252
253 /**
254  * This class contains a field's data plus a way to determine if the field
255  * is NULL or not without having to mess around with NULL pointers.
256  */
257 class SQLfield
258 {
259 public:
260         /**
261          * The data itself
262          */
263         std::string d;
264
265         /**
266          * If the field was null
267          */
268         bool null;
269
270         /** Initialize an SQLfield
271          */
272         SQLfield(const std::string &data = "", bool n = false)
273         : d(data), null(n)
274         {
275
276         }
277 };
278
279 /** A list of items which make up a row of a result or table (tuple)
280  * This does not include field names.
281  */
282 typedef std::vector<SQLfield> SQLfieldList;
283 /** A list of items which make up a row of a result or table (tuple)
284  * This also includes the field names.
285  */
286 typedef std::map<std::string, SQLfield> SQLfieldMap;
287
288 /** SQLresult is a reply to a previous query.
289  * If you send a query to the SQL api, the response will arrive at your
290  * OnRequest method of your module at some later time, depending on the
291  * congestion of the SQL server and complexity of the query. The ID of
292  * this result will match the ID assigned to your original request.
293  * SQLresult contains its own internal cursor (row counter) which is
294  * incremented with each method call which retrieves a single row.
295  */
296 class SQLresult : public Request
297 {
298 public:
299         /** The original query string passed initially to the SQL API
300          */
301         std::string query;
302         /** The database ID the query was executed on
303          */
304         std::string dbid;
305         /**
306          * The error (if any) which occured.
307          * If an error occured the value of error.id will be any
308          * other value than SQL_NO_ERROR.
309          */
310         SQLerror error;
311         /**
312          * This will match  query ID you were given when sending
313          * the request at an earlier time.
314          */
315         unsigned long id;
316
317         /** Used by the SQL API to instantiate an SQLrequest
318          */
319         SQLresult(Module* s, Module* d, unsigned long i)
320         : Request(s, d, SQLRESID), id(i)
321         {
322         }
323
324         /**
325          * Return the number of rows in the result
326          * Note that if you have perfomed an INSERT
327          * or UPDATE query or other query which will
328          * not return rows, this will return the
329          * number of affected rows, and SQLresult::Cols()
330          * will contain 0. In this case you SHOULD NEVER
331          * access any of the result set rows, as there arent any!
332          * @returns Number of rows in the result set.
333          */
334         virtual int Rows() = 0;
335
336         /**
337          * Return the number of columns in the result.
338          * If you performed an UPDATE or INSERT which
339          * does not return a dataset, this value will
340          * be 0.
341          * @returns Number of columns in the result set.
342          */
343         virtual int Cols() = 0;
344
345         /**
346          * Get a string name of the column by an index number
347          * @param column The id number of a column
348          * @returns The column name associated with the given ID
349          */
350         virtual std::string ColName(int column) = 0;
351
352         /**
353          * Get an index number for a column from a string name.
354          * An exception of type SQLbadColName will be thrown if
355          * the name given is invalid.
356          * @param column The column name to get the ID of
357          * @returns The ID number of the column provided
358          */
359         virtual int ColNum(const std::string &column) = 0;
360
361         /**
362          * Get a string value in a given row and column
363          * This does not effect the internal cursor.
364          * @returns The value stored at [row,column] in the table
365          */
366         virtual SQLfield GetValue(int row, int column) = 0;
367
368         /**
369          * Return a list of values in a row, this should
370          * increment an internal counter so you can repeatedly
371          * call it until it returns an empty vector.
372          * This returns a reference to an internal object,
373          * the same object is used for all calls to this function
374          * and therefore the return value is only valid until
375          * you call this function again. It is also invalid if
376          * the SQLresult object is destroyed.
377          * The internal cursor (row counter) is incremented by one.
378          * @returns A reference to the current row's SQLfieldList
379          */
380         virtual SQLfieldList& GetRow() = 0;
381
382         /**
383          * As above, but return a map indexed by key name.
384          * The internal cursor (row counter) is incremented by one.
385          * @returns A reference to the current row's SQLfieldMap
386          */
387         virtual SQLfieldMap& GetRowMap() = 0;
388
389         /**
390          * Like GetRow(), but returns a pointer to a dynamically
391          * allocated object which must be explicitly freed. For
392          * portability reasons this must be freed with SQLresult::Free()
393          * The internal cursor (row counter) is incremented by one.
394          * @returns A newly-allocated SQLfieldList
395          */
396         virtual SQLfieldList* GetRowPtr() = 0;
397
398         /**
399          * As above, but return a map indexed by key name
400          * The internal cursor (row counter) is incremented by one.
401          * @returns A newly-allocated SQLfieldMap
402          */
403         virtual SQLfieldMap* GetRowMapPtr() = 0;
404
405         /**
406          * Overloaded function for freeing the lists and maps
407          * returned by GetRowPtr or GetRowMapPtr.
408          * @param fm The SQLfieldMap to free
409          */
410         virtual void Free(SQLfieldMap* fm) = 0;
411
412         /**
413          * Overloaded function for freeing the lists and maps
414          * returned by GetRowPtr or GetRowMapPtr.
415          * @param fl The SQLfieldList to free
416          */
417         virtual void Free(SQLfieldList* fl) = 0;
418 };
419
420
421 /** SQLHost represents a <database> config line and is useful
422  * for storing in a map and iterating on rehash to see which
423  * <database> tags was added/removed/unchanged.
424  */
425 class SQLhost
426 {
427  public:
428         std::string             id;             /* Database handle id */
429         std::string             host;   /* Database server hostname */
430         std::string             ip;             /* resolved IP, needed for at least pgsql.so */
431         unsigned int    port;   /* Database server port */
432         std::string             name;   /* Database name */
433         std::string             user;   /* Database username */
434         std::string             pass;   /* Database password */
435         bool                    ssl;    /* If we should require SSL */
436
437         SQLhost()
438         : id(""), host(""), ip(""), port(0), name(""), user(""), pass(""), ssl(0)
439         {
440         }
441
442         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)
443         : id(i), host(h), ip(""), port(p), name(n), user(u), pass(pa), ssl(s)
444         {
445         }
446
447         /** Overload this to return a correct Data source Name (DSN) for
448          * the current SQL module.
449          */
450         std::string GetDSN();
451 };
452
453 /** Overload operator== for two SQLhost objects for easy comparison.
454  */
455 bool operator== (const SQLhost& l, const SQLhost& r)
456 {
457         return (l.id == r.id && l.host == r.host && l.port == r.port && l.name == r.name && l.user == r.user && l.pass == r.pass && l.ssl == r.ssl);
458 }
459 /** Overload operator!= for two SQLhost objects for easy comparison.
460  */
461 bool operator!= (const SQLhost& l, const SQLhost& r)
462 {
463         return (l.id != r.id || l.host != r.host || l.port != r.port || l.name != r.name || l.user != r.user || l.pass != r.pass || l.ssl != r.ssl);
464 }
465
466
467 /** QueryQueue, a queue of queries waiting to be executed.
468  * This maintains two queues internally, one for 'priority'
469  * queries and one for less important ones. Each queue has
470  * new queries appended to it and ones to execute are popped
471  * off the front. This keeps them flowing round nicely and no
472  * query should ever get 'stuck' for too long. If there are
473  * queries in the priority queue they will be executed first,
474  * 'unimportant' queries will only be executed when the
475  * priority queue is empty.
476  *
477  * We store lists of SQLrequest's here, by value as we want to avoid storing
478  * any data allocated inside the client module (in case that module is unloaded
479  * while the query is in progress).
480  *
481  * Because we want to work on the current SQLrequest in-situ, we need a way
482  * of accessing the request we are currently processing, QueryQueue::front(),
483  * but that call needs to always return the same request until that request
484  * is removed from the queue, this is what the 'which' variable is. New queries are
485  * always added to the back of one of the two queues, but if when front()
486  * is first called then the priority queue is empty then front() will return
487  * a query from the normal queue, but if a query is then added to the priority
488  * queue then front() must continue to return the front of the *normal* queue
489  * until pop() is called.
490  */
491
492 class QueryQueue : public classbase
493 {
494 private:
495         typedef std::deque<SQLrequest*> ReqDeque;
496
497         ReqDeque priority;      /* The priority queue */
498         ReqDeque normal;        /* The 'normal' queue */
499         enum { PRI, NOR, NON } which;   /* Which queue the currently active element is at the front of */
500
501 public:
502         QueryQueue()
503         : which(NON)
504         {
505         }
506
507         void push(SQLrequest *q)
508         {
509                 if(q->pri)
510                         priority.push_back(q);
511                 else
512                         normal.push_back(q);
513         }
514
515         void pop()
516         {
517                 if((which == PRI) && priority.size())
518                 {
519                         priority.pop_front();
520                 }
521                 else if((which == NOR) && normal.size())
522                 {
523                         normal.pop_front();
524                 }
525
526                 /* Reset this */
527                 which = NON;
528
529                 /* Silently do nothing if there was no element to pop() */
530         }
531
532         SQLrequest* front()
533         {
534                 switch(which)
535                 {
536                         case PRI:
537                                 return priority.front();
538                         case NOR:
539                                 return normal.front();
540                         default:
541                                 if(priority.size())
542                                 {
543                                         which = PRI;
544                                         return priority.front();
545                                 }
546
547                                 if(normal.size())
548                                 {
549                                         which = NOR;
550                                         return normal.front();
551                                 }
552                 }
553                 return NULL;
554         }
555
556         std::pair<int, int> size()
557         {
558                 return std::make_pair(priority.size(), normal.size());
559         }
560
561         int totalsize()
562         {
563                 return priority.size() + normal.size();
564         }
565
566         void PurgeModule(Module* mod)
567         {
568                 DoPurgeModule(mod, priority);
569                 DoPurgeModule(mod, normal);
570         }
571
572 private:
573         void DoPurgeModule(Module* mod, ReqDeque& q)
574         {
575                 ReqDeque::iterator iter = q.begin();
576                 while (iter != q.end())
577                 {
578                         if((**iter).source == mod)
579                         {
580                                 if (*iter == front())
581                                 {
582                                         /* It's the currently active query.. :x */
583                                         (**iter).cancel = true;
584                                         iter++;
585                                 }
586                                 else
587                                 {
588                                         /* It hasn't been executed yet..just remove it */
589                                         iter = q.erase(iter);
590                                 }
591                         }
592                         else
593                                 iter++;
594                 }
595         }
596 };
597
598
599 #endif