1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2009 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
14 #ifndef INSPIRCD_SQLAPI_2
15 #define INSPIRCD_SQLAPI_2
22 /** Identifiers used to identify Request types
24 #define SQLREQID "SQLv2 Request"
25 #define SQLRESID "SQLv2 Result"
26 #define SQLSUCCESS "You shouldn't be reading this (success)"
28 /** Defines the error types which SQLerror may be set to
30 enum SQLerrorNum { SQL_NO_ERROR, SQL_BAD_DBID, SQL_BAD_CONN, SQL_QSEND_FAIL, SQL_QREPLY_FAIL };
32 /** A list of format parameters for an SQLquery object.
34 typedef std::deque<std::string> ParamL;
36 /** The base class of SQL exceptions
38 class SQLexception : public ModuleException
41 SQLexception(const std::string &reason) : ModuleException(reason)
45 SQLexception() : ModuleException("SQLv2: Undefined exception")
50 /** An exception thrown when a bad column or row name or id is requested
52 class SQLbadColName : public SQLexception
55 SQLbadColName() : SQLexception("SQLv2: Bad column name")
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.
64 class SQLerror : public classbase
73 /** Initialize an SQLerror
74 * @param i The error ID to set
75 * @param s The (optional) error string to set
77 SQLerror(SQLerrorNum i = SQL_NO_ERROR, const std::string &s = "")
82 /** Return the ID of the error
89 /** Set the ID of an error
90 * @param i The new error ID to set
91 * @return the ID which was set
93 SQLerrorNum Id(SQLerrorNum i)
99 /** Set the error string for an error
100 * @param s The new error string to set
102 void Str(const std::string &s)
107 /** Return the error string for an error
119 return "Invalid database ID";
121 return "Invalid connection";
123 return "Sending query failed";
124 case SQL_QREPLY_FAIL:
125 return "Getting query result failed";
127 return "Unknown error";
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:
137 * SQLrequest foo = SQLrequest(this, target, "databaseid", SQLquery("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42"));
139 * what's actually happening is functionally this:
141 * SQLrequest foo = SQLrequest(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
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:
150 * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
152 class SQLquery : public classbase
155 /** The query 'format string'
158 /** The query parameter list
159 * There should be one parameter for every ? character
160 * within the format string shown above.
164 /** Initialize an SQLquery with a given format string only
166 SQLquery(const std::string &query)
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().
175 SQLquery(const std::string &query, const ParamL ¶ms)
176 : q(query), p(params)
180 /** An overloaded operator for pushing parameters onto the parameter list
182 template<typename T> SQLquery& operator,(const T &foo)
184 p.push_back(ConvToStr(foo));
188 /** An overloaded operator for pushing parameters onto the parameter list.
189 * This has higher precedence than 'operator,' and can save on parenthesis.
191 template<typename T> SQLquery& operator%(const T &foo)
193 p.push_back(ConvToStr(foo));
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().
203 class SQLrequest : public Request
206 /** The fully parsed and expanded query string
207 * This is initialized from the SQLquery parameter given in the constructor.
210 /** The database ID to apply the request to
213 /** True if this is a priority query.
214 * Priority queries may 'queue jump' in the request queue.
217 /** True if this query has been cancelled; send no response */
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.
225 /** If an error occured, error.id will be any other value than SQL_NO_ERROR.
229 /** Initialize an SQLrequest.
232 * SQLrequest req = SQLrequest(MyMod, SQLModule, dbid, SQLquery("INSERT INTO ircd_log_actors VALUES('','?')" % nick));
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.
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)
245 /** Set the priority of a request.
247 void Priority(bool p = true)
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.
266 * If the field was null
270 /** Initialize an SQLfield
272 SQLfield(const std::string &data = "", bool n = false)
279 /** A list of items which make up a row of a result or table (tuple)
280 * This does not include field names.
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.
286 typedef std::map<std::string, SQLfield> SQLfieldMap;
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.
296 class SQLresult : public Request
299 /** The original query string passed initially to the SQL API
302 /** The database ID the query was executed on
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.
312 * This will match query ID you were given when sending
313 * the request at an earlier time.
317 /** Used by the SQL API to instantiate an SQLrequest
319 SQLresult(Module* s, Module* d, unsigned long i)
320 : Request(s, d, SQLRESID), id(i)
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.
334 virtual int Rows() = 0;
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
341 * @returns Number of columns in the result set.
343 virtual int Cols() = 0;
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
350 virtual std::string ColName(int column) = 0;
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
359 virtual int ColNum(const std::string &column) = 0;
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
366 virtual SQLfield GetValue(int row, int column) = 0;
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
380 virtual SQLfieldList& GetRow() = 0;
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
387 virtual SQLfieldMap& GetRowMap() = 0;
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
396 virtual SQLfieldList* GetRowPtr() = 0;
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
403 virtual SQLfieldMap* GetRowMapPtr() = 0;
406 * Overloaded function for freeing the lists and maps
407 * returned by GetRowPtr or GetRowMapPtr.
408 * @param fm The SQLfieldMap to free
410 virtual void Free(SQLfieldMap* fm) = 0;
413 * Overloaded function for freeing the lists and maps
414 * returned by GetRowPtr or GetRowMapPtr.
415 * @param fl The SQLfieldList to free
417 virtual void Free(SQLfieldList* fl) = 0;
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.
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 */
438 : id(""), host(""), ip(""), port(0), name(""), user(""), pass(""), ssl(0)
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)
447 /** Overload this to return a correct Data source Name (DSN) for
448 * the current SQL module.
450 std::string GetDSN();
453 /** Overload operator== for two SQLhost objects for easy comparison.
455 bool operator== (const SQLhost& l, const SQLhost& r)
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);
459 /** Overload operator!= for two SQLhost objects for easy comparison.
461 bool operator!= (const SQLhost& l, const SQLhost& r)
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);
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.
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).
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.
492 class QueryQueue : public classbase
495 typedef std::deque<SQLrequest*> ReqDeque;
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 */
507 void push(SQLrequest *q)
510 priority.push_back(q);
517 if((which == PRI) && priority.size())
519 priority.pop_front();
521 else if((which == NOR) && normal.size())
529 /* Silently do nothing if there was no element to pop() */
537 return priority.front();
539 return normal.front();
544 return priority.front();
550 return normal.front();
556 std::pair<int, int> size()
558 return std::make_pair(priority.size(), normal.size());
563 return priority.size() + normal.size();
566 void PurgeModule(Module* mod)
568 DoPurgeModule(mod, priority);
569 DoPurgeModule(mod, normal);
573 void DoPurgeModule(Module* mod, ReqDeque& q)
575 ReqDeque::iterator iter = q.begin();
576 while (iter != q.end())
578 if((**iter).source == mod)
580 if (*iter == front())
582 /* It's the currently active query.. :x */
583 (**iter).cancel = true;
588 /* It hasn't been executed yet..just remove it */
589 iter = q.erase(iter);