]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sqlv2.h
Make classbase and refcountbase uncopyable; expand comments on their indended uses
[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
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
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         // Copy constructor - XXX probably shouldn't be needed
246         SQLrequest(const SQLrequest& o)
247                 : Request(o.source, o.dest, SQLREQID), query(o.query), dbid(o.dbid), pri(o.pri), cancel(o.cancel),
248                 id(o.id), error(o.error) {}
249
250         /** Set the priority of a request.
251          */
252         void Priority(bool p = true)
253         {
254                 pri = p;
255         }
256 };
257
258 /**
259  * This class contains a field's data plus a way to determine if the field
260  * is NULL or not without having to mess around with NULL pointers.
261  */
262 class SQLfield
263 {
264 public:
265         /**
266          * The data itself
267          */
268         std::string d;
269
270         /**
271          * If the field was null
272          */
273         bool null;
274
275         /** Initialize an SQLfield
276          */
277         SQLfield(const std::string &data = "", bool n = false)
278         : d(data), null(n)
279         {
280
281         }
282 };
283
284 /** A list of items which make up a row of a result or table (tuple)
285  * This does not include field names.
286  */
287 typedef std::vector<SQLfield> SQLfieldList;
288 /** A list of items which make up a row of a result or table (tuple)
289  * This also includes the field names.
290  */
291 typedef std::map<std::string, SQLfield> SQLfieldMap;
292
293 /** SQLresult is a reply to a previous query.
294  * If you send a query to the SQL api, the response will arrive at your
295  * OnRequest method of your module at some later time, depending on the
296  * congestion of the SQL server and complexity of the query. The ID of
297  * this result will match the ID assigned to your original request.
298  * SQLresult contains its own internal cursor (row counter) which is
299  * incremented with each method call which retrieves a single row.
300  */
301 class SQLresult : public Request
302 {
303 public:
304         /** The original query string passed initially to the SQL API
305          */
306         std::string query;
307         /** The database ID the query was executed on
308          */
309         std::string dbid;
310         /**
311          * The error (if any) which occured.
312          * If an error occured the value of error.id will be any
313          * other value than SQL_NO_ERROR.
314          */
315         SQLerror error;
316         /**
317          * This will match  query ID you were given when sending
318          * the request at an earlier time.
319          */
320         unsigned long id;
321
322         /** Used by the SQL API to instantiate an SQLrequest
323          */
324         SQLresult(Module* s, Module* d, unsigned long i)
325         : Request(s, d, SQLRESID), id(i)
326         {
327         }
328
329         /**
330          * Return the number of rows in the result
331          * Note that if you have perfomed an INSERT
332          * or UPDATE query or other query which will
333          * not return rows, this will return the
334          * number of affected rows, and SQLresult::Cols()
335          * will contain 0. In this case you SHOULD NEVER
336          * access any of the result set rows, as there arent any!
337          * @returns Number of rows in the result set.
338          */
339         virtual int Rows() = 0;
340
341         /**
342          * Return the number of columns in the result.
343          * If you performed an UPDATE or INSERT which
344          * does not return a dataset, this value will
345          * be 0.
346          * @returns Number of columns in the result set.
347          */
348         virtual int Cols() = 0;
349
350         /**
351          * Get a string name of the column by an index number
352          * @param column The id number of a column
353          * @returns The column name associated with the given ID
354          */
355         virtual std::string ColName(int column) = 0;
356
357         /**
358          * Get an index number for a column from a string name.
359          * An exception of type SQLbadColName will be thrown if
360          * the name given is invalid.
361          * @param column The column name to get the ID of
362          * @returns The ID number of the column provided
363          */
364         virtual int ColNum(const std::string &column) = 0;
365
366         /**
367          * Get a string value in a given row and column
368          * This does not effect the internal cursor.
369          * @returns The value stored at [row,column] in the table
370          */
371         virtual SQLfield GetValue(int row, int column) = 0;
372
373         /**
374          * Return a list of values in a row, this should
375          * increment an internal counter so you can repeatedly
376          * call it until it returns an empty vector.
377          * This returns a reference to an internal object,
378          * the same object is used for all calls to this function
379          * and therefore the return value is only valid until
380          * you call this function again. It is also invalid if
381          * the SQLresult object is destroyed.
382          * The internal cursor (row counter) is incremented by one.
383          * @returns A reference to the current row's SQLfieldList
384          */
385         virtual SQLfieldList& GetRow() = 0;
386
387         /**
388          * As above, but return a map indexed by key name.
389          * The internal cursor (row counter) is incremented by one.
390          * @returns A reference to the current row's SQLfieldMap
391          */
392         virtual SQLfieldMap& GetRowMap() = 0;
393
394         /**
395          * Like GetRow(), but returns a pointer to a dynamically
396          * allocated object which must be explicitly freed. For
397          * portability reasons this must be freed with SQLresult::Free()
398          * The internal cursor (row counter) is incremented by one.
399          * @returns A newly-allocated SQLfieldList
400          */
401         virtual SQLfieldList* GetRowPtr() = 0;
402
403         /**
404          * As above, but return a map indexed by key name
405          * The internal cursor (row counter) is incremented by one.
406          * @returns A newly-allocated SQLfieldMap
407          */
408         virtual SQLfieldMap* GetRowMapPtr() = 0;
409
410         /**
411          * Overloaded function for freeing the lists and maps
412          * returned by GetRowPtr or GetRowMapPtr.
413          * @param fm The SQLfieldMap to free
414          */
415         virtual void Free(SQLfieldMap* fm) = 0;
416
417         /**
418          * Overloaded function for freeing the lists and maps
419          * returned by GetRowPtr or GetRowMapPtr.
420          * @param fl The SQLfieldList to free
421          */
422         virtual void Free(SQLfieldList* fl) = 0;
423 };
424
425
426 /** SQLHost represents a <database> config line and is useful
427  * for storing in a map and iterating on rehash to see which
428  * <database> tags was added/removed/unchanged.
429  */
430 class SQLhost
431 {
432  public:
433         std::string             id;             /* Database handle id */
434         std::string             host;   /* Database server hostname */
435         std::string             ip;             /* resolved IP, needed for at least pgsql.so */
436         unsigned int    port;   /* Database server port */
437         std::string             name;   /* Database name */
438         std::string             user;   /* Database username */
439         std::string             pass;   /* Database password */
440         bool                    ssl;    /* If we should require SSL */
441
442         SQLhost()
443         : id(""), host(""), ip(""), port(0), name(""), user(""), pass(""), ssl(0)
444         {
445         }
446
447         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)
448         : id(i), host(h), ip(""), port(p), name(n), user(u), pass(pa), ssl(s)
449         {
450         }
451
452         /** Overload this to return a correct Data source Name (DSN) for
453          * the current SQL module.
454          */
455         std::string GetDSN();
456 };
457
458 /** Overload operator== for two SQLhost objects for easy comparison.
459  */
460 bool operator== (const SQLhost& l, const SQLhost& r)
461 {
462         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);
463 }
464 /** Overload operator!= for two SQLhost objects for easy comparison.
465  */
466 bool operator!= (const SQLhost& l, const SQLhost& r)
467 {
468         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);
469 }
470
471
472 /** QueryQueue, a queue of queries waiting to be executed.
473  * This maintains two queues internally, one for 'priority'
474  * queries and one for less important ones. Each queue has
475  * new queries appended to it and ones to execute are popped
476  * off the front. This keeps them flowing round nicely and no
477  * query should ever get 'stuck' for too long. If there are
478  * queries in the priority queue they will be executed first,
479  * 'unimportant' queries will only be executed when the
480  * priority queue is empty.
481  *
482  * We store lists of SQLrequest's here, by value as we want to avoid storing
483  * any data allocated inside the client module (in case that module is unloaded
484  * while the query is in progress).
485  *
486  * Because we want to work on the current SQLrequest in-situ, we need a way
487  * of accessing the request we are currently processing, QueryQueue::front(),
488  * but that call needs to always return the same request until that request
489  * is removed from the queue, this is what the 'which' variable is. New queries are
490  * always added to the back of one of the two queues, but if when front()
491  * is first called then the priority queue is empty then front() will return
492  * a query from the normal queue, but if a query is then added to the priority
493  * queue then front() must continue to return the front of the *normal* queue
494  * until pop() is called.
495  */
496
497 class QueryQueue
498 {
499 private:
500         typedef std::deque<SQLrequest*> ReqDeque;
501
502         ReqDeque priority;      /* The priority queue */
503         ReqDeque normal;        /* The 'normal' queue */
504         enum { PRI, NOR, NON } which;   /* Which queue the currently active element is at the front of */
505
506 public:
507         QueryQueue()
508         : which(NON)
509         {
510         }
511
512         void push(SQLrequest *q)
513         {
514                 if(q->pri)
515                         priority.push_back(q);
516                 else
517                         normal.push_back(q);
518         }
519
520         void pop()
521         {
522                 if((which == PRI) && priority.size())
523                 {
524                         priority.pop_front();
525                 }
526                 else if((which == NOR) && normal.size())
527                 {
528                         normal.pop_front();
529                 }
530
531                 /* Reset this */
532                 which = NON;
533
534                 /* Silently do nothing if there was no element to pop() */
535         }
536
537         SQLrequest* front()
538         {
539                 switch(which)
540                 {
541                         case PRI:
542                                 return priority.front();
543                         case NOR:
544                                 return normal.front();
545                         default:
546                                 if(priority.size())
547                                 {
548                                         which = PRI;
549                                         return priority.front();
550                                 }
551
552                                 if(normal.size())
553                                 {
554                                         which = NOR;
555                                         return normal.front();
556                                 }
557                 }
558                 return NULL;
559         }
560
561         std::pair<int, int> size()
562         {
563                 return std::make_pair(priority.size(), normal.size());
564         }
565
566         int totalsize()
567         {
568                 return priority.size() + normal.size();
569         }
570
571         void PurgeModule(Module* mod)
572         {
573                 DoPurgeModule(mod, priority);
574                 DoPurgeModule(mod, normal);
575         }
576
577 private:
578         void DoPurgeModule(Module* mod, ReqDeque& q)
579         {
580                 ReqDeque::iterator iter = q.begin();
581                 while (iter != q.end())
582                 {
583                         if((**iter).source == mod)
584                         {
585                                 if (*iter == front())
586                                 {
587                                         /* It's the currently active query.. :x */
588                                         (**iter).cancel = true;
589                                         iter++;
590                                 }
591                                 else
592                                 {
593                                         /* It hasn't been executed yet..just remove it */
594                                         iter = q.erase(iter);
595                                 }
596                         }
597                         else
598                                 iter++;
599                 }
600         }
601 };
602
603
604 #endif