diff options
author | randomdan <randomdan@localhost> | 2006-07-08 16:32:05 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2006-07-08 16:32:05 +0000 |
commit | 2a1fa15d8baa4eda37e17b2e9362f8bde17a939d (patch) | |
tree | 09386e52320b7c52a521ab56fc7553896e639dcd | |
download | libdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.tar.bz2 libdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.tar.xz libdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.zip |
libcodbcpp initial release
-rw-r--r-- | libodbcpp/bind.cpp | 36 | ||||
-rw-r--r-- | libodbcpp/bind.h | 29 | ||||
-rw-r--r-- | libodbcpp/client.h | 7 | ||||
-rw-r--r-- | libodbcpp/column.cpp | 54 | ||||
-rw-r--r-- | libodbcpp/column.h | 40 | ||||
-rw-r--r-- | libodbcpp/command.cpp | 35 | ||||
-rw-r--r-- | libodbcpp/command.h | 38 | ||||
-rwxr-xr-x | libodbcpp/configure | 4 | ||||
-rw-r--r-- | libodbcpp/connection.cpp | 52 | ||||
-rw-r--r-- | libodbcpp/connection.h | 18 | ||||
-rw-r--r-- | libodbcpp/dsn.cpp | 13 | ||||
-rw-r--r-- | libodbcpp/dsn.h | 18 | ||||
-rw-r--r-- | libodbcpp/error.cpp | 62 | ||||
-rw-r--r-- | libodbcpp/error.h | 16 | ||||
-rw-r--r-- | libodbcpp/makefile.in | 26 | ||||
-rw-r--r-- | libodbcpp/modifycommand.cpp | 33 | ||||
-rw-r--r-- | libodbcpp/modifycommand.h | 17 | ||||
-rw-r--r-- | libodbcpp/param.cpp | 165 | ||||
-rw-r--r-- | libodbcpp/param.h | 30 | ||||
-rw-r--r-- | libodbcpp/selectcommand.cpp | 139 | ||||
-rw-r--r-- | libodbcpp/selectcommand.h | 23 | ||||
-rw-r--r-- | libodbcpp/string.cpp | 39 | ||||
-rw-r--r-- | libodbcpp/string.h | 21 | ||||
-rw-r--r-- | libodbcpp/timetypepair.cpp | 59 | ||||
-rw-r--r-- | libodbcpp/timetypepair.h | 30 |
25 files changed, 1004 insertions, 0 deletions
diff --git a/libodbcpp/bind.cpp b/libodbcpp/bind.cpp new file mode 100644 index 0000000..0808bbb --- /dev/null +++ b/libodbcpp/bind.cpp @@ -0,0 +1,36 @@ +#include <sqlext.h> +#include "command.h" +#include "param.h" + +ODBC::BindBase::BindBase() : + bindSize(0), + bindLen(0) +{ +} + +ODBC::Bind<unsigned char *>::~Bind() +{ + if (value) { + delete[] value; + } +} + +SQLINTEGER +ODBC::BindBase::length() const +{ + return bindLen; +} + +SQLUINTEGER +ODBC::BindBase::size() const +{ + return bindSize; +} + +bool +ODBC::BindBase::isNull() const +{ + return (bindLen == SQL_NULL_DATA); +} + + diff --git a/libodbcpp/bind.h b/libodbcpp/bind.h new file mode 100644 index 0000000..36c7584 --- /dev/null +++ b/libodbcpp/bind.h @@ -0,0 +1,29 @@ +#ifndef ODBC_BIND_H +#define ODBC_BIND_H + +#include <sql.h> + +namespace ODBC { + class BindBase { + public: + BindBase(); + virtual ~BindBase() {} + SQLINTEGER length() const; + SQLUINTEGER size() const; + bool isNull() const; + private: + SQLUINTEGER bindSize; // Allocated memory + SQLINTEGER bindLen; // Used memory + friend class Param; + friend class Column; + }; + template <class t> + class Bind { + public: + virtual ~Bind() {} + t value; + }; +} + +#endif + diff --git a/libodbcpp/client.h b/libodbcpp/client.h new file mode 100644 index 0000000..ac76b3f --- /dev/null +++ b/libodbcpp/client.h @@ -0,0 +1,7 @@ +#include "column.h" +#include "command.h" +#include "connection.h" +#include "dsn.h" +#include "modifycommand.h" +#include "param.h" +#include "selectcommand.h" diff --git a/libodbcpp/column.cpp b/libodbcpp/column.cpp new file mode 100644 index 0000000..212b792 --- /dev/null +++ b/libodbcpp/column.cpp @@ -0,0 +1,54 @@ +#include "column.h" +#include "error.h" +#include "timetypepair.h" + +ODBC::Column::Column(String n, u_int i) : + colNo(i), + name(n), + fresh(false) +{ +} + +ODBC::Column::~Column() +{ +} + +#define ODBC_DEFAULT_COLUMN_CAST(ctype, rtype) \ + ODBC::Column::operator rtype() const { \ + return (dynamic_cast<const _Column<ctype>& >(*this)).value; \ + } + +ODBC_DEFAULT_COLUMN_CAST(SQLINTEGER, unsigned int); +ODBC_DEFAULT_COLUMN_CAST(SQLINTEGER, unsigned long long); +ODBC_DEFAULT_COLUMN_CAST(SQLINTEGER, long long); +ODBC_DEFAULT_COLUMN_CAST(SQLINTEGER, int); +ODBC_DEFAULT_COLUMN_CAST(SQLDOUBLE, double); +ODBC_DEFAULT_COLUMN_CAST(SQLDOUBLE, float); +ODBC_DEFAULT_COLUMN_CAST(SQLCHAR*, const unsigned char * const); +ODBC_DEFAULT_COLUMN_CAST(SQLCHAR*, String); +ODBC::Column::operator std::string() const { + return (const char*)((dynamic_cast<const _Column<SQLCHAR*>& >(*this)).value); +} +ODBC::Column::operator const char * const () const { + return (const char*)((dynamic_cast<const _Column<SQLCHAR*>& >(*this)).value); +} +ODBC::Column::operator const struct tm & () const { + const _Column<TimeTypePair>& c = dynamic_cast<const _Column<TimeTypePair>& >(*this); + if (c.fresh) { + c.value.sql2c(); + c.fresh = false; + } + return c.value.c(); +} + +void +ODBC::Column::bind(SQLHANDLE hStmt, SQLUINTEGER col, SQLSMALLINT ctype, void * buf, size_t size) +{ + bindSize = size; + RETCODE rc = SQLBindCol(hStmt, col, ctype, buf, bindSize, &bindLen); + if (rc != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: Bind column %lu", __FUNCTION__, col); + } +} + + diff --git a/libodbcpp/column.h b/libodbcpp/column.h new file mode 100644 index 0000000..9f597c7 --- /dev/null +++ b/libodbcpp/column.h @@ -0,0 +1,40 @@ +#ifndef ODBC_COLUMN_H +#define ODBC_COLUMN_H + +#include "string.h" +#include "bind.h" + +namespace ODBC { + class Column : public BindBase { + public: + Column(String, u_int); + virtual ~Column(); + void bind(SQLHANDLE, SQLUINTEGER, SQLSMALLINT, void*, size_t); + operator int () const; + operator unsigned int () const; + operator long long () const; + operator unsigned long long () const; + operator double () const; + operator float () const; + operator const unsigned char * const () const; + operator const char * const () const; + operator std::string () const; + operator String () const; + operator const struct tm & () const; + + const u_int colNo; + const String name; + private: + mutable bool fresh; + friend class SelectCommand; + }; + template <class t> + class _Column : public Bind<t>, public Column { + public: + _Column(String, u_int); + ~_Column() {} + }; +} + +#endif + diff --git a/libodbcpp/command.cpp b/libodbcpp/command.cpp new file mode 100644 index 0000000..6c8bf3c --- /dev/null +++ b/libodbcpp/command.cpp @@ -0,0 +1,35 @@ +#include "command.h" +#include "error.h" +#include "param.h" +#include <sqlext.h> + +ODBC::Command::Command(const Connection& c, String s) : + sql(s) +{ + RETCODE rc = SQLAllocHandle(SQL_HANDLE_STMT, c.conn, &hStmt); + if (rc != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "Allocate statement handle"); + } + rc = SQLPrepare(hStmt, sql, sql.size()); + if (rc != SQL_SUCCESS) { + SQLFreeHandle(SQL_HANDLE_STMT, hStmt); + throw Error(rc, SQL_HANDLE_STMT, hStmt, "Prepare statement"); + } + SQLSMALLINT pcount; + rc = SQLNumParams(hStmt, &pcount); + if (rc != SQL_SUCCESS) { + SQLFreeHandle(SQL_HANDLE_STMT, hStmt); + throw Error(rc, SQL_HANDLE_STMT, hStmt, "Parameter count"); + } + params.resize(pcount); +} + +ODBC::Command::~Command() +{ + for (Params::iterator i = params.begin(); i != params.end(); i++) { + if (*i) { + delete *i; + } + } +} + diff --git a/libodbcpp/command.h b/libodbcpp/command.h new file mode 100644 index 0000000..8e59c76 --- /dev/null +++ b/libodbcpp/command.h @@ -0,0 +1,38 @@ +#ifndef ODBC_COMMAND_H +#define ODBC_COMMAND_H + +#include <vector> +#include "connection.h" +#include "timetypepair.h" + +namespace ODBC { + class Param; + class Command { + typedef std::vector<Param*> Params; + public: + Command(const Connection&, String sql); + virtual ~Command() = 0; + + void bindParamI(unsigned int i, int val); + void bindParamI(unsigned int i, unsigned int val); + void bindParamF(unsigned int i, double val); + void bindParamF(unsigned int i, float val); + void bindParamS(unsigned int i, const char *); + void bindParamS(unsigned int i, const unsigned char *); + void bindParamS(unsigned int i, const unsigned char *, size_t); + void bindParamS(unsigned int i, std::string); + void bindParamS(unsigned int i, String); + void bindParamT(unsigned int i, struct tm *); + void bindParamT(unsigned int i, time_t); + + const String sql; + protected: + SQLHSTMT hStmt; + private: + Params params; + }; + +} + +#endif + diff --git a/libodbcpp/configure b/libodbcpp/configure new file mode 100755 index 0000000..a7d295d --- /dev/null +++ b/libodbcpp/configure @@ -0,0 +1,4 @@ +#!/bin/bash + +cp makefile.in makefile +makedepend *.cpp diff --git a/libodbcpp/connection.cpp b/libodbcpp/connection.cpp new file mode 100644 index 0000000..4130f08 --- /dev/null +++ b/libodbcpp/connection.cpp @@ -0,0 +1,52 @@ +#include <sqlext.h> +#include <syslog.h> +#include "connection.h" +#include "error.h" + +ODBC::Connection::Connection(const DSN& d) : + DSN(d), + env(0), + conn(0) +{ + SQLRETURN dberr = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + if ((dberr != SQL_SUCCESS) && (dberr != SQL_SUCCESS_WITH_INFO)) { + throw Error(dberr, SQL_HANDLE_ENV, env, "Allocate handle"); + } + + dberr = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + if ((dberr != SQL_SUCCESS) && (dberr != SQL_SUCCESS_WITH_INFO)) { + throw Error(dberr, SQL_HANDLE_ENV, env, "Set ODBC version"); + } + + dberr = SQLAllocHandle(SQL_HANDLE_DBC, env, &conn); + if ((dberr != SQL_SUCCESS) && (dberr != SQL_SUCCESS_WITH_INFO)) { + throw Error(dberr, SQL_HANDLE_ENV, env, "Allocate DBC handle"); + } + + dberr = SQLSetConnectAttr(conn, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); + if ((dberr != SQL_SUCCESS) && (dberr != SQL_SUCCESS_WITH_INFO)) { + throw Error(dberr, SQL_HANDLE_ENV, env, "Set connection attributes"); + } + + dberr = SQLConnect(conn, dsn, SQL_NTS, username, SQL_NTS, password, SQL_NTS); + if ((dberr != SQL_SUCCESS) && (dberr != SQL_SUCCESS_WITH_INFO)) { + throw Error(dberr, SQL_HANDLE_DBC, conn, "Connect"); + } +} + +ODBC::Connection::~Connection() +{ + if (conn) { + if (SQLDisconnect(conn) != SQL_SUCCESS) { + syslog(LOG_WARNING, "%s: Disconnect error", __FUNCTION__); + } + if (SQLFreeHandle(SQL_HANDLE_DBC, conn) != SQL_SUCCESS) { + syslog(LOG_WARNING, "%s: Free connection handle error", __FUNCTION__); + } + } + if (env) { + if (SQLFreeHandle(SQL_HANDLE_ENV, env) != SQL_SUCCESS) { + syslog(LOG_WARNING, "%s: Free connection handle error", __FUNCTION__); + } + } +} diff --git a/libodbcpp/connection.h b/libodbcpp/connection.h new file mode 100644 index 0000000..d5adb31 --- /dev/null +++ b/libodbcpp/connection.h @@ -0,0 +1,18 @@ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "dsn.h" +#include <sql.h> + +namespace ODBC { + class Connection : private DSN { + public: + Connection(const DSN& d); + ~Connection(); + SQLHENV env; + SQLHDBC conn; + }; +} + +#endif + diff --git a/libodbcpp/dsn.cpp b/libodbcpp/dsn.cpp new file mode 100644 index 0000000..ee4f5fe --- /dev/null +++ b/libodbcpp/dsn.cpp @@ -0,0 +1,13 @@ +#include "dsn.h" + +ODBC::DSN::DSN(String d, String u, String p) : + dsn(d), + username(u), + password(p) +{ +} + +ODBC::DSN::~DSN() +{ +} + diff --git a/libodbcpp/dsn.h b/libodbcpp/dsn.h new file mode 100644 index 0000000..ac4b6f5 --- /dev/null +++ b/libodbcpp/dsn.h @@ -0,0 +1,18 @@ +#ifndef DSN_H +#define DSN_H + +#include "string.h" + +namespace ODBC { + class DSN { + public: + DSN(String, String, String); + virtual ~DSN(); + String dsn; // DSN name for odbc.ini + String username; // User name + String password; // Password + }; +} + +#endif + diff --git a/libodbcpp/error.cpp b/libodbcpp/error.cpp new file mode 100644 index 0000000..c7f16c7 --- /dev/null +++ b/libodbcpp/error.cpp @@ -0,0 +1,62 @@ +#include <stdarg.h> +#include <stdio.h> +#include <syslog.h> +#include <malloc.h> +#include "error.h" + +static +void +odbc_verror(RETCODE err, SQLSMALLINT handletype, SQLHANDLE handle, char const * actionfmt, va_list ap) +{ + SQLCHAR sqlstatus[6]; + SQLINTEGER sqlerr; + SQLCHAR sqlerrmsg[12800]; + + char * action; + vasprintf(&action, actionfmt, ap); + + SQLRETURN rc = SQLGetDiagRec(handletype, handle, 1, sqlstatus, &sqlerr, sqlerrmsg, + sizeof(sqlerrmsg), NULL); + switch (rc) { + case SQL_SUCCESS: + case SQL_SUCCESS_WITH_INFO: + syslog(LOG_WARNING, "%s: %d: %ld: %5.5s: \"%s\" while attempting to %s", + __FUNCTION__, err, sqlerr, sqlstatus, sqlerrmsg, action); + break; + + case SQL_INVALID_HANDLE: + syslog(LOG_ERR, "%s: (%d) Invalid handle passed into function trying to %s.", + __FUNCTION__, err, action); + break; + + case SQL_NO_DATA: + syslog(LOG_ERR, "%s: (%d) No error data available for record trying to %s.", + __FUNCTION__, err, action); + break; + + case SQL_ERROR: + default: + syslog(LOG_ERR, "%s: Unexpected error!!", __FUNCTION__); + break; + } + free(action); +} + +ODBC::Error::Error(RETCODE err, SQLSMALLINT handletype, SQLHANDLE handle, char const * action, ...) +{ + va_list ap; + + va_start(ap, action); + odbc_verror(err, handletype, handle, action, ap); + va_end(ap); +} + +ODBC::Error::Error(char const * action, ...) +{ + va_list ap; + + va_start(ap, action); + vsyslog(LOG_ERR, action, ap); + va_end(ap); +} + diff --git a/libodbcpp/error.h b/libodbcpp/error.h new file mode 100644 index 0000000..f7c2958 --- /dev/null +++ b/libodbcpp/error.h @@ -0,0 +1,16 @@ +#ifndef ODBC_ERROR_H +#define ODBC_ERROR_H + +#include <sql.h> + +namespace ODBC { + class Error { + public: + Error(RETCODE err, SQLSMALLINT handletype, SQLHANDLE handle, char const * action, ...) + __attribute__((format(printf, 5, 6))); + Error(char const * action, ...) + __attribute__((format(printf, 2, 3))); + }; +} + +#endif diff --git a/libodbcpp/makefile.in b/libodbcpp/makefile.in new file mode 100644 index 0000000..93b361b --- /dev/null +++ b/libodbcpp/makefile.in @@ -0,0 +1,26 @@ +default : libodbcpp.so libodbcpp.a + +OBJS = \ + error.o \ + connection.o \ + dsn.o \ + command.o \ + string.o \ + bind.o \ + param.o \ + column.o \ + timetypepair.o \ + modifycommand.o \ + selectcommand.o + +libodbcpp.so : ${OBJS} + ${CXX} -shared -o $@ ${OBJS} -lodbc +libodbcpp.a : ${OBJS} + ar rc $@ ${OBJS} + +.PHONY: clean +.PHONY: reallyclean +clean: + rm -f *.o *.a *.so +reallyclean: clean + rm makefile diff --git a/libodbcpp/modifycommand.cpp b/libodbcpp/modifycommand.cpp new file mode 100644 index 0000000..706b3fd --- /dev/null +++ b/libodbcpp/modifycommand.cpp @@ -0,0 +1,33 @@ +#include "modifycommand.h" +#include "error.h" + +ODBC::ModifyCommand::ModifyCommand(const ODBC::Connection &c, String sql) : + Command(c, sql) +{ +} + +ODBC::ModifyCommand::~ModifyCommand() +{ +} + +unsigned int +ODBC::ModifyCommand::execute(bool anc) +{ + RETCODE rc = SQLExecute(hStmt); + if (rc != SQL_SUCCESS) { + if (rc != SQL_NO_DATA || !anc) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLExecute", + __FUNCTION__); + } + } + SQLINTEGER rows; + if ((rc = SQLRowCount(hStmt, &rows)) != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLRowCount", + __FUNCTION__); + } + if (rows > 0 || anc) { + return rows; + } + throw Error("%s: No rows affected", __FUNCTION__); +} + diff --git a/libodbcpp/modifycommand.h b/libodbcpp/modifycommand.h new file mode 100644 index 0000000..ef480c9 --- /dev/null +++ b/libodbcpp/modifycommand.h @@ -0,0 +1,17 @@ +#ifndef ODBC_MODIFYCOMMAND_H +#define ODBC_MODIFYCOMMAND_H + +#include "command.h" + +namespace ODBC { + class ModifyCommand : public Command { + public: + ModifyCommand(const Connection&, String sql); + ~ModifyCommand(); + // Execute the command and return effected row count + unsigned int execute(bool allowNoChange = true); + }; +} + +#endif + diff --git a/libodbcpp/param.cpp b/libodbcpp/param.cpp new file mode 100644 index 0000000..7657ef4 --- /dev/null +++ b/libodbcpp/param.cpp @@ -0,0 +1,165 @@ +#include <sqlext.h> +#include "param.h" +#include "command.h" +#include "error.h" + +ODBC::Param::Param() : + bound(false) +{ +} + +ODBC::Param::~Param(){ +} + +template <class t> +ODBC::_Param<t>* +ODBC::Param::makeParam(ODBC::Param*& p) +{ + if (p) { + _Param<t>* np = dynamic_cast<_Param<t>*>(p); + if (np) { + return np; + } + delete p; + } + _Param<t>* np = new _Param<t>(); + p = np; + return np; +} + +void +ODBC::Param::bind(SQLHANDLE hStmt, SQLUINTEGER col, SQLSMALLINT ctype, SQLSMALLINT stype, + SQLINTEGER colsize, SQLINTEGER dp, const void* value, size_t buflen) +{ + RETCODE rc = SQLBindParameter(hStmt, col, SQL_PARAM_INPUT, ctype, stype, + colsize, dp, (void*)value, buflen, (SQLINTEGER*)&bindLen); + if (rc != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: Bind for column %lu", + __FUNCTION__, col); + } +} + +template <class T> +void +ODBC::Param::makeBindLen(T*& p, size_t newLen) +{ + if (bindSize <= newLen) { + if (bindSize) { + delete p; + } + bindSize = newLen; + p = new T[newLen]; + } + bindLen = newLen; +} + +void +ODBC::Command::bindParamI(unsigned int i, int val) +{ + if (i < params.size()) { + _Param<SQLINTEGER>* p = Param::makeParam<SQLINTEGER>(params[i]); + p->value = val; + if (!p->bound) { + p->bind(this->hStmt, i + 1, SQL_C_SLONG, SQL_C_LONG, 0, 0, + &p->value, sizeof(SQLINTEGER)); + } + return; + } + throw Error("%s: Bind out of bounds", __FUNCTION__); +} +void +ODBC::Command::bindParamI(unsigned int i, unsigned int val) +{ + if (i < params.size()) { + _Param<SQLUINTEGER>* p = Param::makeParam<SQLUINTEGER>(params[i]); + p->value = val; + if (!p->bound) { + p->bind(this->hStmt, i + 1, SQL_C_ULONG, SQL_C_ULONG, 0, 0, + &p->value, sizeof(SQLUINTEGER)); + } + return; + } + throw Error("%s: Bind out of bounds", __FUNCTION__); +} +void +ODBC::Command::bindParamF(unsigned int i, double val) +{ + if (i < params.size()) { + _Param<SQLDOUBLE>* p = Param::makeParam<SQLDOUBLE>(params[i]); + p->value = val; + if (!p->bound) { + p->bind(this->hStmt, i + 1, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, + &p->value, sizeof(SQLDOUBLE)); + } + return; + } + throw Error("%s: Bind out of bounds", __FUNCTION__); +} +void +ODBC::Command::bindParamS(unsigned int i, const unsigned char * val, size_t length) +{ + if (i < params.size()) { + _Param<SQLCHAR*>* p = Param::makeParam<SQLCHAR*>(params[i]); + p->makeBindLen(p->value, length); + memcpy(p->value, val, length); + if (!p->bound) { + p->bind(this->hStmt, i + 1, SQL_C_CHAR, SQL_CHAR, 0, 0, p->value, length); + } + return; + } + throw Error("%s: Bind out of bounds", __FUNCTION__); +} +void +ODBC::Command::bindParamT(unsigned int i, struct tm * val) +{ + if (i < params.size()) { + _Param<TimeTypePair>* p = Param::makeParam<TimeTypePair>(params[i]); + p->value.set(*val); + if (!p->bound) { + p->bind(this->hStmt, i + 1, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, + sizeof(SQL_TIMESTAMP_STRUCT), 0, &p->value.sql(), sizeof(SQL_TIMESTAMP_STRUCT)); + } + return; + } + throw Error("%s: Bind out of bounds", __FUNCTION__); +} + + + +// Wrappers for all the roughly compatable types + +void +ODBC::Command::bindParamS(unsigned int i, String val) +{ + bindParamS(i, val.c_str(), val.size()); +} +void +ODBC::Command::bindParamS(unsigned int i, const unsigned char * val) +{ + const unsigned char * x = val; + while (*val++); + bindParamS(i, val, val - x); +} +void +ODBC::Command::bindParamS(unsigned int i, const char * val) +{ + bindParamS(i, (unsigned char *)val, strlen(val)); +} +void +ODBC::Command::bindParamS(unsigned int i, std::string val) +{ + bindParamS(i, (unsigned char *)val.c_str(), val.size()); +} +void +ODBC::Command::bindParamF(unsigned int i, float val) +{ + bindParamF(i, (double)val); +} +void +ODBC::Command::bindParamT(unsigned int i, time_t val) +{ + struct tm t; + gmtime_r(&val, &t); + bindParamT(i, &t); +} + diff --git a/libodbcpp/param.h b/libodbcpp/param.h new file mode 100644 index 0000000..a3cb347 --- /dev/null +++ b/libodbcpp/param.h @@ -0,0 +1,30 @@ +#ifndef ODBC_PARAM_H +#define ODBC_PARAM_H + +#include <malloc.h> +#include "bind.h" + +namespace ODBC { + template <class> class _Param; + class Param : public BindBase { + public: + Param(); + virtual ~Param(); + bool bound; // Has SqlBind... been called? + void bind(SQLHANDLE, SQLUINTEGER, SQLSMALLINT, SQLSMALLINT, SQLINTEGER, + SQLINTEGER, const void*, size_t); + template <class T> + void makeBindLen(T*&, size_t newSize); + template <class t> + static ODBC::_Param<t>* + makeParam(ODBC::Param*& p); + }; + template <class t> + class _Param : public Bind<t>, public Param { + public: + ~_Param() {} + }; +} + +#endif + diff --git a/libodbcpp/selectcommand.cpp b/libodbcpp/selectcommand.cpp new file mode 100644 index 0000000..e097865 --- /dev/null +++ b/libodbcpp/selectcommand.cpp @@ -0,0 +1,139 @@ +#include "selectcommand.h" +#include "error.h" +#include "column.h" +#include <sqlext.h> + +ODBC::SelectCommand::SelectCommand(const Connection& c, String s) : + Command(c, s) +{ +} + +ODBC::SelectCommand::~SelectCommand() +{ + for (Columns::iterator i = columns.begin(); i != columns.end(); i++) { + if (*i) { + delete *i; + } + } + RETCODE rc; + if ((rc = SQLCloseCursor(hStmt)) != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLCloseCursor", + __FUNCTION__); + } +} + +bool +ODBC::SelectCommand::fetch() +{ + if (columns.size() == 0) { + execute(); + } + RETCODE rc = SQLFetch(hStmt); + switch (rc) { + case SQL_SUCCESS: + for (Columns::iterator col = columns.begin(); col != columns.end(); col++) { + (*col)->fresh = true; + } + return true; + case SQL_NO_DATA: + return false; + default: + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLFetch", + __FUNCTION__); + } +} + +// This is here cos it needs to be referenced by (and only by) execute +template <class t> +ODBC::_Column<t>::_Column(String n, u_int i) : Column(n, i) +{ +} + +void +ODBC::SelectCommand::execute() +{ + RETCODE rc = SQLExecute(hStmt); + if (rc != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLExecute", + __FUNCTION__); + } + SQLSMALLINT colCount; + if ((rc = SQLNumResultCols(hStmt, &colCount)) != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLNumResultCols", + __FUNCTION__); + } + if (colCount < 1) { + throw Error("%s: No result columns", __FUNCTION__); + } + columns.resize(colCount); + for (int col = 0; col < colCount; col++) { + SQLCHAR colName[300]; + SQLSMALLINT nameLen, dp, nullable, bindType; + SQLUINTEGER bindSize; + int sqlcol = col + 1; + if ((rc = SQLDescribeCol(hStmt, sqlcol, colName, sizeof(colName), &nameLen, &bindType, + &bindSize, &dp, &nullable)) != SQL_SUCCESS) { + throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLDescribeCol for %d", + __FUNCTION__, col); + } + colName[nameLen] = '\0'; + switch (bindType) { + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + { + _Column<SQLCHAR*>* s = new _Column<SQLCHAR*>(colName, col); + s->value = new SQLCHAR[bindSize + 1]; + s->bind(hStmt, sqlcol, SQL_C_CHAR, s->value, bindSize + 1); + columns[col] = s; + break; + } + case SQL_DECIMAL: + case SQL_NUMERIC: + case SQL_REAL: + case SQL_FLOAT: + case SQL_DOUBLE: + { + _Column<SQLDOUBLE>* d = new _Column<SQLDOUBLE>(colName, col); + d->bind(hStmt, sqlcol, SQL_C_DOUBLE, &d->value, sizeof(double)); + columns[col] = d; + break; + } + case SQL_SMALLINT: + case SQL_INTEGER: + case SQL_TINYINT: + case SQL_BIGINT: + { + _Column<SQLINTEGER>* i = new _Column<SQLINTEGER>(colName, col); + i->bind(hStmt, sqlcol, SQL_C_LONG, &i->value, sizeof(int)); + columns[col] = i; + break; + } + case SQL_DATETIME: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_TYPE_TIMESTAMP: + { + _Column<TimeTypePair>* t = new _Column<TimeTypePair>(colName, col); + t->bind(hStmt, sqlcol, SQL_C_TYPE_TIMESTAMP, &t->value.sql(), + sizeof(SQL_TIMESTAMP_STRUCT)); + columns[col] = t; + break; + } + default: + throw Error( + "%s: Bad column type: idx=%d, name=%s, type=%d, size=%ld, dp=%d, null=%d", + __FUNCTION__, col, colName, bindType, bindSize, dp, nullable); + break; + }; + } +} + + +const ODBC::Column& +ODBC::SelectCommand::operator[](unsigned int col) const +{ + return *columns[col]; +} + + diff --git a/libodbcpp/selectcommand.h b/libodbcpp/selectcommand.h new file mode 100644 index 0000000..fee1afc --- /dev/null +++ b/libodbcpp/selectcommand.h @@ -0,0 +1,23 @@ +#ifndef ODBC_SELECTCOMMAND_H +#define ODBC_SELECTCOMMAND_H + +#include "command.h" + +namespace ODBC { + class Column; + class SelectCommand : public Command { + typedef std::vector<Column*> Columns; + public: + SelectCommand (const Connection&, String sql); + ~SelectCommand(); + bool fetch(); + const Column& operator[](unsigned int col) const; + unsigned int columnCount() const; + private: + void execute(); + Columns columns; + }; +} + +#endif + diff --git a/libodbcpp/string.cpp b/libodbcpp/string.cpp new file mode 100644 index 0000000..d04e141 --- /dev/null +++ b/libodbcpp/string.cpp @@ -0,0 +1,39 @@ +#include <stdarg.h> +#include "string.h" + +ODBC::String::String() +{ +} +ODBC::String::String(std::basic_string<unsigned char> s) : + std::basic_string<unsigned char>(s) +{ +} +ODBC::String::String(std::basic_string<char> s) : + std::basic_string<unsigned char>((unsigned char *)s.c_str()) +{ +} +ODBC::String::String(const char * s) : + std::basic_string<unsigned char>((unsigned char *)s) +{ +} +ODBC::String::String(const unsigned char * s) : + std::basic_string<unsigned char>(s) +{ +} + +ODBC::String::operator unsigned char * () const +{ + return (unsigned char*)c_str(); +} + +ODBC::String +ODBC::String::Format(const char * fmt, ...) +{ + char * buf; + va_list va; + va_start(va, fmt); + vasprintf(&buf, fmt, va); + va_end(va); + return buf; +} + diff --git a/libodbcpp/string.h b/libodbcpp/string.h new file mode 100644 index 0000000..d3c4d41 --- /dev/null +++ b/libodbcpp/string.h @@ -0,0 +1,21 @@ +#ifndef ODBC_STRING_H +#define ODBC_STRING_H + +#include <string> + +namespace ODBC { + class String : public std::basic_string<unsigned char> { + public: + String(const unsigned char *); + String(const char *); + String(std::basic_string<unsigned char>); + String(std::basic_string<char>); + String(); + operator unsigned char *() const; + + static String Format(const char *, ...); + }; +} + +#endif + diff --git a/libodbcpp/timetypepair.cpp b/libodbcpp/timetypepair.cpp new file mode 100644 index 0000000..17a840e --- /dev/null +++ b/libodbcpp/timetypepair.cpp @@ -0,0 +1,59 @@ +#include "command.h" + +ODBC::TimeTypePair::TimeTypePair() +{ + memset(&_c, 0, sizeof(_c)); + memset(&_sql, 0, sizeof(_sql)); +} +ODBC::TimeTypePair::TimeTypePair(const ODBC::TimeTypePair::SQL_TS& t) +{ + memset(&_c, 0, sizeof(_c)); + memset(&_sql, 0, sizeof(_sql)); + set(t); +} +ODBC::TimeTypePair::TimeTypePair(tm const& t) +{ + memset(&_c, 0, sizeof(_c)); + memset(&_sql, 0, sizeof(_sql)); + set(t); +} + +tm const& +ODBC::TimeTypePair::set(const ODBC::TimeTypePair::SQL_TS& t) +{ + _sql = t; + sql2c(); + return _c; +} + +void +ODBC::TimeTypePair::sql2c() const +{ + _c.tm_year = _sql.year - 1900; + _c.tm_mon = _sql.month - 1; + _c.tm_mday = _sql.day; + _c.tm_hour = _sql.hour; + _c.tm_min = _sql.minute; + _c.tm_sec = _sql.second; +} + +const ODBC::TimeTypePair::SQL_TS& +ODBC::TimeTypePair::set(tm const& t) +{ + _c = t; + c2sql(); + return _sql; +} + +void +ODBC::TimeTypePair::c2sql() const +{ + _sql.year = _c.tm_year + 1900; + _sql.month = _c.tm_mon + 1; + _sql.day = _c.tm_mday; + _sql.hour = _c.tm_hour; + _sql.minute = _c.tm_min; + _sql.second = _c.tm_sec; + _sql.fraction = 0; +} + diff --git a/libodbcpp/timetypepair.h b/libodbcpp/timetypepair.h new file mode 100644 index 0000000..af77c0f --- /dev/null +++ b/libodbcpp/timetypepair.h @@ -0,0 +1,30 @@ +#ifndef ODBC_TIMETYPEPAIR_H +#define ODBC_TIMETYPEPAIR_H + +#include <time.h> +#include <sql.h> + +namespace ODBC { + class TimeTypePair { + typedef SQL_TIMESTAMP_STRUCT SQL_TS; + public: + TimeTypePair (); + TimeTypePair (const tm&); + TimeTypePair (const SQL_TS&); + + const SQL_TS& set(const tm&); + const tm& set(const SQL_TS&); + SQL_TS& sql() { return _sql; } + tm& c() { return _c; } + const SQL_TS& sql() const { return _sql; } + const tm& c() const { return _c; } + void sql2c() const; + void c2sql() const; + private: + mutable SQL_TS _sql; + mutable tm _c; + }; +}; + +#endif + |