]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/extra/m_sqlv2.h
Now two types of log macro, log() and ilog(). log() assumes an InspIRCd object calle...
[user/henk/code/inspircd.git] / src / modules / extra / m_sqlv2.h
1 #ifndef INSPIRCD_SQLAPI_2
2 #define INSPIRCD_SQLAPI_2
3
4 #include <string>
5 #include <deque>
6 #include <map>
7 #include "modules.h"
8
9 /** SQLreq define.
10  * This is the voodoo magic which lets us pass multiple
11  * parameters to the SQLrequest constructor... voodoo...
12  */
13 #define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))
14
15 /** Identifiers used to identify Request types
16  */
17 #define SQLREQID "SQLv2 Request"
18 #define SQLRESID "SQLv2 Result"
19 #define SQLSUCCESS "You shouldn't be reading this (success)"
20
21 /** Defines the error types which SQLerror may be set to
22  */
23 enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };
24
25 /** A list of format parameters for an SQLquery object.
26  */
27 typedef std::deque<std::string> ParamL;
28
29 /** The base class of SQL exceptions
30  */
31 class SQLexception : public ModuleException
32 {
33 };
34
35 /** An exception thrown when a bad column or row name or id is requested
36  */
37 class SQLbadColName : public SQLexception
38 {
39 public:
40         SQLbadColName() { }
41 };
42
43 /** SQLerror holds the error state of any SQLrequest or SQLresult.
44  * The error string varies from database software to database software
45  * and should be used to display informational error messages to users.
46  */
47 class SQLerror : public classbase
48 {
49         /** The error id
50          */
51         SQLerrorNum id;
52         /** The error string
53          */
54         std::string str;
55 public:
56         /** Initialize an SQLerror
57          * @param i The error ID to set
58          * @param s The (optional) error string to set
59          */
60         SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
61         : id(i), str(s)
62         {       
63         }
64         
65         /** Return the ID of the error
66          */
67         SQLerrorNum Id()
68         {
69                 return id;
70         }
71         
72         /** Set the ID of an error
73          * @param i The new error ID to set
74          * @return the ID which was set
75          */
76         SQLerrorNum Id(SQLerrorNum i)
77         {
78                 id = i;
79                 return id;
80         }
81         
82         /** Set the error string for an error
83          * @param s The new error string to set
84          */
85         void Str(const std::string &s)
86         {
87                 str = s;
88         }
89         
90         /** Return the error string for an error
91          */
92         const char* Str()
93         {
94                 if(str.length())
95                         return str.c_str();
96                 
97                 switch(id)
98                 {
99                         case NO_ERROR:
100                                 return "No error";
101                         case BAD_DBID:
102                                 return "Invalid database ID";
103                         case BAD_CONN:
104                                 return "Invalid connection";
105                         case QSEND_FAIL:
106                                 return "Sending query failed";
107                         case QREPLY_FAIL:
108                                 return "Getting query result failed";
109                         default:
110                                 return "Unknown error";                         
111                 }
112         }
113 };
114
115 /** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
116  * C++ has no native type-safe way of having a variable number of arguments to a function,
117  * the workaround for this isn't easy to describe simply, but in a nutshell what's really
118  * happening when - from the above example - you do this:
119  *
120  * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");
121  *
122  * what's actually happening is functionally this:
123  *
124  * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
125  *
126  * with 'query()' returning a reference to an object with a 'addparam()' member function which
127  * in turn returns a reference to that object. There are actually four ways you can create a
128  * SQLrequest..all have their disadvantages and advantages. In the real implementations the
129  * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
130  * the query string and a ParamL (std::deque<std::string>) of query parameters.
131  * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
132  *
133  * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
134  */
135 class SQLquery
136 {
137 public:
138         /** The query 'format string'
139          */
140         std::string q;
141         /** The query parameter list
142          * There should be one parameter for every ? character
143          * within the format string shown above.
144          */
145         ParamL p;
146
147         /** Initialize an SQLquery with a given format string only
148          */
149         SQLquery(const std::string &query)
150         : q(query)
151         {
152         }
153
154         /** Initialize an SQLquery with a format string and parameters.
155          * If you provide parameters, you must initialize the list yourself
156          * if you choose to do it via this method, using std::deque::push_back().
157          */
158         SQLquery(const std::string &query, const ParamL &params)
159         : q(query), p(params)
160         {
161         }       
162         
163         /** An overloaded operator for pushing parameters onto the parameter list
164          */
165         SQLquery& operator,(const std::string &foo)
166         {
167                 p.push_back(foo);
168                 return *this;
169         }
170         
171         /** An overloaded operator for pushing parameters onto the parameter list.
172          * This has higher precedence than 'operator,' and can save on parenthesis.
173          */
174         SQLquery& operator%(const std::string &foo)
175         {
176                 p.push_back(foo);
177                 return *this;
178         }
179 };
180
181 /** SQLrequest is sent to the SQL API to command it to run a query and return the result.
182  * You must instantiate this object with a valid SQLquery object and its parameters, then
183  * send it using its Send() method to the module providing the 'SQL' feature. To find this
184  * module, use Server::FindFeature().
185  */
186 class SQLrequest : public Request
187 {
188 public:
189         /** The fully parsed and expanded query string
190          * This is initialized from the SQLquery parameter given in the constructor.
191          */
192         SQLquery query;
193         /** The database ID to apply the request to
194          */
195         std::string dbid;
196         /** True if this is a priority query.
197          * Priority queries may 'queue jump' in the request queue.
198          */
199         bool pri;
200         /** The query ID, assigned by the SQL api.
201          * After your request is processed, this will
202          * be initialized for you by the API to a valid request ID,
203          * except in the case of an error.
204          */
205         unsigned long id;
206         /** If an error occured, error.id will be any other value than NO_ERROR.
207          */
208         SQLerror error;
209         
210         /** Initialize an SQLrequest.
211          * For example:
212          *
213          * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);
214          *
215          * @param s A pointer to the sending module, where the result should be routed
216          * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
217          * @param databaseid The database ID to perform the query on. This must match a valid
218          * database ID from the configuration of the SQL module.
219          * @param q A properly initialized SQLquery object.
220          */
221         SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
222         : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
223         {
224         }
225         
226         /** Set the priority of a request.
227          */
228         void Priority(bool p = true)
229         {
230                 pri = p;
231         }
232         
233         /** Set the source of a request. You should not need to use this method.
234          */
235         void SetSource(Module* mod)
236         {
237                 source = mod;
238         }
239 };
240
241 /**
242  * This class contains a field's data plus a way to determine if the field
243  * is NULL or not without having to mess around with NULL pointers.
244  */
245 class SQLfield
246 {
247 public:
248         /**
249          * The data itself
250          */
251         std::string d;
252
253         /**
254          * If the field was null
255          */
256         bool null;
257
258         /** Initialize an SQLfield
259          */
260         SQLfield(const std::string &data = "", bool n = false)
261         : d(data), null(n)
262         {
263                 
264         }
265 };
266
267 /** A list of items which make up a row of a result or table (tuple)
268  * This does not include field names.
269  */
270 typedef std::vector<SQLfield> SQLfieldList;
271 /** A list of items which make up a row of a result or table (tuple)
272  * This also includes the field names.
273  */
274 typedef std::map<std::string, SQLfield> SQLfieldMap;
275
276 /** SQLresult is a reply to a previous query.
277  * If you send a query to the SQL api, the response will arrive at your
278  * OnRequest method of your module at some later time, depending on the
279  * congestion of the SQL server and complexity of the query. The ID of
280  * this result will match the ID assigned to your original request.
281  * SQLresult contains its own internal cursor (row counter) which is
282  * incremented with each method call which retrieves a single row.
283  */
284 class SQLresult : public Request
285 {
286 public:
287         /** The original query string passed initially to the SQL API
288          */
289         std::string query;
290         /** The database ID the query was executed on
291          */
292         std::string dbid;
293         /**
294          * The error (if any) which occured.
295          * If an error occured the value of error.id will be any
296          * other value than NO_ERROR.
297          */
298         SQLerror error; 
299         /**
300          * This will match  query ID you were given when sending
301          * the request at an earlier time.
302          */
303         unsigned long id;
304
305         /** Used by the SQL API to instantiate an SQLrequest
306          */
307         SQLresult(Module* s, Module* d, unsigned long i)
308         : Request(s, d, SQLRESID), id(i)
309         {
310         }
311         
312         /**
313          * Return the number of rows in the result
314          * Note that if you have perfomed an INSERT
315          * or UPDATE query or other query which will
316          * not return rows, this will return the
317          * number of affected rows, and SQLresult::Cols()
318          * will contain 0. In this case you SHOULD NEVER
319          * access any of the result set rows, as there arent any!
320          * @returns Number of rows in the result set.
321          */
322         virtual int Rows() = 0;
323         
324         /**
325          * Return the number of columns in the result.
326          * If you performed an UPDATE or INSERT which
327          * does not return a dataset, this value will
328          * be 0.
329          * @returns Number of columns in the result set.
330          */
331         virtual int Cols() = 0;
332         
333         /**
334          * Get a string name of the column by an index number
335          * @param column The id number of a column
336          * @returns The column name associated with the given ID
337          */
338         virtual std::string ColName(int column) = 0;
339         
340         /**
341          * Get an index number for a column from a string name.
342          * An exception of type SQLbadColName will be thrown if
343          * the name given is invalid.
344          * @param column The column name to get the ID of
345          * @returns The ID number of the column provided
346          */
347         virtual int ColNum(const std::string &column) = 0;
348         
349         /**
350          * Get a string value in a given row and column
351          * This does not effect the internal cursor.
352          * @returns The value stored at [row,column] in the table
353          */
354         virtual SQLfield GetValue(int row, int column) = 0;
355         
356         /**
357          * Return a list of values in a row, this should
358          * increment an internal counter so you can repeatedly
359          * call it until it returns an empty vector.
360          * This returns a reference to an internal object,
361          * the same object is used for all calls to this function
362          * and therefore the return value is only valid until
363          * you call this function again. It is also invalid if
364          * the SQLresult object is destroyed.
365          * The internal cursor (row counter) is incremented by one.
366          * @returns A reference to the current row's SQLfieldList
367          */
368         virtual SQLfieldList& GetRow() = 0;
369         
370         /**
371          * As above, but return a map indexed by key name.
372          * The internal cursor (row counter) is incremented by one.
373          * @returns A reference to the current row's SQLfieldMap
374          */
375         virtual SQLfieldMap& GetRowMap() = 0;
376         
377         /**
378          * Like GetRow(), but returns a pointer to a dynamically
379          * allocated object which must be explicitly freed. For
380          * portability reasons this must be freed with SQLresult::Free()
381          * The internal cursor (row counter) is incremented by one.
382          * @returns A newly-allocated SQLfieldList
383          */
384         virtual SQLfieldList* GetRowPtr() = 0;
385         
386         /**
387          * As above, but return a map indexed by key name
388          * The internal cursor (row counter) is incremented by one.
389          * @returns A newly-allocated SQLfieldMap
390          */
391         virtual SQLfieldMap* GetRowMapPtr() = 0;
392         
393         /**
394          * Overloaded function for freeing the lists and maps
395          * returned by GetRowPtr or GetRowMapPtr.
396          * @param fm The SQLfieldMap to free
397          */
398         virtual void Free(SQLfieldMap* fm) = 0;
399
400         /**
401          * Overloaded function for freeing the lists and maps
402          * returned by GetRowPtr or GetRowMapPtr.
403          * @param fl The SQLfieldList to free
404          */
405         virtual void Free(SQLfieldList* fl) = 0;
406 };
407
408 #endif