diff options
-rw-r--r-- | src/modules/extra/m_pgsql.cpp | 159 | ||||
-rw-r--r-- | src/modules/extra/m_sqlv2.h | 77 | ||||
-rw-r--r-- | src/modules/extra/m_testclient.cpp | 105 |
3 files changed, 331 insertions, 10 deletions
diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp index 1e2a73f69..e36821cb9 100644 --- a/src/modules/extra/m_pgsql.cpp +++ b/src/modules/extra/m_pgsql.cpp @@ -175,34 +175,175 @@ public: class PgSQLresult : public SQLresult { PGresult* res; + int currentrow; + + SQLfieldList* fieldlist; + SQLfieldMap* fieldmap; public: PgSQLresult(Module* self, Module* to, PGresult* result) - : SQLresult(self, to), res(result) + : SQLresult(self, to), res(result), currentrow(0), fieldlist(NULL), fieldmap(NULL) { int rows = PQntuples(res); int cols = PQnfields(res); log(DEBUG, "Created new PgSQL result; %d rows, %d columns", rows, cols); + } + + ~PgSQLresult() + { + PQclear(res); + } + + virtual int Rows() + { + return PQntuples(res); + } + + virtual int Cols() + { + return PQnfields(res); + } + + virtual std::string ColName(int column) + { + char* name = PQfname(res, column); + + return (name) ? name : ""; + } + + virtual int ColNum(const std::string &column) + { + int n = PQfnumber(res, column.c_str()); - for (int r = 0; r < rows; r++) + if(n == -1) { - log(DEBUG, "Row %d:", r); - + throw SQLbadColName(); + } + else + { + return n; + } + } + + virtual SQLfield GetValue(int row, int column) + { + char* v = PQgetvalue(res, row, column); + + if(v) + { + return SQLfield(std::string(v, PQgetlength(res, row, column)), PQgetisnull(res, row, column)); + } + else + { + log(DEBUG, "PQgetvalue returned a null pointer..nobody wants to tell us what this means"); + throw SQLbadColName(); + } + } + + virtual SQLfieldList& GetRow() + { + /* In an effort to reduce overhead we don't actually allocate the list + * until the first time it's needed...so... + */ + if(fieldlist) + { + fieldlist->clear(); + } + else + { + fieldlist = new SQLfieldList; + } + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + for(int i = 0; i < cols; i++) { - log(DEBUG, "\t[%s]: %s", PQfname(result, i), PQgetvalue(result, r, i)); + fieldlist->push_back(GetValue(currentrow, i)); } + + currentrow++; } + + return *fieldlist; } - ~PgSQLresult() + virtual SQLfieldMap& GetRowMap() { - PQclear(res); + /* In an effort to reduce overhead we don't actually allocate the map + * until the first time it's needed...so... + */ + if(fieldmap) + { + fieldmap->clear(); + } + else + { + fieldmap = new SQLfieldMap; + } + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fieldmap->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); + } + + currentrow++; + } + + return *fieldmap; } - virtual int Rows() + virtual SQLfieldList* GetRowPtr() { - return PQntuples(res); + SQLfieldList* fl = new SQLfieldList; + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fl->push_back(GetValue(currentrow, i)); + } + + currentrow++; + } + + return fl; + } + + virtual SQLfieldMap* GetRowMapPtr() + { + SQLfieldMap* fm = new SQLfieldMap; + + if(currentrow < PQntuples(res)) + { + int cols = PQnfields(res); + + for(int i = 0; i < cols; i++) + { + fm->insert(std::make_pair(ColName(i), GetValue(currentrow, i))); + } + + currentrow++; + } + + return fm; + } + + virtual void Free(SQLfieldMap* fm) + { + DELETE(fm); + } + + virtual void Free(SQLfieldList* fl) + { + DELETE(fl); } }; diff --git a/src/modules/extra/m_sqlv2.h b/src/modules/extra/m_sqlv2.h index 1354e5dfc..d93e59b47 100644 --- a/src/modules/extra/m_sqlv2.h +++ b/src/modules/extra/m_sqlv2.h @@ -6,10 +6,22 @@ #define SQLSUCCESS "You shouldn't be reading this (success)" #include <string> +#include <vector> +#include <map> #include "modules.h" enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL }; +class SQLexception +{ +}; + +class SQLbadColName : public SQLexception +{ +public: + SQLbadColName() { } +}; + class SQLerror : public classbase { SQLerrorNum id; @@ -72,6 +84,25 @@ public: } }; +class SQLfield +{ +public: + /* The data itself */ + std::string d; + + /* If the field was null */ + bool null; + + SQLfield(const std::string &data, bool n) + : d(data), null(n) + { + + } +}; + +typedef std::vector<SQLfield> SQLfieldList; +typedef std::map<std::string, SQLfield> SQLfieldMap; + class SQLresult : public Request { public: @@ -82,10 +113,54 @@ public: SQLresult(Module* s, Module* d) : Request(SQLRESID, s, d) { - } + /* Return the number of rows in the result */ virtual int Rows() = 0; + + /* Return the number of columns in the result */ + virtual int Cols() = 0; + + /* Get a string name of the column by an index number */ + virtual std::string ColName(int column) = 0; + + /* Get an index number for a column from a string name. + * An exception of type SQLbadColName will be thrown if + * the name given is invalid. + */ + virtual int ColNum(const std::string &column) = 0; + + /* Get a string value in a given row and column */ + virtual SQLfield GetValue(int row, int column) = 0; + + /* Return a list of values in a row, this should + * increment an internal counter so you can repeatedly + * call it until it returns an empty vector. + * This returns a reference to an internal object, + * the same object is used for all calls to this function + * and therefore the return value is only valid until + * you call this function again. It is also invalid if + * the SQLresult object is destroyed. + */ + virtual SQLfieldList& GetRow() = 0; + + /* As above, but return a map indexed by key name */ + virtual SQLfieldMap& GetRowMap() = 0; + + /* Like GetRow(), but returns a pointer to a dynamically + * allocated object which must be explicitly freed. For + * portability reasons this must be freed with SQLresult::Free() + */ + virtual SQLfieldList* GetRowPtr() = 0; + + /* As above, but return a map indexed by key name */ + virtual SQLfieldMap* GetRowMapPtr() = 0; + + /* Overloaded function for freeing the lists and maps returned + * above. + */ + virtual void Free(SQLfieldMap* fm) = 0; + virtual void Free(SQLfieldList* fl) = 0; }; #endif diff --git a/src/modules/extra/m_testclient.cpp b/src/modules/extra/m_testclient.cpp new file mode 100644 index 000000000..b1d8a3384 --- /dev/null +++ b/src/modules/extra/m_testclient.cpp @@ -0,0 +1,105 @@ +#include <string> + +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "helperfuncs.h" +#include "inspircd.h" +#include "configreader.h" +#include "m_sqlv2.h" + +class ModuleTestClient : public Module +{ +private: + Server* Srv; + +public: + ModuleTestClient(Server* Me) + : Module::Module(Me), Srv(Me) + { + } + + void Implements(char* List) + { + List[I_OnRequest] = List[I_OnBackgroundTimer] = 1; + } + + virtual Version GetVersion() + { + return Version(1, 0, 0, 0, VF_VENDOR); + } + + virtual void OnBackgroundTimer(time_t foo) + { + Module* target = Srv->FindFeature("SQL"); + + if(target) + { + SQLrequest foo = SQLrequest(this, target, "SELECT foo, bar FROM rawr", "foo"); + + if(foo.Send()) + { + log(DEBUG, "Sent query, got given ID %lu", foo.id); + } + else + { + log(DEBUG, "SQLrequest failed: %s", foo.error.Str()); + } + } + } + + virtual char* OnRequest(Request* request) + { + if(strcmp(SQLRESID, request->GetData()) == 0) + { + log(DEBUG, "Got SQL result (%s)", request->GetData()); + + SQLresult* res = (SQLresult*)request; + + log(DEBUG, "Got result with %d rows and %d columns", res->Rows(), res->Cols()); + + for (int r = 0; r < res->Rows(); r++) + { + log(DEBUG, "Row %d:", r); + + for(int i = 0; i < res->Cols(); i++) + { + log(DEBUG, "\t[%s]: %s", res->ColName(i).c_str(), res->GetValue(r, i).d.c_str()); + } + } + + return SQLSUCCESS; + } + + log(DEBUG, "Got unsupported API version string: %s", request->GetData()); + + return NULL; + } + + virtual ~ModuleTestClient() + { + } +}; + +class ModuleTestClientFactory : public ModuleFactory +{ + public: + ModuleTestClientFactory() + { + } + + ~ModuleTestClientFactory() + { + } + + virtual Module * CreateModule(Server* Me) + { + return new ModuleTestClient(Me); + } +}; + + +extern "C" void * init_module( void ) +{ + return new ModuleTestClientFactory; +} |