diff options
author | randomdan <randomdan@localhost> | 2012-11-18 19:13:16 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2012-11-18 19:13:16 +0000 |
commit | 49286cb2d2987e4d2f6a07f6b9ed46d4de97d9ac (patch) | |
tree | 9064882e6e44203bf35bf70089500c326458b6f1 | |
parent | Migrate all stuff to stricter compilations/links and C++0x builds (diff) | |
download | libdbpp-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.jam | 26 | ||||
-rw-r--r-- | libmysqlpp/column.cpp | 112 | ||||
-rw-r--r-- | libmysqlpp/column.h | 42 | ||||
-rw-r--r-- | libmysqlpp/command.cpp | 159 | ||||
-rw-r--r-- | libmysqlpp/command.h | 45 | ||||
-rw-r--r-- | libmysqlpp/connection.cpp | 157 | ||||
-rw-r--r-- | libmysqlpp/connection.h | 42 | ||||
-rw-r--r-- | libmysqlpp/error.cpp | 29 | ||||
-rw-r--r-- | libmysqlpp/error.h | 24 | ||||
-rw-r--r-- | libmysqlpp/modifycommand.cpp | 30 | ||||
-rw-r--r-- | libmysqlpp/modifycommand.h | 25 | ||||
-rw-r--r-- | libmysqlpp/selectcommand.cpp | 129 | ||||
-rw-r--r-- | libmysqlpp/selectcommand.h | 44 |
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 + + |