1 /* +------------------------------------+
\r * | Inspire Internet Relay Chat Daemon |
\r * +------------------------------------+
\r *
\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team
\r * See: http://www.inspircd.org/wiki/index.php/Credits
\r *
\r * This program is free but copyrighted software; see
\r * the file COPYING for details.
\r *
\r * ---------------------------------------------------
\r */
\r\r#ifndef INSPIRCD_SQLAPI_2
\r#define INSPIRCD_SQLAPI_2
\r\r#include <string>
\r#include <deque>
\r#include <map>
\r#include "modules.h"
\r\r/** SQLreq define.
\r * This is the voodoo magic which lets us pass multiple
\r * parameters to the SQLrequest constructor... voodoo...
\r */
\r#define SQLreq(a, b, c, d, e...) SQLrequest(a, b, c, (SQLquery(d), ##e))
\r\r/** Identifiers used to identify Request types
\r */
\r#define SQLREQID "SQLv2 Request"
\r#define SQLRESID "SQLv2 Result"
\r#define SQLSUCCESS "You shouldn't be reading this (success)"
\r\r/** Defines the error types which SQLerror may be set to
\r */
\renum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL, QREPLY_FAIL };
\r\r/** A list of format parameters for an SQLquery object.
\r */
\rtypedef std::deque<std::string> ParamL;
\r\r/** The base class of SQL exceptions
\r */
\rclass SQLexception : public ModuleException
\r{
\r public:
\r SQLexception(const std::string &reason) : ModuleException(reason)
\r {
\r }
\r\r SQLexception() : ModuleException("SQLv2: Undefined exception")
\r {
\r }
\r};
\r\r/** An exception thrown when a bad column or row name or id is requested
\r */
\rclass SQLbadColName : public SQLexception
\r{
\rpublic:
\r SQLbadColName() : SQLexception("SQLv2: Bad column name")
\r {
\r }
\r};
\r\r/** SQLerror holds the error state of any SQLrequest or SQLresult.
\r * The error string varies from database software to database software
\r * and should be used to display informational error messages to users.
\r */
\rclass SQLerror : public classbase
\r{
\r /** The error id
\r */
\r SQLerrorNum id;
\r /** The error string
\r */
\r std::string str;
\rpublic:
\r /** Initialize an SQLerror
\r * @param i The error ID to set
\r * @param s The (optional) error string to set
\r */
\r SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
\r : id(i), str(s)
\r {
\r }
\r\r /** Return the ID of the error
\r */
\r SQLerrorNum Id()
\r {
\r return id;
\r }
\r\r /** Set the ID of an error
\r * @param i The new error ID to set
\r * @return the ID which was set
\r */
\r SQLerrorNum Id(SQLerrorNum i)
\r {
\r id = i;
\r return id;
\r }
\r\r /** Set the error string for an error
\r * @param s The new error string to set
\r */
\r void Str(const std::string &s)
\r {
\r str = s;
\r }
\r\r /** Return the error string for an error
\r */
\r const char* Str()
\r {
\r if(str.length())
\r return str.c_str();
\r\r switch(id)
\r {
\r case NO_ERROR:
\r return "No error";
\r case BAD_DBID:
\r return "Invalid database ID";
\r case BAD_CONN:
\r return "Invalid connection";
\r case QSEND_FAIL:
\r return "Sending query failed";
\r case QREPLY_FAIL:
\r return "Getting query result failed";
\r default:
\r return "Unknown error";
\r }
\r }
\r};
\r\r/** SQLquery provides a way to represent a query string, and its parameters in a type-safe way.
\r * C++ has no native type-safe way of having a variable number of arguments to a function,
\r * the workaround for this isn't easy to describe simply, but in a nutshell what's really
\r * happening when - from the above example - you do this:
\r *
\r * SQLrequest foo = SQLreq(this, target, "databaseid", "SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?", "Hello", "42");
\r *
\r * what's actually happening is functionally this:
\r *
\r * SQLrequest foo = SQLreq(this, target, "databaseid", query("SELECT (foo, bar) FROM rawr WHERE foo = '?' AND bar = ?").addparam("Hello").addparam("42"));
\r *
\r * with 'query()' returning a reference to an object with a 'addparam()' member function which
\r * in turn returns a reference to that object. There are actually four ways you can create a
\r * SQLrequest..all have their disadvantages and advantages. In the real implementations the
\r * 'query()' function is replaced by the constructor of another class 'SQLquery' which holds
\r * the query string and a ParamL (std::deque<std::string>) of query parameters.
\r * This is essentially the same as the above example except 'addparam()' is replaced by operator,(). The full syntax for this method is:
\r *
\r * SQLrequest foo = SQLrequest(this, target, "databaseid", (SQLquery("SELECT.. ?"), parameter, parameter));
\r */
\rclass SQLquery : public classbase
\r{
\rpublic:
\r /** The query 'format string'
\r */
\r std::string q;
\r /** The query parameter list
\r * There should be one parameter for every ? character
\r * within the format string shown above.
\r */
\r ParamL p;
\r\r /** Initialize an SQLquery with a given format string only
\r */
\r SQLquery(const std::string &query)
\r : q(query)
\r {
\r }
\r\r /** Initialize an SQLquery with a format string and parameters.
\r * If you provide parameters, you must initialize the list yourself
\r * if you choose to do it via this method, using std::deque::push_back().
\r */
\r SQLquery(const std::string &query, const ParamL ¶ms)
\r : q(query), p(params)
\r {
\r }
\r\r /** An overloaded operator for pushing parameters onto the parameter list
\r */
\r template<typename T> SQLquery& operator,(const T &foo)
\r {
\r p.push_back(ConvToStr(foo));
\r return *this;
\r }
\r\r /** An overloaded operator for pushing parameters onto the parameter list.
\r * This has higher precedence than 'operator,' and can save on parenthesis.
\r */
\r template<typename T> SQLquery& operator%(const T &foo)
\r {
\r p.push_back(ConvToStr(foo));
\r return *this;
\r }
\r};
\r\r/** SQLrequest is sent to the SQL API to command it to run a query and return the result.
\r * You must instantiate this object with a valid SQLquery object and its parameters, then
\r * send it using its Send() method to the module providing the 'SQL' feature. To find this
\r * module, use Server::FindFeature().
\r */
\rclass SQLrequest : public Request
\r{
\rpublic:
\r /** The fully parsed and expanded query string
\r * This is initialized from the SQLquery parameter given in the constructor.
\r */
\r SQLquery query;
\r /** The database ID to apply the request to
\r */
\r std::string dbid;
\r /** True if this is a priority query.
\r * Priority queries may 'queue jump' in the request queue.
\r */
\r bool pri;
\r /** The query ID, assigned by the SQL api.
\r * After your request is processed, this will
\r * be initialized for you by the API to a valid request ID,
\r * except in the case of an error.
\r */
\r unsigned long id;
\r /** If an error occured, error.id will be any other value than NO_ERROR.
\r */
\r SQLerror error;
\r\r /** Initialize an SQLrequest.
\r * For example:
\r *
\r * SQLrequest req = SQLreq(MyMod, SQLModule, dbid, "INSERT INTO ircd_log_actors VALUES('','?')", nick);
\r *
\r * @param s A pointer to the sending module, where the result should be routed
\r * @param d A pointer to the receiving module, identified as implementing the 'SQL' feature
\r * @param databaseid The database ID to perform the query on. This must match a valid
\r * database ID from the configuration of the SQL module.
\r * @param q A properly initialized SQLquery object.
\r */
\r SQLrequest(Module* s, Module* d, const std::string &databaseid, const SQLquery &q)
\r : Request(s, d, SQLREQID), query(q), dbid(databaseid), pri(false), id(0)
\r {
\r }
\r\r /** Set the priority of a request.
\r */
\r void Priority(bool p = true)
\r {
\r pri = p;
\r }
\r\r /** Set the source of a request. You should not need to use this method.
\r */
\r void SetSource(Module* mod)
\r {
\r source = mod;
\r }
\r};
\r\r/**
\r * This class contains a field's data plus a way to determine if the field
\r * is NULL or not without having to mess around with NULL pointers.
\r */
\rclass SQLfield
\r{
\rpublic:
\r /**
\r * The data itself
\r */
\r std::string d;
\r\r /**
\r * If the field was null
\r */
\r bool null;
\r\r /** Initialize an SQLfield
\r */
\r SQLfield(const std::string &data = "", bool n = false)
\r : d(data), null(n)
\r {
\r\r }
\r};
\r\r/** A list of items which make up a row of a result or table (tuple)
\r * This does not include field names.
\r */
\rtypedef std::vector<SQLfield> SQLfieldList;
\r/** A list of items which make up a row of a result or table (tuple)
\r * This also includes the field names.
\r */
\rtypedef std::map<std::string, SQLfield> SQLfieldMap;
\r\r/** SQLresult is a reply to a previous query.
\r * If you send a query to the SQL api, the response will arrive at your
\r * OnRequest method of your module at some later time, depending on the
\r * congestion of the SQL server and complexity of the query. The ID of
\r * this result will match the ID assigned to your original request.
\r * SQLresult contains its own internal cursor (row counter) which is
\r * incremented with each method call which retrieves a single row.
\r */
\rclass SQLresult : public Request
\r{
\rpublic:
\r /** The original query string passed initially to the SQL API
\r */
\r std::string query;
\r /** The database ID the query was executed on
\r */
\r std::string dbid;
\r /**
\r * The error (if any) which occured.
\r * If an error occured the value of error.id will be any
\r * other value than NO_ERROR.
\r */
\r SQLerror error;
\r /**
\r * This will match query ID you were given when sending
\r * the request at an earlier time.
\r */
\r unsigned long id;
\r\r /** Used by the SQL API to instantiate an SQLrequest
\r */
\r SQLresult(Module* s, Module* d, unsigned long i)
\r : Request(s, d, SQLRESID), id(i)
\r {
\r }
\r\r /**
\r * Return the number of rows in the result
\r * Note that if you have perfomed an INSERT
\r * or UPDATE query or other query which will
\r * not return rows, this will return the
\r * number of affected rows, and SQLresult::Cols()
\r * will contain 0. In this case you SHOULD NEVER
\r * access any of the result set rows, as there arent any!
\r * @returns Number of rows in the result set.
\r */
\r virtual int Rows() = 0;
\r\r /**
\r * Return the number of columns in the result.
\r * If you performed an UPDATE or INSERT which
\r * does not return a dataset, this value will
\r * be 0.
\r * @returns Number of columns in the result set.
\r */
\r virtual int Cols() = 0;
\r\r /**
\r * Get a string name of the column by an index number
\r * @param column The id number of a column
\r * @returns The column name associated with the given ID
\r */
\r virtual std::string ColName(int column) = 0;
\r\r /**
\r * Get an index number for a column from a string name.
\r * An exception of type SQLbadColName will be thrown if
\r * the name given is invalid.
\r * @param column The column name to get the ID of
\r * @returns The ID number of the column provided
\r */
\r virtual int ColNum(const std::string &column) = 0;
\r\r /**
\r * Get a string value in a given row and column
\r * This does not effect the internal cursor.
\r * @returns The value stored at [row,column] in the table
\r */
\r virtual SQLfield GetValue(int row, int column) = 0;
\r\r /**
\r * Return a list of values in a row, this should
\r * increment an internal counter so you can repeatedly
\r * call it until it returns an empty vector.
\r * This returns a reference to an internal object,
\r * the same object is used for all calls to this function
\r * and therefore the return value is only valid until
\r * you call this function again. It is also invalid if
\r * the SQLresult object is destroyed.
\r * The internal cursor (row counter) is incremented by one.
\r * @returns A reference to the current row's SQLfieldList
\r */
\r virtual SQLfieldList& GetRow() = 0;
\r\r /**
\r * As above, but return a map indexed by key name.
\r * The internal cursor (row counter) is incremented by one.
\r * @returns A reference to the current row's SQLfieldMap
\r */
\r virtual SQLfieldMap& GetRowMap() = 0;
\r\r /**
\r * Like GetRow(), but returns a pointer to a dynamically
\r * allocated object which must be explicitly freed. For
\r * portability reasons this must be freed with SQLresult::Free()
\r * The internal cursor (row counter) is incremented by one.
\r * @returns A newly-allocated SQLfieldList
\r */
\r virtual SQLfieldList* GetRowPtr() = 0;
\r\r /**
\r * As above, but return a map indexed by key name
\r * The internal cursor (row counter) is incremented by one.
\r * @returns A newly-allocated SQLfieldMap
\r */
\r virtual SQLfieldMap* GetRowMapPtr() = 0;
\r\r /**
\r * Overloaded function for freeing the lists and maps
\r * returned by GetRowPtr or GetRowMapPtr.
\r * @param fm The SQLfieldMap to free
\r */
\r virtual void Free(SQLfieldMap* fm) = 0;
\r\r /**
\r * Overloaded function for freeing the lists and maps
\r * returned by GetRowPtr or GetRowMapPtr.
\r * @param fl The SQLfieldList to free
\r */
\r virtual void Free(SQLfieldList* fl) = 0;
\r};
\r\r\r/** SQLHost represents a <database> config line and is useful
\r * for storing in a map and iterating on rehash to see which
\r * <database> tags was added/removed/unchanged.
\r */
\rclass SQLhost
\r{
\r public:
\r std::string id; /* Database handle id */
\r std::string host; /* Database server hostname */
\r std::string ip; /* resolved IP, needed for at least pgsql.so */
\r unsigned int port; /* Database server port */
\r std::string name; /* Database name */
\r std::string user; /* Database username */
\r std::string pass; /* Database password */
\r bool ssl; /* If we should require SSL */
\r\r SQLhost()
\r {
\r }
\r\r 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)
\r : id(i), host(h), port(p), name(n), user(u), pass(pa), ssl(s)
\r {
\r }
\r\r /** Overload this to return a correct Data source Name (DSN) for
\r * the current SQL module.
\r */
\r std::string GetDSN();
\r};
\r\r/** Overload operator== for two SQLhost objects for easy comparison.
\r */
\rbool operator== (const SQLhost& l, const SQLhost& r)
\r{
\r 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);
\r}
\r\r\r/** QueryQueue, a queue of queries waiting to be executed.
\r * This maintains two queues internally, one for 'priority'
\r * queries and one for less important ones. Each queue has
\r * new queries appended to it and ones to execute are popped
\r * off the front. This keeps them flowing round nicely and no
\r * query should ever get 'stuck' for too long. If there are
\r * queries in the priority queue they will be executed first,
\r * 'unimportant' queries will only be executed when the
\r * priority queue is empty.
\r *
\r * We store lists of SQLrequest's here, by value as we want to avoid storing
\r * any data allocated inside the client module (in case that module is unloaded
\r * while the query is in progress).
\r *
\r * Because we want to work on the current SQLrequest in-situ, we need a way
\r * of accessing the request we are currently processing, QueryQueue::front(),
\r * but that call needs to always return the same request until that request
\r * is removed from the queue, this is what the 'which' variable is. New queries are
\r * always added to the back of one of the two queues, but if when front()
\r * is first called then the priority queue is empty then front() will return
\r * a query from the normal queue, but if a query is then added to the priority
\r * queue then front() must continue to return the front of the *normal* queue
\r * until pop() is called.
\r */
\r\rclass QueryQueue : public classbase
\r{
\rprivate:
\r typedef std::deque<SQLrequest> ReqDeque;
\r\r ReqDeque priority; /* The priority queue */
\r ReqDeque normal; /* The 'normal' queue */
\r enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */
\r\rpublic:
\r QueryQueue()
\r : which(NON)
\r {
\r }
\r\r void push(const SQLrequest &q)
\r {
\r if(q.pri)
\r priority.push_back(q);
\r else
\r normal.push_back(q);
\r }
\r\r void pop()
\r {
\r if((which == PRI) && priority.size())
\r {
\r priority.pop_front();
\r }
\r else if((which == NOR) && normal.size())
\r {
\r normal.pop_front();
\r }
\r\r /* Reset this */
\r which = NON;
\r\r /* Silently do nothing if there was no element to pop() */
\r }
\r\r SQLrequest& front()
\r {
\r switch(which)
\r {
\r case PRI:
\r return priority.front();
\r case NOR:
\r return normal.front();
\r default:
\r if(priority.size())
\r {
\r which = PRI;
\r return priority.front();
\r }
\r\r if(normal.size())
\r {
\r which = NOR;
\r return normal.front();
\r }
\r\r /* This will probably result in a segfault,
\r * but the caller should have checked totalsize()
\r * first so..meh - moron :p
\r */
\r\r return priority.front();
\r }
\r }
\r\r std::pair<int, int> size()
\r {
\r return std::make_pair(priority.size(), normal.size());
\r }
\r\r int totalsize()
\r {
\r return priority.size() + normal.size();
\r }
\r\r void PurgeModule(Module* mod)
\r {
\r DoPurgeModule(mod, priority);
\r DoPurgeModule(mod, normal);
\r }
\r\rprivate:
\r void DoPurgeModule(Module* mod, ReqDeque& q)
\r {
\r for(ReqDeque::iterator iter = q.begin(); iter != q.end(); iter++)
\r {
\r if(iter->GetSource() == mod)
\r {
\r if(iter->id == front().id)
\r {
\r /* It's the currently active query.. :x */
\r iter->SetSource(NULL);
\r }
\r else
\r {
\r /* It hasn't been executed yet..just remove it */
\r iter = q.erase(iter);
\r }
\r }
\r }
\r }
\r};
\r\r\r#endif
\r