summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2012-11-18 19:13:16 +0000
committerrandomdan <randomdan@localhost>2012-11-18 19:13:16 +0000
commit49286cb2d2987e4d2f6a07f6b9ed46d4de97d9ac (patch)
tree9064882e6e44203bf35bf70089500c326458b6f1
parentMigrate all stuff to stricter compilations/links and C++0x builds (diff)
downloadlibdbpp-mysql-49286cb2d2987e4d2f6a07f6b9ed46d4de97d9ac.tar.bz2
libdbpp-mysql-49286cb2d2987e4d2f6a07f6b9ed46d4de97d9ac.tar.xz
libdbpp-mysql-49286cb2d2987e4d2f6a07f6b9ed46d4de97d9ac.zip
Add a basic MySQL connector, not fully functional, but will suffice for p2tv
-rw-r--r--libmysqlpp/Jamfile.jam26
-rw-r--r--libmysqlpp/column.cpp112
-rw-r--r--libmysqlpp/column.h42
-rw-r--r--libmysqlpp/command.cpp159
-rw-r--r--libmysqlpp/command.h45
-rw-r--r--libmysqlpp/connection.cpp157
-rw-r--r--libmysqlpp/connection.h42
-rw-r--r--libmysqlpp/error.cpp29
-rw-r--r--libmysqlpp/error.h24
-rw-r--r--libmysqlpp/modifycommand.cpp30
-rw-r--r--libmysqlpp/modifycommand.h25
-rw-r--r--libmysqlpp/selectcommand.cpp129
-rw-r--r--libmysqlpp/selectcommand.h44
13 files changed, 864 insertions, 0 deletions
diff --git a/libmysqlpp/Jamfile.jam b/libmysqlpp/Jamfile.jam
new file mode 100644
index 0000000..a03e470
--- /dev/null
+++ b/libmysqlpp/Jamfile.jam
@@ -0,0 +1,26 @@
+alias glibmm : : : :
+ <cflags>"`pkg-config --cflags glibmm-2.4`"
+ <linkflags>"`pkg-config --libs glibmm-2.4`"
+ ;
+
+lib mysql ;
+
+alias libmysql : : : :
+ <cflags>"`mysql_config --include`"
+ <linkflags>"`mysql_config --libs`"
+ ;
+
+
+lib mysqlpp :
+ [ glob *.cpp ] :
+ <cflags>-fPIC
+ <library>glibmm
+ <library>libmysql
+ <library>../libdbpp
+ <include>../libmisc
+ : :
+ <include>.
+ <cflags>"-I`mysql_config --include`"
+ <library>glibmm
+ <library>../libdbpp
+ ;
diff --git a/libmysqlpp/column.cpp b/libmysqlpp/column.cpp
new file mode 100644
index 0000000..cd1d811
--- /dev/null
+++ b/libmysqlpp/column.cpp
@@ -0,0 +1,112 @@
+#include "column.h"
+#include "selectcommand.h"
+#include "error.h"
+#include <string.h>
+
+MySQL::ColumnBase::ColumnBase(const char * name, unsigned int i) :
+ DB::Column(name, i)
+{
+}
+
+bool
+MySQL::ColumnBase::isNull() const
+{
+ return is_null;
+}
+
+void
+MySQL::ColumnBase::rebind(DB::Command *, unsigned int) const
+{
+ throw Error("Not supported");
+}
+
+MySQL::StringColumn::StringColumn(const char * name, unsigned int field, MYSQL_BIND * b, unsigned int len) :
+ ColumnBase(name, field),
+ value(new char[len])
+{
+ b->is_null = &is_null;
+ b->buffer_type = MYSQL_TYPE_STRING;
+ b->is_unsigned = 0;
+ b->buffer = value;
+ b->buffer_length = len;
+ b->length = &length;
+}
+MySQL::StringColumn::~StringColumn()
+{
+ delete[] value;
+}
+void
+MySQL::StringColumn::apply(DB::HandleField & h) const
+{
+ if (is_null) {
+ h.null();
+ }
+ else {
+ h.string(value, length);
+ }
+}
+MySQL::NullColumn::NullColumn(const char * name, unsigned int field, MYSQL_BIND * b) :
+ ColumnBase(name, field)
+{
+ b->is_null = &is_null;
+ b->buffer_type = MYSQL_TYPE_NULL;
+ b->buffer = NULL;
+ b->buffer_length = 0;
+}
+void
+MySQL::NullColumn::apply(DB::HandleField & h) const
+{
+ h.null();
+}
+
+namespace MySQL {
+ template <class T, enum_field_types MT> Column<T, MT>::Column(const char * name, unsigned int field, MYSQL_BIND * b) :
+ ColumnBase(name, field)
+ {
+ b->is_null = &is_null;
+ b->buffer_type = MT;
+ b->is_unsigned = 0;
+ b->buffer = &value;
+ b->buffer_length = sizeof(T);
+ }
+
+ template <> void Column<int64_t, MYSQL_TYPE_LONGLONG>::apply(DB::HandleField & h) const
+ {
+ if (is_null) {
+ h.null();
+ }
+ else {
+ h.integer(value);
+ }
+ }
+ template <> void Column<double, MYSQL_TYPE_DOUBLE>::apply(DB::HandleField & h) const
+ {
+ if (is_null) {
+ h.null();
+ }
+ else {
+ h.floatingpoint(value);
+ }
+ }
+ template <> void Column<MYSQL_TIME, MYSQL_TYPE_DATETIME>::apply(DB::HandleField & h) const
+ {
+ if (is_null) {
+ h.null();
+ }
+ else {
+ struct tm tm;
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = value.year - 1900;
+ tm.tm_mon = value.month - 1;
+ tm.tm_mday = value.day;
+ tm.tm_hour = value.hour;
+ tm.tm_min = value.minute;
+ tm.tm_sec = value.second;
+ h.timestamp(tm);
+ }
+ }
+
+ template class Column<int64_t, MYSQL_TYPE_LONGLONG>;
+ template class Column<double, MYSQL_TYPE_DOUBLE>;
+ template class Column<MYSQL_TIME, MYSQL_TYPE_DATETIME>;
+}
diff --git a/libmysqlpp/column.h b/libmysqlpp/column.h
new file mode 100644
index 0000000..6146f53
--- /dev/null
+++ b/libmysqlpp/column.h
@@ -0,0 +1,42 @@
+#ifndef MY_COLUMN_H
+#define MY_COLUMN_H
+
+#include "../libdbpp/column.h"
+#include <mysql.h>
+
+namespace MySQL {
+ class SelectCommand;
+ class ColumnBase : public DB::Column {
+ public:
+ ColumnBase(const char * name, unsigned int field);
+
+ bool isNull() const;
+ void rebind(DB::Command *, unsigned int) const;
+ protected:
+ my_bool is_null;
+ long unsigned int length;
+ friend class SelectCommand;
+ };
+ class StringColumn : public ColumnBase {
+ public:
+ StringColumn(const char * name, unsigned int field, MYSQL_BIND * b, unsigned int len);
+ ~StringColumn();
+ void apply(DB::HandleField &) const;
+ char * value;
+ long unsigned int length;
+ };
+ class NullColumn : public ColumnBase {
+ public:
+ NullColumn(const char * name, unsigned int field, MYSQL_BIND * b);
+ void apply(DB::HandleField &) const;
+ };
+ template <class T, enum_field_types MT> class Column : public ColumnBase {
+ public:
+ Column(const char * name, unsigned int field, MYSQL_BIND * b);
+ void apply(DB::HandleField & h) const;
+ T value;
+ };
+}
+
+#endif
+
diff --git a/libmysqlpp/command.cpp b/libmysqlpp/command.cpp
new file mode 100644
index 0000000..f33306f
--- /dev/null
+++ b/libmysqlpp/command.cpp
@@ -0,0 +1,159 @@
+#include "command.h"
+#include "connection.h"
+#include <stdlib.h>
+#include <string.h>
+
+MySQL::Command::Command(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ c(conn),
+ stmt(mysql_stmt_init(&conn->conn)),
+ paramsNeedBinding(false)
+{
+ if (!stmt) {
+ fprintf(stderr, "here1\n");
+ throw Error(mysql_error(&conn->conn));
+ }
+ if (mysql_stmt_prepare(stmt, sql.c_str(), sql.length())) {
+ fprintf(stderr, "here2\n");
+ throw Error(mysql_stmt_error(stmt));
+ }
+ binds.resize(mysql_stmt_param_count(stmt));
+ if (binds.size()) {
+ paramsNeedBinding = true;
+ for (Binds::iterator i = binds.begin(); i != binds.end(); ++i) {
+ memset(&*i, 0, sizeof(MYSQL_BIND));
+ i->buffer_type = MYSQL_TYPE_NULL;
+ }
+ }
+}
+
+MySQL::Command::~Command()
+{
+ for (Binds::const_iterator i = binds.begin(); i != binds.end(); ++i) {
+ free(i->buffer);
+ }
+ mysql_stmt_close(stmt);
+}
+
+void *
+MySQL::Command::realloc(void * buffer, size_t size)
+{
+ void * newBuffer = ::realloc(buffer, size);
+ if (buffer != newBuffer) {
+ paramsNeedBinding = true;
+ }
+ return newBuffer;
+}
+
+void
+MySQL::Command::bindParamI(unsigned int n, int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(int));
+ *static_cast<int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 0;
+}
+void
+MySQL::Command::bindParamI(unsigned int n, long int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONGLONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(long long int));
+ *static_cast<long long int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 0;
+}
+void
+MySQL::Command::bindParamI(unsigned int n, long long int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONGLONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(long long int));
+ *static_cast<long long int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 0;
+}
+void
+MySQL::Command::bindParamI(unsigned int n, unsigned int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(int));
+ *static_cast<int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 1;
+}
+void
+MySQL::Command::bindParamI(unsigned int n, long unsigned int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONGLONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(long long int));
+ *static_cast<long long int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 1;
+}
+void
+MySQL::Command::bindParamI(unsigned int n, long long unsigned int v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_LONGLONG;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(long long int));
+ *static_cast<long long int*>(binds[n].buffer) = v;
+ binds[n].is_unsigned = 1;
+}
+void
+MySQL::Command::bindParamF(unsigned int n, double v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_DOUBLE;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(double));
+ *static_cast<double*>(binds[n].buffer) = v;
+}
+void
+MySQL::Command::bindParamF(unsigned int n, float v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_FLOAT;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(float));
+ *static_cast<float*>(binds[n].buffer) = v;
+}
+void
+MySQL::Command::bindParamS(unsigned int n, const Glib::ustring & s)
+{
+ binds[n].buffer_type = MYSQL_TYPE_STRING;
+ binds[n].buffer = realloc(binds[n].buffer, s.bytes());
+ s.copy(static_cast<char*>(binds[n].buffer), s.bytes());
+ binds[n].buffer_length = s.bytes();
+ binds[n].is_unsigned = 0;
+}
+void
+MySQL::Command::bindParamT(unsigned int n, const tm * v)
+{
+ binds[n].buffer_type = MYSQL_TYPE_DATETIME;
+ binds[n].buffer = realloc(binds[n].buffer, sizeof(MYSQL_TIME));
+ MYSQL_TIME & ts = *static_cast<MYSQL_TIME*>(binds[n].buffer);
+ ts.year = v->tm_year + 1900;
+ ts.month = v->tm_mon + 1;
+ ts.day = v->tm_mday;
+ ts.hour = v->tm_hour;
+ ts.minute = v->tm_min;
+ ts.second = v->tm_sec;
+ ts.neg = 0;
+}
+void
+MySQL::Command::bindParamT(unsigned int n, time_t v)
+{
+ struct tm t;
+ gmtime_r(&v, &t);
+ bindParamT(n, &t);
+}
+void
+MySQL::Command::bindNull(unsigned int n)
+{
+ binds[n].buffer_type = MYSQL_TYPE_NULL;
+ binds[n].buffer = NULL;
+ free(binds[n].buffer);
+}
+
+void
+MySQL::Command::bindParams()
+{
+ if (paramsNeedBinding) {
+ if (mysql_stmt_bind_param(stmt, &binds.front())) {
+ throw Error(mysql_stmt_error(stmt));
+ paramsNeedBinding = false;
+ }
+ }
+}
+
+
diff --git a/libmysqlpp/command.h b/libmysqlpp/command.h
new file mode 100644
index 0000000..19a1323
--- /dev/null
+++ b/libmysqlpp/command.h
@@ -0,0 +1,45 @@
+#ifndef MY_COMMAND_H
+#define MY_COMMAND_H
+
+#include "../libdbpp/command.h"
+#include <mysql.h>
+#include <vector>
+
+namespace MySQL {
+ class Connection;
+ class Command : public virtual DB::Command {
+ public:
+ Command(const Connection *, const std::string & sql);
+ virtual ~Command() = 0;
+
+ void bindParamI(unsigned int, int);
+ void bindParamI(unsigned int, long int);
+ void bindParamI(unsigned int, long long int);
+ void bindParamI(unsigned int, unsigned int);
+ void bindParamI(unsigned int, long unsigned int);
+ void bindParamI(unsigned int, long long unsigned int);
+
+ void bindParamF(unsigned int, double);
+ void bindParamF(unsigned int, float);
+
+ void bindParamS(unsigned int, const Glib::ustring&);
+
+ void bindParamT(unsigned int, const tm*);
+ void bindParamT(unsigned int, time_t);
+
+ void bindNull(unsigned int);
+ protected:
+ void bindParams();
+ void * realloc(void * buffer, size_t size);
+
+ const Connection * c;
+ MYSQL_STMT * stmt;
+ typedef std::vector<MYSQL_BIND> Binds;
+ Binds binds;
+ bool paramsNeedBinding;
+ };
+}
+
+#endif
+
+
diff --git a/libmysqlpp/connection.cpp b/libmysqlpp/connection.cpp
new file mode 100644
index 0000000..f2b747a
--- /dev/null
+++ b/libmysqlpp/connection.cpp
@@ -0,0 +1,157 @@
+#include "connection.h"
+#include "error.h"
+#include "selectcommand.h"
+#include "modifycommand.h"
+#include "reflection.h"
+
+class Opts {
+ public:
+ Opts() { port = 3306; }
+ std::string server;
+ std::string user;
+ std::string password;
+ std::string database;
+ unsigned int port;
+ std::string unix_socket;
+
+ static Reflector<Opts>::Vars vars;
+};
+
+Reflector<Opts>::Vars Opts::vars = {
+ Map(Opts, server),
+ Map(Opts, user),
+ Map(Opts, password),
+ Map(Opts, database),
+ Map(Opts, unix_socket),
+ Map(Opts, port),
+};
+
+
+MySQL::Connection::Connection(const std::string & str) :
+ txDepth(0),
+ rolledback(false)
+{
+ Opts o(Reflector<Opts>::NameValueNew(str));
+ mysql_init(&conn);
+ if (mysql_real_connect(&conn, o.server.c_str(), o.user.c_str(), o.password.c_str(), o.database.c_str(),
+ o.port, o.unix_socket.c_str(), CLIENT_LOCAL_FILES | CLIENT_MULTI_STATEMENTS) == NULL) {
+ throw ConnectionError();
+ }
+ if (mysql_set_character_set(&conn, "utf8")) {
+ throw ConnectionError();
+ }
+}
+
+MySQL::Connection::~Connection()
+{
+ mysql_close(&conn);
+}
+
+void
+MySQL::Connection::finish() const
+{
+ if (txDepth != 0) {
+ rollbackTx();
+ throw Error("Transaction still open");
+ }
+}
+
+int
+MySQL::Connection::beginTx() const
+{
+ if (txDepth == 0) {
+ checkResult(mysql_autocommit(&conn, 0), true);
+ rolledback = false;
+ }
+ return ++txDepth;
+}
+
+int
+MySQL::Connection::commitTx() const
+{
+ if (rolledback) {
+ return rollbackTx();
+ }
+ if (--txDepth == 0) {
+ checkResult(mysql_commit(&conn), true);
+ }
+ return txDepth;
+}
+
+int
+MySQL::Connection::rollbackTx() const
+{
+ if (--txDepth == 0) {
+ checkResult(mysql_rollback(&conn), true);
+ }
+ else {
+ rolledback = true;
+ }
+ return txDepth;
+}
+
+bool
+MySQL::Connection::inTx() const
+{
+ return txDepth;
+}
+
+DB::BulkDeleteStyle
+MySQL::Connection::bulkDeleteStyle() const
+{
+ return DB::BulkDeleteUsingUsingAlias;
+}
+
+DB::BulkUpdateStyle
+MySQL::Connection::bulkUpdateStyle() const
+{
+ return DB::BulkUpdateUsingJoin;
+}
+
+void
+MySQL::Connection::ping() const
+{
+ checkResult(mysql_ping(&conn), true);
+}
+
+
+DB::SelectCommand *
+MySQL::Connection::newSelectCommand(const std::string & sql) const
+{
+ return new SelectCommand(this, sql);
+}
+
+DB::ModifyCommand *
+MySQL::Connection::newModifyCommand(const std::string & sql) const
+{
+ return new ModifyCommand(this, sql);
+}
+
+void
+MySQL::Connection::checkResult(my_bool actual, my_bool expected) const
+{
+ if (actual != expected) {
+ throw Error(mysql_error(&conn));
+ }
+}
+
+void
+MySQL::Connection::beginBulkUpload(const char * table, const char * extra) const
+{
+ (void)table;
+ (void)extra;
+}
+
+void
+MySQL::Connection::endBulkUpload(const char * msg) const
+{
+ (void)msg;
+}
+
+size_t
+MySQL::Connection::bulkUploadData(const char * data, size_t len) const
+{
+ (void)data;
+ return len;
+}
+
diff --git a/libmysqlpp/connection.h b/libmysqlpp/connection.h
new file mode 100644
index 0000000..a88d758
--- /dev/null
+++ b/libmysqlpp/connection.h
@@ -0,0 +1,42 @@
+#ifndef MY_CONNECTION_H
+#define MY_CONNECTION_H
+
+#include "../libdbpp/connection.h"
+#include "error.h"
+#include <mysql.h>
+
+namespace MySQL {
+ class Connection : public DB::Connection {
+ public:
+ Connection(const std::string & info);
+ ~Connection();
+
+ void finish() const;
+ int beginTx() const;
+ int commitTx() const;
+ int rollbackTx() const;
+ bool inTx() const;
+ void ping() const;
+ DB::BulkDeleteStyle bulkDeleteStyle() const;
+ DB::BulkUpdateStyle bulkUpdateStyle() const;
+
+ DB::SelectCommand * newSelectCommand(const std::string & sql) const;
+ DB::ModifyCommand * newModifyCommand(const std::string & sql) const;
+
+ void beginBulkUpload(const char *, const char *) const;
+ void endBulkUpload(const char *) const;
+ size_t bulkUploadData(const char *, size_t) const;
+
+ mutable MYSQL conn;
+
+ private:
+ my_bool my_true;
+
+ void checkResult(my_bool actual, my_bool expected) const;
+ mutable unsigned int txDepth;
+ mutable bool rolledback;
+ };
+}
+
+#endif
+
diff --git a/libmysqlpp/error.cpp b/libmysqlpp/error.cpp
new file mode 100644
index 0000000..2b9b418
--- /dev/null
+++ b/libmysqlpp/error.cpp
@@ -0,0 +1,29 @@
+#include "error.h"
+#include <string.h>
+
+MySQL::Error::Error() :
+ msg(NULL)
+{
+}
+
+MySQL::Error::Error(const MySQL::Error & e) :
+ msg(e.msg ? strdup(e.msg) : NULL)
+{
+}
+
+MySQL::Error::Error(const char * e) :
+ msg(e ? strdup(e) : NULL)
+{
+}
+
+MySQL::Error::~Error() throw()
+{
+ free(msg);
+}
+
+const char *
+MySQL::Error::what() const throw()
+{
+ return msg ? msg : "No message";
+}
+
diff --git a/libmysqlpp/error.h b/libmysqlpp/error.h
new file mode 100644
index 0000000..9ab666b
--- /dev/null
+++ b/libmysqlpp/error.h
@@ -0,0 +1,24 @@
+#ifndef MY_ERROR_H
+#define MY_ERROR_H
+
+#include "../libdbpp/error.h"
+
+namespace MySQL {
+ class Error : public DB::Error {
+ public:
+ Error();
+ Error(const Error &);
+ Error(const char *);
+ ~Error() throw();
+
+ const char * what() const throw();
+
+ private:
+ char * msg;
+ };
+ class ConnectionError : public Error, public virtual DB::ConnectionError {
+ };
+}
+
+#endif
+
diff --git a/libmysqlpp/modifycommand.cpp b/libmysqlpp/modifycommand.cpp
new file mode 100644
index 0000000..e6e86b7
--- /dev/null
+++ b/libmysqlpp/modifycommand.cpp
@@ -0,0 +1,30 @@
+#include "modifycommand.h"
+#include "error.h"
+#include <stdlib.h>
+#include "connection.h"
+
+MySQL::ModifyCommand::ModifyCommand(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ DB::ModifyCommand(sql),
+ MySQL::Command(conn, sql)
+{
+}
+
+MySQL::ModifyCommand::~ModifyCommand()
+{
+}
+
+unsigned int
+MySQL::ModifyCommand::execute(bool anc)
+{
+ bindParams();
+ if (mysql_stmt_execute(stmt)) {
+ throw Error(mysql_stmt_error(stmt));
+ }
+ int rows = mysql_stmt_affected_rows(stmt);
+ if (rows == 0 && !anc) {
+ throw Error("No rows affected");
+ }
+ return rows;
+}
+
diff --git a/libmysqlpp/modifycommand.h b/libmysqlpp/modifycommand.h
new file mode 100644
index 0000000..27c4620
--- /dev/null
+++ b/libmysqlpp/modifycommand.h
@@ -0,0 +1,25 @@
+#ifndef MY_MODIFYCOMMAND_H
+#define MY_MODIFYCOMMAND_H
+
+#include "../libdbpp/modifycommand.h"
+#include "command.h"
+
+namespace MySQL {
+ class Connection;
+ class ModifyCommand : public DB::ModifyCommand, public Command {
+ public:
+ ModifyCommand(const Connection *, const std::string & sql);
+ virtual ~ModifyCommand();
+
+ unsigned int execute(bool);
+
+ private:
+ void prepare() const;
+ mutable bool prepared;
+ };
+}
+
+#endif
+
+
+
diff --git a/libmysqlpp/selectcommand.cpp b/libmysqlpp/selectcommand.cpp
new file mode 100644
index 0000000..e6bf789
--- /dev/null
+++ b/libmysqlpp/selectcommand.cpp
@@ -0,0 +1,129 @@
+#include "selectcommand.h"
+#include "connection.h"
+#include "column.h"
+#include "error.h"
+#include <string.h>
+
+MySQL::SelectCommand::SelectCommand(const Connection * conn, const std::string & sql) :
+ DB::Command(sql),
+ DB::SelectCommand(sql),
+ MySQL::Command(conn, sql),
+ executed(false)
+{
+}
+
+void
+MySQL::SelectCommand::execute()
+{
+ if (!executed) {
+ bindParams();
+ fields.resize(mysql_stmt_field_count(stmt));
+ for (Binds::iterator i = fields.begin(); i != fields.end(); ++i) {
+ memset(&*i, 0, sizeof(MYSQL_BIND));
+ }
+ MYSQL_RES * prepare_meta_result = mysql_stmt_result_metadata(stmt);
+ MYSQL_FIELD * fieldDefs = mysql_fetch_fields(prepare_meta_result);
+ for (unsigned int i = 0; i < fields.size(); i += 1) {
+ switch (fieldDefs[i].type) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_YEAR:
+ columns.insert(boost::shared_ptr<ColumnBase>(new Column<int64_t, MYSQL_TYPE_LONGLONG>(fieldDefs[i].name, i, &fields[i])));
+ break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ columns.insert(boost::shared_ptr<ColumnBase>(new Column<double, MYSQL_TYPE_DOUBLE>(fieldDefs[i].name, i, &fields[i])));
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ columns.insert(boost::shared_ptr<ColumnBase>(new Column<MYSQL_TIME, MYSQL_TYPE_DATETIME>(fieldDefs[i].name, i, &fields[i])));
+ break;
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ columns.insert(boost::shared_ptr<ColumnBase>(new StringColumn(fieldDefs[i].name, i, &fields[i], fieldDefs[i].length)));
+ break;
+ case MYSQL_TYPE_NULL:
+ columns.insert(boost::shared_ptr<ColumnBase>(new NullColumn(fieldDefs[i].name, i, &fields[i])));
+ break;
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_TIME:
+ default:
+ mysql_free_result(prepare_meta_result);
+ throw Error("Unexpected type");
+ }
+ }
+ mysql_free_result(prepare_meta_result);
+ if (mysql_stmt_bind_result(stmt, &fields.front())) {
+ throw Error(mysql_stmt_error(stmt));
+ }
+ if (mysql_stmt_execute(stmt)) {
+ throw Error(mysql_stmt_error(stmt));
+ }
+ if (mysql_stmt_store_result(stmt)) {
+ throw Error(mysql_stmt_error(stmt));
+ }
+ executed = true;
+ }
+}
+
+bool
+MySQL::SelectCommand::fetch()
+{
+ execute();
+ switch (mysql_stmt_fetch(stmt)) {
+ case 0:
+ return true;
+ case MYSQL_NO_DATA:
+ return false;
+ default:
+ throw Error(mysql_stmt_error(stmt));
+ }
+}
+
+const DB::Column&
+MySQL::SelectCommand::operator[](unsigned int n) const
+{
+ if (n < columns.size()) {
+ return **columns.get<0>().find(n);
+ }
+ throw Error();
+}
+
+const DB::Column&
+MySQL::SelectCommand::operator[](const Glib::ustring & n) const
+{
+ typedef Columns::nth_index<1>::type CbyName;
+ CbyName::iterator i = columns.get<1>().find(n);
+ if (i != columns.get<1>().end()) {
+ return **i;
+ }
+ throw Error();
+}
+
+unsigned int
+MySQL::SelectCommand::getOrdinal(const Glib::ustring & n) const
+{
+ typedef Columns::nth_index<1>::type CbyName;
+ CbyName::iterator i = columns.get<1>().find(n);
+ if (i != columns.get<1>().end()) {
+ return (*i)->colNo;
+ }
+ throw Error();
+}
+
+unsigned int
+MySQL::SelectCommand::columnCount() const
+{
+ return fields.size();
+}
+
diff --git a/libmysqlpp/selectcommand.h b/libmysqlpp/selectcommand.h
new file mode 100644
index 0000000..a338599
--- /dev/null
+++ b/libmysqlpp/selectcommand.h
@@ -0,0 +1,44 @@
+#ifndef MY_SELECTCOMMAND_H
+#define MY_SELECTCOMMAND_H
+
+#include "../libdbpp/selectcommand.h"
+#include "../libdbpp/column.h"
+#include "command.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <vector>
+#include <map>
+
+namespace MySQL {
+ class Connection;
+ class ColumnBase;
+ class SelectCommand : public DB::SelectCommand, public Command {
+ public:
+ SelectCommand(const Connection *, const std::string & sql);
+
+ bool fetch();
+ void execute();
+ const DB::Column& operator[](unsigned int) const;
+ const DB::Column& operator[](const Glib::ustring&) const;
+ unsigned int columnCount() const;
+ unsigned int getOrdinal(const Glib::ustring&) const;
+
+ private:
+ bool executed;
+ Binds fields;
+ typedef boost::multi_index_container<boost::shared_ptr<ColumnBase>, boost::multi_index::indexed_by<
+ boost::multi_index::ordered_unique<boost::multi_index::member<DB::Column, const unsigned int, &DB::Column::colNo>>,
+ boost::multi_index::ordered_unique<boost::multi_index::member<DB::Column, const Glib::ustring, &DB::Column::name>>
+ >> Columns;
+ Columns columns;
+
+ friend class ColumnBase;
+ };
+}
+
+#endif
+
+