summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2006-07-08 16:32:05 +0000
committerrandomdan <randomdan@localhost>2006-07-08 16:32:05 +0000
commit2a1fa15d8baa4eda37e17b2e9362f8bde17a939d (patch)
tree09386e52320b7c52a521ab56fc7553896e639dcd
downloadlibdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.tar.bz2
libdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.tar.xz
libdbpp-odbc-2a1fa15d8baa4eda37e17b2e9362f8bde17a939d.zip
libcodbcpp initial release
-rw-r--r--libodbcpp/bind.cpp36
-rw-r--r--libodbcpp/bind.h29
-rw-r--r--libodbcpp/client.h7
-rw-r--r--libodbcpp/column.cpp54
-rw-r--r--libodbcpp/column.h40
-rw-r--r--libodbcpp/command.cpp35
-rw-r--r--libodbcpp/command.h38
-rwxr-xr-xlibodbcpp/configure4
-rw-r--r--libodbcpp/connection.cpp52
-rw-r--r--libodbcpp/connection.h18
-rw-r--r--libodbcpp/dsn.cpp13
-rw-r--r--libodbcpp/dsn.h18
-rw-r--r--libodbcpp/error.cpp62
-rw-r--r--libodbcpp/error.h16
-rw-r--r--libodbcpp/makefile.in26
-rw-r--r--libodbcpp/modifycommand.cpp33
-rw-r--r--libodbcpp/modifycommand.h17
-rw-r--r--libodbcpp/param.cpp165
-rw-r--r--libodbcpp/param.h30
-rw-r--r--libodbcpp/selectcommand.cpp139
-rw-r--r--libodbcpp/selectcommand.h23
-rw-r--r--libodbcpp/string.cpp39
-rw-r--r--libodbcpp/string.h21
-rw-r--r--libodbcpp/timetypepair.cpp59
-rw-r--r--libodbcpp/timetypepair.h30
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
+