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