From 95bc5ac789c2f29220f76c94fc93d6379dcd00c6 Mon Sep 17 00:00:00 2001 From: randomdan Date: Mon, 13 Sep 2010 19:57:25 +0000 Subject: Remove duplication in ODBC::Connection constructors Remove pointless specialisation on _Column for strings Set cursor type to scrollable (required to refetch a row) Resize binds if fetched data is truncated Support scrolling fetch (default is old 'next record' behaviour) --- libodbcpp/bind.h | 8 +------ libodbcpp/column.cpp | 16 ++++++++++++++ libodbcpp/column.h | 10 +++++++-- libodbcpp/command.cpp | 4 ++++ libodbcpp/connection.cpp | 53 +++++++++++++++++---------------------------- libodbcpp/connection.h | 2 ++ libodbcpp/selectcommand.cpp | 19 +++++++++++++--- libodbcpp/selectcommand.h | 2 +- 8 files changed, 68 insertions(+), 46 deletions(-) (limited to 'libodbcpp') diff --git a/libodbcpp/bind.h b/libodbcpp/bind.h index 5baa53a..fd69900 100644 --- a/libodbcpp/bind.h +++ b/libodbcpp/bind.h @@ -11,7 +11,7 @@ namespace ODBC { BindBase(); virtual ~BindBase() {} protected: - SQLINTEGER bindLen; // Used memory + SQLLEN bindLen; // Used memory friend class Param; friend class Column; friend class Command; @@ -23,12 +23,6 @@ namespace ODBC { mutable t value; }; typedef std::vector SQLCHARVEC; - template <> - class Bind { - public: - virtual ~Bind() {} - mutable SQLCHARVEC value; - }; } #endif diff --git a/libodbcpp/column.cpp b/libodbcpp/column.cpp index 6e517a6..3d37e50 100644 --- a/libodbcpp/column.cpp +++ b/libodbcpp/column.cpp @@ -1,3 +1,4 @@ +#include #include #include #include "column.h" @@ -17,6 +18,21 @@ ODBC::Column::~Column() delete composeCache; } +void +ODBC::Column::resize(SQLHANDLE hStmt) +{ +} + +void +ODBC::StringColumn::resize(SQLHANDLE hStmt) +{ + if (bindSize < bindLen) { + value.resize(bindLen + 1); + bindSize = bindLen; + bind(hStmt, colNo + 1, SQL_C_CHAR, &value[0], bindSize + 1); + } +} + bool ODBC::Column::isNull() const { diff --git a/libodbcpp/column.h b/libodbcpp/column.h index 67dd0c4..c9e65ca 100644 --- a/libodbcpp/column.h +++ b/libodbcpp/column.h @@ -10,6 +10,7 @@ namespace ODBC { Column(const Glib::ustring &, unsigned int); virtual ~Column(); void bind(SQLHANDLE, SQLUINTEGER, SQLSMALLINT, void*, size_t); + virtual void resize(SQLHANDLE); operator int () const; operator unsigned int () const; operator long long () const; @@ -32,8 +33,7 @@ namespace ODBC { const Glib::ustring name; protected: mutable Glib::ustring * composeCache; - private: - SQLUINTEGER bindSize; // Allocated memory + SQLLEN bindSize; // Allocated memory friend class SelectCommand; }; template @@ -47,6 +47,12 @@ namespace ODBC { int writeToBuf(char ** buf) const; int writeToBuf(char ** buf, const char * fmt) const; }; + class StringColumn : public _Column { + public: + StringColumn(const Glib::ustring & n, unsigned int i) : + _Column(n, i) { } + void resize(SQLHANDLE); + }; } void operator << (SQL_TIMESTAMP_STRUCT & target, const struct tm &); diff --git a/libodbcpp/command.cpp b/libodbcpp/command.cpp index 4ceb825..50dd679 100644 --- a/libodbcpp/command.cpp +++ b/libodbcpp/command.cpp @@ -11,6 +11,10 @@ ODBC::Command::Command(const Connection & c, const std::string & s) : if (rc != SQL_SUCCESS) { throw Error(rc, SQL_HANDLE_STMT, hStmt, "Allocate statement handle"); } + rc = SQLSetStmtAttr(hStmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_DYNAMIC, 0); + if ((rc != SQL_SUCCESS)) { + throw ConnectionError(rc, SQL_HANDLE_STMT, hStmt, "Set scrollable cursor"); + } rc = SQLPrepare(hStmt, (SQLCHAR*)sql.c_str(), sql.length()); if (rc != SQL_SUCCESS) { SQLFreeHandle(SQL_HANDLE_STMT, hStmt); diff --git a/libodbcpp/connection.cpp b/libodbcpp/connection.cpp index ef8fdbb..79c0f9d 100644 --- a/libodbcpp/connection.cpp +++ b/libodbcpp/connection.cpp @@ -10,6 +10,18 @@ ODBC::Connection::Connection(const DSN& d) : conn(0), txDepth(0), txAborted(false) +{ + connectPre(); + RETCODE dberr = SQLConnect(conn, (SQLCHAR*)d.dsn.c_str(), SQL_NTS, + (SQLCHAR*)d.username.c_str(), SQL_NTS, (SQLCHAR*)d.password.c_str(), SQL_NTS); + if ((dberr != SQL_SUCCESS)) { + throw ConnectionError(dberr, SQL_HANDLE_DBC, conn, "Connect"); + } + connectPost(); +} + +void +ODBC::Connection::connectPre() { SQLRETURN dberr = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); if ((dberr != SQL_SUCCESS)) { @@ -30,14 +42,12 @@ ODBC::Connection::Connection(const DSN& d) : if ((dberr != SQL_SUCCESS)) { throw ConnectionError(dberr, SQL_HANDLE_ENV, env, "Set connection attributes"); } +} - dberr = SQLConnect(conn, (SQLCHAR*)d.dsn.c_str(), SQL_NTS, - (SQLCHAR*)d.username.c_str(), SQL_NTS, (SQLCHAR*)d.password.c_str(), SQL_NTS); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_DBC, conn, "Connect"); - } - - dberr = SQLSetConnectOption(conn, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); +void +ODBC::Connection::connectPost() +{ + RETCODE dberr = SQLSetConnectOption(conn, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); if ((dberr != SQL_SUCCESS)) { throw ConnectionError(dberr, SQL_HANDLE_DBC, conn, "Set default auto commit"); } @@ -49,35 +59,12 @@ ODBC::Connection::Connection(const std::string & s) : txDepth(0), txAborted(false) { - SQLRETURN dberr = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_ENV, env, "Allocate handle"); - } - - dberr = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_ENV, env, "Set ODBC version"); - } - - dberr = SQLAllocHandle(SQL_HANDLE_DBC, env, &conn); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_ENV, env, "Allocate DBC handle"); - } - - dberr = SQLSetConnectAttr(conn, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_ENV, env, "Set connection attributes"); - } - - dberr = SQLDriverConnect(conn, NULL, (SQLCHAR*)s.c_str(), s.length(), NULL, 0, NULL, SQL_DRIVER_NOPROMPT); + connectPre(); + RETCODE dberr = SQLDriverConnect(conn, NULL, (SQLCHAR*)s.c_str(), s.length(), NULL, 0, NULL, SQL_DRIVER_NOPROMPT); if ((dberr != SQL_SUCCESS)) { throw ConnectionError(dberr, SQL_HANDLE_DBC, conn, "Connect"); } - - dberr = SQLSetConnectOption(conn, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); - if ((dberr != SQL_SUCCESS)) { - throw ConnectionError(dberr, SQL_HANDLE_DBC, conn, "Set default auto commit"); - } + connectPost(); } ODBC::Connection::~Connection() diff --git a/libodbcpp/connection.h b/libodbcpp/connection.h index 0d2e9d4..86ff7d8 100644 --- a/libodbcpp/connection.h +++ b/libodbcpp/connection.h @@ -24,6 +24,8 @@ namespace ODBC { SQLINTEGER getAttrInt(SQLINTEGER) const; private: + void connectPre(); + void connectPost(); mutable unsigned int txDepth; mutable bool txAborted; }; diff --git a/libodbcpp/selectcommand.cpp b/libodbcpp/selectcommand.cpp index 88a071f..3b903d6 100644 --- a/libodbcpp/selectcommand.cpp +++ b/libodbcpp/selectcommand.cpp @@ -3,6 +3,7 @@ #include "column.h" #include #include +#include ODBC::SelectCommand::SelectCommand(const Connection & c, const std::string & s) : Command(c, s) @@ -26,12 +27,12 @@ ODBC::SelectCommand::~SelectCommand() } bool -ODBC::SelectCommand::fetch() +ODBC::SelectCommand::fetch(SQLSMALLINT orientation, SQLLEN offset) { if (columns.size() == 0) { execute(); } - RETCODE rc = SQLFetch(hStmt); + RETCODE rc = SQLFetchScroll(hStmt, orientation, offset); switch (rc) { case SQL_SUCCESS: for (Columns::iterator i = columns.begin(); i != columns.end(); i++) { @@ -42,6 +43,18 @@ ODBC::SelectCommand::fetch() case SQL_NO_DATA: return false; default: + { + SQLCHAR sqlstatus[5]; + RETCODE diagrc = SQLGetDiagRec(SQL_HANDLE_STMT, hStmt, 1, sqlstatus, NULL, NULL, 0, NULL); + if (SQL_SUCCEEDED(diagrc)) { + if (!strncmp((const char*)sqlstatus, "01004", 5)) { + for (Columns::iterator i = columns.begin(); i != columns.end(); i++) { + (*i)->resize(hStmt); + } + return fetch(SQL_FETCH_RELATIVE, 0); + } + } + } throw Error(rc, SQL_HANDLE_STMT, hStmt, "%s: SQLFetch", __FUNCTION__); } @@ -87,7 +100,7 @@ ODBC::SelectCommand::execute() case SQL_VARCHAR: case SQL_LONGVARCHAR: { - _Column* s = new _Column(colName, col); + StringColumn* s = new StringColumn(colName, col); s->value.resize(bindSize + 1); s->bind(hStmt, sqlcol, SQL_C_CHAR, &s->value[0], bindSize + 1); columns[col] = s; diff --git a/libodbcpp/selectcommand.h b/libodbcpp/selectcommand.h index 541cd08..c523f25 100644 --- a/libodbcpp/selectcommand.h +++ b/libodbcpp/selectcommand.h @@ -10,7 +10,7 @@ namespace ODBC { public: SelectCommand (const Connection &, const std::string & sql); ~SelectCommand(); - bool fetch(); + bool fetch(SQLSMALLINT orientation = SQL_FETCH_NEXT, SQLLEN offset = 0); const Column & operator[](unsigned int col) const; const Column & operator[](const Glib::ustring &) const; unsigned int columnCount() const; -- cgit v1.2.3