summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/extra/m_pgsql.cpp159
-rw-r--r--src/modules/extra/m_sqlv2.h77
-rw-r--r--src/modules/extra/m_testclient.cpp105
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;
+}