Browse Source

Replace Source/ThirdParty/nanodbc subdirectory with subtree.

Yao Wei Tjong 姚伟忠 10 years ago
parent
commit
c1ae46ec41

+ 0 - 41
Source/ThirdParty/nanodbc/CMakeLists.txt

@@ -1,41 +0,0 @@
-#
-# Copyright (c) 2008-2015 the Urho3D project.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-# Define target name
-set (TARGET_NAME nanodbc)
-
-# Define source files
-define_source_files (GLOB_CPP_PATTERNS src/*.cpp GLOB_H_PATTERNS src/*.h)
-
-# Define dependency libs
-if (ODBC_INCLUDE_DIRS)
-    set (INCLUDE_DIRS ${ODBC_INCLUDE_DIRS})
-endif ()
-if (ODBC_DEFINES)
-    add_definitions (${ODBC_DEFINES})
-endif ()
-
-# Setup target
-setup_library ()
-
-# Install headers for building and using the Urho3D library
-install_header_files (DIRECTORY src/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/nanodbc FILES_MATCHING PATTERN *.h)  # Note: the trailing slash is significant

+ 0 - 19
Source/ThirdParty/nanodbc/LICENSE

@@ -1,19 +0,0 @@
-The MIT License
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 61
Source/ThirdParty/nanodbc/README.md

@@ -1,61 +0,0 @@
-# Synopsis
-
-A very small C++ wrapper for the native C ODBC API. Please see the [online documentation](http://lexicalunit.github.com/nanodbc/) for user information, example usage, propaganda, and detailed source level documentation.
-
-# Versions
-
-| Tag or Branch | Description |
-| --------------| ----------- |
-| `master`      | This branch contains the latest development code, possibly not ready for release yet. |
-| `release`     | This branch is always tagged to the most recent stable release. |
-| `v2.0.0`      | Supports C++11 only. All future development will build upon this version. |
-| `v1.0.0`      | Supports C++03 and optionally C++11. *There is no longer any support for this version.* |
-
-# Building
-
-You don't necessarily need to build nanodbc, as you can just drop the header and implementation files into your project. However I have provided [CMake](http://www.cmake.org/) files which can be used to build a shared library object, or build and run the included unit tests. The CMake files will also support out of source builds.
-
-Unit tests are built using [Boost.Test](http://www.boost.org/doc/libs/release/libs/test/) and therefore require Boost be installed. In order to build the tests you will also need to have either unixODBC or iODBC installed and discoverable by CMake. This is easy on OS X (for example) where you can just use [Homebrew](http://brew.sh/) to install unixODBC with `brew install unixodbc`, or simply use the system provided iODBC if you have OS X prior to 10.9. Also note that you can install Boost via Homebrew as well, which is very convenient!
-
-Finally, the unit tests attempt to connect to a [SQLite](https://www.sqlite.org/) database, so you will have to have that and a SQLite ODBC driver installed. At the time of this writing, there happens to be a nice [SQLite ODBC driver](http://www.ch-werner.de/sqliteodbc/) available from Christian Werner's website. The tests expect to find a data source named `sqlite`. So for example, your `odbcinst.ini` file on OS X might have a section similar to the following.
-
-```
-[sqlite]
-Description             = SQLite ODBC Driver
-Driver                  = /usr/lib/libsqlite3odbc-0.93.dylib
-Setup                   = /usr/lib/libsqlite3odbc-0.93.dylib
-Threading               = 2
-```
-
-## Example Build Process
-
-It's most convenient to create a build directory for an out of source build, but this isn't required. After you've used cmake to generate your Makefiles, you probably only really need to be aware of `make nanodbc` which will build your shared object, or `make check` which will build and run the unit tests. If the unit tests fail, please don't hesitate to report it by creating an issue [with your detailed test log](http://stackoverflow.com/questions/5709914/using-cmake-how-do-i-get-verbose-output-from-ctest)! You may also want to install nanodbc to your system using `make install`.
-
-```shell
-cd path/to/nanodbc/repository
-mkdir build
-cd build
-cmake .. [-DNANODBC_USE_UNICODE=ON]
-make # creates shared library
-make nanodbc # creates shared library
-make tests # only builds the unit tests
-make test # only runs the unit tests
-make check # builds and then runs unit tests
-make install # installs nanodbc.h and shared library
-```
-
-## Future work
-
-### NANODBC_HANDLE_NODATA_BUG
-
-A macro `NANODBC_HANDLE_NODATA_BUG` was introduced to handle drivers like vertica's which return `SQL_NO_DATA` where they probably shouldn't. It would be nice to have the library *just work* instead of having to rely on users compiling with this flag set properly for their driver.
-
-### C++11
-
-Moving forward, nanodbc will target C++11. Since it was originally developed for C++03, it would probably be a good idea to go through it and see what places it can be updated. This shouldn't be too bad because nanodbc has had optional C++11 support for a long time.
-
-### Todo
-
-- More tests for a large variety of drivers. Include performance tests.
-- Clean up `bind_*` family of functions, reduce any duplication.
-- Improve documentation: The main website and API docs should be more responsive.

+ 0 - 1
Source/ThirdParty/nanodbc/VERSION

@@ -1 +0,0 @@
-2.2.2

+ 0 - 3159
Source/ThirdParty/nanodbc/src/nanodbc.cpp

@@ -1,3159 +0,0 @@
-//! \file nanodbc.cpp Implementation details.
-#ifndef DOXYGEN
-
-#include "nanodbc.h"
-
-#include <algorithm>
-#include <cassert>
-#include <clocale>
-#include <codecvt>
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-#include <ctime>
-#include <iomanip>
-#include <map>
-#include <sstream>
-
-#if defined(_MSC_VER) && _MSC_VER <= 1800
-    // silence spurious Visual C++ warnings
-    #pragma warning(disable:4244) // warning about integer conversion issues.
-    #pragma warning(disable:4312) // warning about 64-bit portability issues.
-    #pragma warning(disable:4996) // warning about snprintf() deprecated.
-#endif
-
-#ifdef __APPLE__
-    // silence spurious OS X deprecation warnings
-    #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
-#endif
-
-#ifdef _WIN32
-    // needs to be included above sql.h for windows
-    #define NOMINMAX
-    #include <windows.h>
-#endif
-
-#include <sql.h>
-#include <sqlext.h>
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Unicode Support
-///////////////////////////////////////////////////////////////////////////////////////////////////
-#if defined(_MSC_VER)
-    #ifdef NANODBC_USE_UNICODE
-        #define NANODBC_TEXT(s) L ## s
-        #define NANODBC_SNPRINTF std::swprintf
-        #define NANODBC_STRFTIME std::wcsftime
-        #define NANODBC_STRLEN std::wcslen
-        #define NANADBC_STRNCMP std::wcsncmp
-        #define NANODBC_UNICODE(f) f ## W
-        #define NANODBC_SQLCHAR SQLWCHAR
-    #else
-        #define NANODBC_TEXT(s) s
-        #define NANODBC_SNPRINTF _snprintf
-        #define NANODBC_STRFTIME std::strftime
-        #define NANODBC_STRLEN std::strlen
-        #define NANADBC_STRNCMP std::strncmp
-        #define NANODBC_UNICODE(f) f
-        #define NANODBC_SQLCHAR SQLCHAR
-    #endif
-#else
-    #ifdef NANODBC_USE_UNICODE
-        #define NANODBC_TEXT(s) L ## s
-        #define NANODBC_SNPRINTF std::swprintf
-        #define NANODBC_STRFTIME std::wcsftime
-        #define NANODBC_STRLEN std::wcslen
-        #define NANADBC_STRNCMP std::wcsncmp
-        #define NANODBC_UNICODE(f) f ## W
-        #define NANODBC_SQLCHAR SQLWCHAR
-    #else
-        #define NANODBC_TEXT(s) s
-        #define NANODBC_SNPRINTF std::snprintf
-        #define NANODBC_STRFTIME std::strftime
-        #define NANODBC_STRLEN std::strlen
-        #define NANADBC_STRNCMP std::strncmp
-        #define NANODBC_UNICODE(f) f
-        #define NANODBC_SQLCHAR SQLCHAR
-    #endif
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// ODBC API Interface
-///////////////////////////////////////////////////////////////////////////////////////////////////
-#define NANODBC_STRINGIZE_I(text) #text
-#define NANODBC_STRINGIZE(text) NANODBC_STRINGIZE_I(text)
-
-// By making all calls to ODBC functions through this macro, we can easily get
-// runtime debugging information of which ODBC functions are being called,
-// in what order, and with what parameters by defining NANODBC_ODBC_API_DEBUG.
-#ifdef NANODBC_ODBC_API_DEBUG
-    #include <iostream>
-    #define NANODBC_CALL_RC(FUNC, RC, ...)                                    \
-        do                                                                    \
-        {                                                                     \
-            std::cerr << __FILE__ ":" NANODBC_STRINGIZE(__LINE__) " "         \
-                NANODBC_STRINGIZE(FUNC) "(" #__VA_ARGS__ ")" << std::endl;    \
-            RC = FUNC(__VA_ARGS__);                                           \
-        } while(false)                                                        \
-        /**/
-    #define NANODBC_CALL(FUNC, ...)                                           \
-        do                                                                    \
-        {                                                                     \
-            std::cerr << __FILE__ ":" NANODBC_STRINGIZE(__LINE__) " "         \
-                NANODBC_STRINGIZE(FUNC) "(" #__VA_ARGS__ ")" << std::endl;    \
-            FUNC(__VA_ARGS__);                                                \
-        } while(false)                                                        \
-        /**/
-#else
-    #define NANODBC_CALL_RC(FUNC, RC, ...) RC = FUNC(__VA_ARGS__)
-    #define NANODBC_CALL(FUNC, ...) FUNC(__VA_ARGS__)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Error and Exception Handling
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace
-{
-    #ifdef NANODBC_ODBC_API_DEBUG
-        inline nanodbc::string_type return_code(RETCODE rc)
-        {
-            switch(rc)
-            {
-                case SQL_SUCCESS: return NANODBC_UNICODE("SQL_SUCCESS");
-                case SQL_SUCCESS_WITH_INFO: return NANODBC_UNICODE("SQL_SUCCESS_WITH_INFO");
-                case SQL_ERROR: return NANODBC_UNICODE("SQL_ERROR");
-                case SQL_INVALID_HANDLE: return NANODBC_UNICODE("SQL_INVALID_HANDLE");
-                case SQL_NO_DATA: return NANODBC_UNICODE("SQL_NO_DATA");
-                case SQL_NEED_DATA: return NANODBC_UNICODE("SQL_NEED_DATA");
-                case SQL_STILL_EXECUTING: return NANODBC_UNICODE("SQL_STILL_EXECUTING");
-            }
-            assert(0);
-            return "unknown"; // should never make it here
-        }
-    #endif
-
-    // Easy way to check if a return code signifies success.
-    inline bool success(RETCODE rc)
-    {
-        #ifdef NANODBC_ODBC_API_DEBUG
-            std::cerr << "<-- rc: " << return_code(rc) << " | ";
-        #endif
-        return rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO;
-    }
-
-    // Returns the array size.
-    template<typename T, std::size_t N>
-    inline std::size_t arrlen(T(&)[N])
-    {
-        return N;
-    }
-
-    // Operates like strlen() on a character array.
-    template<typename T, std::size_t N>
-    inline std::size_t strarrlen(T(&a)[N])
-    {
-        const T* s = &a[0];
-        std::size_t i = 0;
-        while(*s++ && i < N) i++;
-        return i;
-    }
-
-    inline void convert(const std::wstring& in, std::string& out)
-    {
-        std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
-        out = conv.to_bytes(in);
-    }
-
-    #ifdef NANODBC_USE_UNICODE
-        inline void convert(const std::string& in, std::wstring& out)
-        {
-            std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
-            out = conv.from_bytes(in);
-        }
-
-        inline void convert(const std::wstring& in, std::wstring & out)
-        {
-            out = in;
-        }
-    #else
-        inline void convert(const std::string& in, std::string& out)
-        {
-            out = in;
-        }
-    #endif
-
-    // Attempts to get the most recent ODBC error as a string.
-    // Always returns std::string, even in unicode mode.
-    inline std::string recent_error(SQLHANDLE handle, SQLSMALLINT handle_type)
-    {
-        nanodbc::string_type result;
-        std::string rvalue;
-        std::vector<NANODBC_SQLCHAR> sql_message(SQL_MAX_MESSAGE_LENGTH);
-        sql_message[0] = '\0';
-
-        SQLINTEGER i = 1;
-        SQLINTEGER native_error = 0;
-        SQLSMALLINT total_bytes;
-        NANODBC_SQLCHAR sql_state[6];
-        RETCODE rc;
-
-        do
-        {
-            NANODBC_CALL_RC(
-                NANODBC_UNICODE(SQLGetDiagRec)
-                , rc
-                , handle_type
-                , handle
-                , i
-                , sql_state
-                , &native_error
-                , 0
-                , 0
-                , &total_bytes);
-
-            if(success(rc) && total_bytes > 0)
-                sql_message.resize(total_bytes + 1);
-
-            NANODBC_CALL_RC(
-                NANODBC_UNICODE(SQLGetDiagRec)
-                , rc
-                , handle_type
-                , handle
-                , i
-                , sql_state
-                , &native_error
-                , sql_message.data()
-                , (SQLSMALLINT)sql_message.size()
-                , &total_bytes);
-
-            if(!success(rc))
-            {
-                convert(result, rvalue);
-                return rvalue;
-            }
-
-            if(!result.empty())
-                result += ' ';
-
-            result += nanodbc::string_type(sql_message.begin(), sql_message.end());
-            i++;
-        } while(rc != SQL_NO_DATA);
-
-        convert(result, rvalue);
-        std::string status(&sql_state[0], &sql_state[arrlen(sql_state)]);
-        status += ": ";
-        status += rvalue;
-
-        // some drivers insert \0 into error messages for unknown reasons
-        using std::replace;
-        replace(status.begin(), status.end(), '\0', ' ');
-
-        return status;
-    }
-} // namespace
-
-namespace nanodbc
-{
-    type_incompatible_error::type_incompatible_error()
-    : std::runtime_error("type incompatible") { }
-
-    const char* type_incompatible_error::what() const noexcept
-    {
-        return std::runtime_error::what();
-    }
-
-    null_access_error::null_access_error()
-    : std::runtime_error("null access") { }
-
-    const char* null_access_error::what() const noexcept
-    {
-        return std::runtime_error::what();
-    }
-
-    index_range_error::index_range_error()
-    : std::runtime_error("index out of range") { }
-
-    const char* index_range_error::what() const noexcept
-    {
-        return std::runtime_error::what();
-    }
-
-    programming_error::programming_error(const std::string& info)
-    : std::runtime_error(info.c_str()) { }
-
-    const char* programming_error::what() const noexcept
-    {
-        return std::runtime_error::what();
-    }
-
-    database_error::database_error(void* handle, short handle_type, const std::string& info)
-    : std::runtime_error(info + recent_error(handle, handle_type)) { }
-
-    const char* database_error::what() const noexcept
-    {
-        return std::runtime_error::what();
-    }
-} // namespace nanodbc
-
-// Throwing exceptions using NANODBC_THROW_DATABASE_ERROR enables file name
-// and line numbers to be inserted into the error message. Useful for debugging.
-#define NANODBC_THROW_DATABASE_ERROR(handle, handle_type)                     \
-    throw nanodbc::database_error(                                            \
-        handle                                                                \
-        , handle_type                                                         \
-        , __FILE__ ":" NANODBC_STRINGIZE(__LINE__) ": ")                      \
-    /**/
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Implementation Details
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace
-{
-    using namespace std; // if int64_t is in std namespace (in c++11)
-
-    // A utility for calculating the ctype from the given type T.
-    // I essentially create a lookup table based on the MSDN ODBC documentation.
-    // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms714556(v=vs.85).aspx
-    template<class T>
-    struct sql_ctype { };
-
-    template<>
-    struct sql_ctype<nanodbc::string_type::value_type>
-    {
-        #ifdef NANODBC_USE_UNICODE
-            static const SQLSMALLINT value = SQL_C_WCHAR;
-        #else
-            static const SQLSMALLINT value = SQL_C_CHAR;
-        #endif
-    };
-
-    template<>
-    struct sql_ctype<short>
-    {
-        static const SQLSMALLINT value = SQL_C_SSHORT;
-    };
-
-    template<>
-    struct sql_ctype<unsigned short>
-    {
-        static const SQLSMALLINT value = SQL_C_USHORT;
-    };
-
-    template<>
-    struct sql_ctype<int32_t>
-    {
-        static const SQLSMALLINT value = SQL_C_SLONG;
-    };
-
-    template<>
-    struct sql_ctype<uint32_t>
-    {
-        static const SQLSMALLINT value = SQL_C_ULONG;
-    };
-
-    template<>
-    struct sql_ctype<int64_t>
-    {
-        static const SQLSMALLINT value = SQL_C_SBIGINT;
-    };
-
-    template<>
-    struct sql_ctype<uint64_t>
-    {
-        static const SQLSMALLINT value = SQL_C_UBIGINT;
-    };
-
-    template<>
-    struct sql_ctype<float>
-    {
-        static const SQLSMALLINT value = SQL_C_FLOAT;
-    };
-
-    template<>
-    struct sql_ctype<double>
-    {
-        static const SQLSMALLINT value = SQL_C_DOUBLE;
-    };
-
-    template<>
-    struct sql_ctype<nanodbc::string_type>
-    {
-        #ifdef NANODBC_USE_UNICODE
-            static const SQLSMALLINT value = SQL_C_WCHAR;
-        #else
-            static const SQLSMALLINT value = SQL_C_CHAR;
-        #endif
-    };
-
-    template<>
-    struct sql_ctype<nanodbc::date>
-    {
-        static const SQLSMALLINT value = SQL_C_DATE;
-    };
-
-    template<>
-    struct sql_ctype<nanodbc::timestamp>
-    {
-        static const SQLSMALLINT value = SQL_C_TIMESTAMP;
-    };
-
-    // Encapsulates resources needed for column binding.
-    class bound_column
-    {
-    public:
-        bound_column(const bound_column& rhs) =delete;
-        bound_column& operator=(bound_column rhs) =delete;
-
-        bound_column()
-        : name_()
-        , column_(0)
-        , sqltype_(0)
-        , sqlsize_(0)
-        , scale_(0)
-        , ctype_(0)
-        , clen_(0)
-        , blob_(false)
-        , rowset_size_(0)
-        , cbdata_(0)
-        , pdata_(0)
-        {
-
-        }
-
-        ~bound_column()
-        {
-            delete[] cbdata_;
-            delete[] pdata_;
-        }
-
-    public:
-        nanodbc::string_type name_;
-        short column_;
-        SQLSMALLINT sqltype_;
-        SQLULEN sqlsize_;
-        SQLSMALLINT scale_;
-        SQLSMALLINT ctype_;
-        SQLULEN clen_;
-        bool blob_;
-        long rowset_size_;
-        nanodbc::null_type* cbdata_;
-        char* pdata_;
-    };
-
-    // Allocates the native ODBC handles.
-    inline void allocate_handle(SQLHENV& env, SQLHDBC& conn)
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLAllocHandle
-            , rc
-            , SQL_HANDLE_ENV
-            , SQL_NULL_HANDLE
-            , &env);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
-
-        try
-        {
-            NANODBC_CALL_RC(
-                SQLSetEnvAttr
-                , rc
-                , env
-                , SQL_ATTR_ODBC_VERSION
-                , (SQLPOINTER)SQL_OV_ODBC3
-                , SQL_IS_UINTEGER);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
-
-            NANODBC_CALL_RC(
-                SQLAllocHandle
-                , rc
-                , SQL_HANDLE_DBC
-                , env
-                , &conn);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
-        }
-        catch(...)
-        {
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_ENV
-                , env);
-            throw;
-        }
-    }
-} // namespace
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Private Implementation: connection
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-class connection::connection_impl
-{
-public:
-    connection_impl(const connection_impl&) =delete;
-    connection_impl& operator=(const connection_impl&) =delete;
-
-    connection_impl()
-    : env_(0)
-    , conn_(0)
-    , connected_(false)
-    , transactions_(0)
-    , rollback_(false)
-    {
-        allocate_handle(env_, conn_);
-    }
-
-    connection_impl(
-        const string_type& dsn
-        , const string_type& user
-        , const string_type& pass
-        , long timeout)
-    : env_(0)
-    , conn_(0)
-    , connected_(false)
-    , transactions_(0)
-    , rollback_(false)
-    {
-        allocate_handle(env_, conn_);
-        try
-        {
-            connect(dsn, user, pass, timeout);
-        }
-        catch(...)
-        {
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_DBC
-                , conn_);
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_ENV
-                , env_);
-            throw;
-        }
-    }
-
-    connection_impl(const string_type& connection_string, long timeout)
-    : env_(0)
-    , conn_(0)
-    , connected_(false)
-    , transactions_(0)
-    , rollback_(false)
-    {
-        allocate_handle(env_, conn_);
-        try
-        {
-            connect(connection_string, timeout);
-        }
-        catch(...)
-        {
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_DBC
-                , conn_);
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_ENV
-                , env_);
-            throw;
-        }
-    }
-
-    ~connection_impl() noexcept
-    {
-        try
-        {
-            disconnect();
-        }
-        catch(...)
-        {
-            // ignore exceptions thrown during disconnect
-        }
-        NANODBC_CALL(
-            SQLFreeHandle
-            , SQL_HANDLE_DBC
-            , conn_);
-        NANODBC_CALL(
-            SQLFreeHandle
-            , SQL_HANDLE_ENV
-            , env_);
-    }
-
-    void connect(
-        const string_type& dsn
-        , const string_type& user
-        , const string_type& pass
-        , long timeout)
-    {
-        disconnect();
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLFreeHandle
-            , rc
-            , SQL_HANDLE_DBC
-            , conn_);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        NANODBC_CALL_RC(
-            SQLAllocHandle
-            , rc
-            , SQL_HANDLE_DBC
-            , env_
-            , &conn_);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
-
-        NANODBC_CALL_RC(
-            SQLSetConnectAttr
-            , rc
-            , conn_
-            , SQL_LOGIN_TIMEOUT
-            , (SQLPOINTER)timeout
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLConnect)
-            , rc
-            , conn_
-            , (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
-            , !user.empty() ? (NANODBC_SQLCHAR*)user.c_str() : 0, SQL_NTS
-            , !pass.empty() ? (NANODBC_SQLCHAR*)pass.c_str() : 0, SQL_NTS);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        connected_ = success(rc);
-    }
-
-    void connect(const string_type& connection_string, long timeout)
-    {
-        disconnect();
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLFreeHandle
-            , rc
-            , SQL_HANDLE_DBC
-            , conn_);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        NANODBC_CALL_RC(
-            SQLAllocHandle
-            , rc
-            , SQL_HANDLE_DBC
-            , env_
-            , &conn_);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
-
-        NANODBC_CALL_RC(
-            SQLSetConnectAttr
-            , rc
-            , conn_
-            , SQL_LOGIN_TIMEOUT
-            , (SQLPOINTER)timeout
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        NANODBC_SQLCHAR dsn[1024];
-        SQLSMALLINT dsn_size = 0;
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLDriverConnect)
-            , rc
-            , conn_
-            , 0
-            , (NANODBC_SQLCHAR*)connection_string.c_str(), SQL_NTS
-            , dsn
-            , sizeof(dsn) / sizeof(NANODBC_SQLCHAR)
-            , &dsn_size
-            , SQL_DRIVER_NOPROMPT);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-
-        connected_ = success(rc);
-    }
-
-    bool connected() const
-    {
-        return connected_;
-    }
-
-    void disconnect()
-    {
-        if(connected())
-        {
-            RETCODE rc;
-            NANODBC_CALL_RC(
-                SQLDisconnect
-                , rc
-                , conn_);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-        }
-        connected_ = false;
-    }
-
-    std::size_t transactions() const
-    {
-        return transactions_;
-    }
-
-    void* native_dbc_handle() const
-    {
-        return conn_;
-    }
-
-    void* native_env_handle() const
-    {
-        return env_;
-    }
-
-    string_type driver_name() const
-    {
-        NANODBC_SQLCHAR name[1024];
-        SQLSMALLINT length;
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLGetInfo)
-            , rc
-            , conn_
-            , SQL_DRIVER_NAME
-            , name
-            , sizeof(name) / sizeof(NANODBC_SQLCHAR)
-            , &length);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
-        return string_type(&name[0], &name[strarrlen(name)]);
-    }
-
-    std::size_t ref_transaction()
-    {
-        return --transactions_;
-    }
-
-    std::size_t unref_transaction()
-    {
-        return ++transactions_;
-    }
-
-    bool rollback() const
-    {
-        return rollback_;
-    }
-
-    void rollback(bool onoff)
-    {
-        rollback_ = onoff;
-    }
-
-private:
-    HENV env_;
-    HDBC conn_;
-    bool connected_;
-    std::size_t transactions_;
-    bool rollback_; // if true, this connection is marked for eventual transaction rollback
-};
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Private Implementation: transaction
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-class transaction::transaction_impl
-{
-public:
-    transaction_impl(const transaction_impl&) =delete;
-    transaction_impl& operator=(const transaction_impl&) =delete;
-
-    transaction_impl(const class connection& conn)
-    : conn_(conn)
-    , committed_(false)
-    {
-        if(conn_.transactions() == 0 && conn_.connected())
-        {
-            RETCODE rc;
-            NANODBC_CALL_RC(
-                SQLSetConnectAttr
-                , rc
-                , conn_.native_dbc_handle()
-                , SQL_ATTR_AUTOCOMMIT
-                , (SQLPOINTER)SQL_AUTOCOMMIT_OFF
-                , SQL_IS_UINTEGER);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
-        }
-        conn_.ref_transaction();
-    }
-
-    ~transaction_impl() noexcept
-    {
-        if(!committed_)
-        {
-            conn_.rollback(true);
-            conn_.unref_transaction();
-        }
-
-        if(conn_.transactions() == 0 && conn_.connected())
-        {
-            if(conn_.rollback())
-            {
-                NANODBC_CALL(
-                    SQLEndTran
-                    , SQL_HANDLE_DBC
-                    , conn_.native_dbc_handle()
-                    , SQL_ROLLBACK);
-                conn_.rollback(false);
-            }
-
-            NANODBC_CALL(
-                SQLSetConnectAttr
-                , conn_.native_dbc_handle()
-                , SQL_ATTR_AUTOCOMMIT
-                , (SQLPOINTER)SQL_AUTOCOMMIT_ON
-                , SQL_IS_UINTEGER);
-        }
-    }
-
-    void commit()
-    {
-        if(committed_)
-            return;
-        committed_ = true;
-        if(conn_.unref_transaction() == 0 && conn_.connected())
-        {
-            RETCODE rc;
-            NANODBC_CALL_RC(
-                SQLEndTran
-                , rc
-                , SQL_HANDLE_DBC
-                , conn_.native_dbc_handle()
-                , SQL_COMMIT);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
-        }
-    }
-
-    void rollback() noexcept
-    {
-        if(committed_)
-            return;
-        conn_.rollback(true);
-    }
-
-    class connection& connection()
-    {
-        return conn_;
-    }
-
-    const class connection& connection() const
-    {
-        return conn_;
-    }
-
-private:
-    class connection conn_;
-    bool committed_;
-};
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Private Implementation: statement
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-class statement::statement_impl
-{
-public:
-    statement_impl(const statement_impl&) =delete;
-    statement_impl& operator=(const statement_impl&) =delete;
-
-    statement_impl()
-    : stmt_(0)
-    , open_(false)
-    , conn_()
-    , bind_len_or_null_()
-    {
-
-    }
-
-    statement_impl(class connection& conn)
-    : stmt_(0)
-    , open_(false)
-    , conn_()
-    , bind_len_or_null_()
-    {
-        open(conn);
-    }
-
-    statement_impl(class connection& conn, const string_type& query, long timeout)
-    : stmt_(0)
-    , open_(false)
-    , conn_()
-    , bind_len_or_null_()
-    {
-        prepare(conn, query, timeout);
-    }
-
-    ~statement_impl() noexcept
-    {
-        if(open() && connected())
-        {
-            NANODBC_CALL(
-                SQLCancel
-                , stmt_);
-            reset_parameters();
-            NANODBC_CALL(
-                SQLFreeHandle
-                , SQL_HANDLE_STMT
-                , stmt_);
-        }
-    }
-
-    void open(class connection& conn)
-    {
-        close();
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLAllocHandle
-            , rc
-            , SQL_HANDLE_STMT
-            , conn.native_dbc_handle()
-            , &stmt_);
-        open_ = success(rc);
-        if(!open_)
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        conn_ = conn;
-    }
-
-    bool open() const
-    {
-        return open_;
-    }
-
-    bool connected() const
-    {
-        return conn_.connected();
-    }
-
-    const class connection& connection() const
-    {
-        return conn_;
-    }
-
-    class connection& connection()
-    {
-        return conn_;
-    }
-
-    void* native_statement_handle() const
-    {
-        return stmt_;
-    }
-
-    void close()
-    {
-        if(open() && connected())
-        {
-            RETCODE rc;
-            NANODBC_CALL_RC(
-                SQLCancel
-                , rc
-                , stmt_);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-            reset_parameters();
-
-            NANODBC_CALL_RC(
-                SQLFreeHandle
-                , rc
-                , SQL_HANDLE_STMT
-                , stmt_);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        }
-
-        open_ = false;
-        stmt_ = 0;
-    }
-
-    void cancel()
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLCancel
-            , rc
-            , stmt_);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-    }
-
-    void prepare(class connection& conn, const string_type& query, long timeout)
-    {
-        open(conn);
-        prepare(query, timeout);
-    }
-
-    void prepare(const string_type& query, long timeout)
-    {
-        if(!open())
-            throw programming_error("statement has no associated open connection");
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLPrepare)
-            , rc
-            , stmt_
-            , (NANODBC_SQLCHAR*)query.c_str()
-            , (SQLINTEGER)query.size());
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        this->timeout(timeout);
-    }
-
-    void timeout(long timeout)
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLSetStmtAttr
-            , rc
-            , stmt_
-            , SQL_ATTR_QUERY_TIMEOUT
-            , (SQLPOINTER)timeout,
-             0);
-
-        // some drivers don't support timeout for statements,
-        // so only raise the error if a non-default timeout was requested.
-        if(!success(rc) && (timeout != 0))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-    }
-
-    result execute_direct(
-        class connection& conn
-        , const string_type& query
-        , long batch_operations
-        , long timeout
-        , statement& statement)
-    {
-        #ifdef NANODBC_HANDLE_NODATA_BUG
-            const RETCODE rc = just_execute_direct(conn, query, batch_operations, timeout, statement);
-            if(rc == SQL_NO_DATA)
-                return result();
-        #else
-            just_execute_direct(conn, query, batch_operations, timeout, statement);
-        #endif
-        return result(statement, batch_operations);
-    }
-
-    RETCODE just_execute_direct(
-        class connection& conn
-        , const string_type& query
-        , long batch_operations
-        , long timeout
-        , statement& statement)
-    {
-        open(conn);
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLSetStmtAttr
-            , rc
-            , stmt_
-            , SQL_ATTR_PARAMSET_SIZE
-            , (SQLPOINTER)batch_operations
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        this->timeout(timeout);
-
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLExecDirect)
-            , rc
-            , stmt_
-            , (NANODBC_SQLCHAR*)query.c_str()
-            , SQL_NTS);
-        if(!success(rc) && rc != SQL_NO_DATA)
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        return rc;
-    }
-
-    result execute(long batch_operations, long timeout, statement& statement)
-    {
-        #ifdef NANODBC_HANDLE_NODATA_BUG
-            const RETCODE rc = just_execute(batch_operations, timeout, statement);
-            if(rc == SQL_NO_DATA)
-                return result();
-        #else
-            just_execute(batch_operations, timeout, statement);
-        #endif
-        return result(statement, batch_operations);
-    }
-
-    RETCODE just_execute(long batch_operations, long timeout, statement& statement)
-    {
-        RETCODE rc;
-
-        if(open())
-        {
-            // The ODBC cursor must be closed before subsequent executions, as described
-            // here http://msdn.microsoft.com/en-us/library/windows/desktop/ms713584%28v=vs.85%29.aspx
-            //
-            // However, we don't necessarily want to call SQLCloseCursor() because that
-            // will cause an invalid cursor state in the case that no cursor is currently open.
-            // A better solution is to use SQLFreeStmt() with the SQL_CLOSE option, which has
-            // the same effect without the undesired limitations.
-            NANODBC_CALL_RC(
-                SQLFreeStmt
-                , rc
-                , stmt_
-                , SQL_CLOSE);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        }
-
-        NANODBC_CALL_RC(
-            SQLSetStmtAttr
-            , rc
-            , stmt_
-            , SQL_ATTR_PARAMSET_SIZE
-            , (SQLPOINTER)batch_operations
-            , 0);
-        if(!success(rc) && rc != SQL_NO_DATA)
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        this->timeout(timeout);
-
-        NANODBC_CALL_RC(
-            SQLExecute
-            , rc
-            , stmt_);
-        if(!success(rc) && rc != SQL_NO_DATA)
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        return rc;
-    }
-
-    result procedure_columns(
-        const string_type& catalog
-        , const string_type& schema
-        , const string_type& procedure
-        , const string_type& column
-        , statement& statement)
-    {
-        if(!open())
-            throw programming_error("statement has no associated open connection");
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            NANODBC_UNICODE(SQLProcedureColumns)
-            , rc
-            , stmt_
-            , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
-            , (catalog.empty() ? 0 : SQL_NTS)
-            , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
-            , (schema.empty() ? 0 : SQL_NTS)
-            , (NANODBC_SQLCHAR*)procedure.c_str()
-            , SQL_NTS
-            , (NANODBC_SQLCHAR*)(column.empty() ? NULL : column.c_str())
-            , (column.empty() ? 0 : SQL_NTS));
-
-        if (!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        return result(statement, 1);
-    }
-
-    long affected_rows() const
-    {
-        SQLLEN rows;
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLRowCount
-            , rc
-            , stmt_
-            , &rows);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        return rows;
-    }
-
-    short columns() const
-    {
-        SQLSMALLINT cols;
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLNumResultCols
-            , rc
-            , stmt_
-            , &cols);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        return cols;
-    }
-
-    void reset_parameters() noexcept
-    {
-        NANODBC_CALL(
-            SQLFreeStmt
-            , stmt_
-            , SQL_RESET_PARAMS);
-    }
-
-    unsigned long parameter_size(short param) const
-    {
-        RETCODE rc;
-        SQLSMALLINT data_type;
-        SQLULEN parameter_size;
-        NANODBC_CALL_RC(
-            SQLDescribeParam
-            , rc
-            , stmt_
-            , param + 1
-            , &data_type
-            , &parameter_size
-            , 0
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-        return parameter_size;
-    }
-
-    static SQLSMALLINT param_type_from_direction(param_direction direction)
-    {
-        switch(direction)
-        {
-            default:
-                assert(false);
-                // fallthrough
-            case PARAM_IN:
-                return SQL_PARAM_INPUT;
-                break;
-            case PARAM_OUT:
-                return SQL_PARAM_OUTPUT;
-                break;
-            case PARAM_INOUT:
-                return SQL_PARAM_INPUT_OUTPUT;
-                break;
-            case PARAM_RETURN:
-                return SQL_PARAM_OUTPUT;
-                break;
-        }
-        // Remove warning C4702 : unreachable code nanodbc.cpp 1284	Nanodbc
-        assert(false);
-    }
-
-    // initializes bind_len_or_null_ and gets information for bind
-    void prepare_bind(
-        short param
-        , std::size_t elements
-        , param_direction direction
-        , SQLSMALLINT& data_type
-        , SQLSMALLINT& param_type
-        , SQLULEN& parameter_size)
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLDescribeParam
-            , rc
-            , stmt_
-            , param + 1
-            , &data_type
-            , &parameter_size
-            , 0
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-
-        param_type = param_type_from_direction(direction);
-
-        if(!bind_len_or_null_.count(param))
-            bind_len_or_null_[param] = std::vector<null_type>();
-        std::vector<null_type>().swap(bind_len_or_null_[param]);
-
-        // ODBC weirdness: this must be at least 8 elements in size
-        const std::size_t indicator_size = elements > 8 ? elements : 8;
-
-        bind_len_or_null_[param].reserve(indicator_size);
-        bind_len_or_null_[param].assign(indicator_size, SQL_NULL_DATA);
-    }
-
-    // calls actual ODBC bind parameter function
-    template<class T>
-    void bind_parameter(
-        short param
-        , const T* data
-        , std::size_t /*elements*/
-        , SQLSMALLINT data_type
-        , SQLSMALLINT param_type
-        , SQLULEN parameter_size)
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLBindParameter
-            , rc
-            , stmt_ // handle
-            , param + 1 // parameter number
-            , param_type // input or output type
-            , sql_ctype<T>::value // value type
-            , data_type // parameter type
-            , parameter_size // column size ignored for many types, but needed for strings
-            , 0 // decimal digits
-            , (SQLPOINTER)data // parameter value
-            , parameter_size // buffer length
-            , bind_len_or_null_[param].data());
-
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-    }
-
-    // handles a single value (possibly a single string value), or multiple non-string values
-    template<class T>
-    void bind(short param, const T* values, std::size_t elements, param_direction direction);
-
-    // handles multiple string values
-    void bind_strings(
-        short param
-        , const string_type::value_type* values
-        , std::size_t /*length*/
-        , std::size_t elements
-        , param_direction direction)
-    {
-        bind(param, values, elements, direction);
-    }
-
-    // handles multiple null values
-    void bind_null(short param, std::size_t elements)
-    {
-        SQLSMALLINT data_type;
-        SQLSMALLINT param_type;
-        SQLULEN parameter_size;
-        prepare_bind(param, elements, PARAM_IN, data_type, param_type, parameter_size);
-
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLBindParameter
-            , rc
-            , stmt_
-            , param + 1
-            , param_type
-            , SQL_C_CHAR
-            , data_type
-            , parameter_size // column size ignored for many types, but needed for strings
-            , 0
-            , (SQLPOINTER)0 // null value
-            , 0 // parameter_size
-            , bind_len_or_null_[param].data());
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-    }
-
-    // comparator for null sentry values
-    template<class T>
-    bool equals(const T& lhs, const T& rhs)
-    {
-        return lhs == rhs;
-    }
-
-    // handles multiple non-string values with a null sentry
-    template<class T>
-    void bind(
-        short param
-        , const T* values
-        , std::size_t elements
-        , const bool* nulls
-        , const T* null_sentry
-        , param_direction direction);
-
-    // handles multiple string values
-    void bind_strings(
-        short param
-        , const string_type::value_type* values
-        , std::size_t length
-        , std::size_t elements
-        , const bool* nulls
-        , const string_type::value_type* null_sentry
-        , param_direction direction);
-
-private:
-    HSTMT stmt_;
-    bool open_;
-    class connection conn_;
-    std::map<short, std::vector<null_type> > bind_len_or_null_;
-};
-
-// Supports code like: query.bind(0, std_string.c_str())
-// In this case, we need to pass NULL to the final parameter of SQLBindParameter().
-template<>
-void statement::statement_impl::bind_parameter<string_type::value_type>(
-    short param
-    , const string_type::value_type* data
-    , std::size_t elements
-    , SQLSMALLINT data_type
-    , SQLSMALLINT param_type
-    , SQLULEN parameter_size)
-{
-    RETCODE rc;
-    NANODBC_CALL_RC(
-        SQLBindParameter
-        , rc
-        , stmt_ // handle
-        , param + 1 // parameter number
-        , param_type // input or output type
-        , sql_ctype<string_type::value_type>::value // value type
-        , data_type // parameter type
-        , parameter_size // column size ignored for many types, but needed for strings
-        , 0 // decimal digits
-        , (SQLPOINTER)data // parameter value
-        , parameter_size // buffer length
-        , (elements <= 1 ? NULL : bind_len_or_null_[param].data()));
-
-    if(!success(rc))
-        NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
-}
-
-template<class T>
-void statement::statement_impl::bind(
-    short param
-    , const T* values
-    , std::size_t elements
-    , param_direction direction)
-{
-    SQLSMALLINT data_type;
-    SQLSMALLINT param_type;
-    SQLULEN parameter_size;
-    prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
-
-    for(std::size_t i = 0; i < elements; ++i)
-        bind_len_or_null_[param][i] = parameter_size;
-
-    bind_parameter(param, values, elements, data_type, param_type, parameter_size);
-}
-
-template<class T>
-void statement::statement_impl::bind(
-    short param
-    , const T* values
-    , std::size_t elements
-    , const bool* nulls
-    , const T* null_sentry
-    , param_direction direction)
-{
-    SQLSMALLINT data_type;
-    SQLSMALLINT param_type;
-    SQLULEN parameter_size;
-    prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
-
-    for(std::size_t i = 0; i < elements; ++i)
-        if((null_sentry && !equals(values[i], *null_sentry)) || (nulls && !nulls[i]))
-            bind_len_or_null_[param][i] = parameter_size;
-
-    bind_parameter(param, values, elements, data_type, param_type, parameter_size);
-}
-
-void statement::statement_impl::bind_strings(
-    short param
-    , const string_type::value_type* values
-    , std::size_t length
-    , std::size_t elements
-    , const bool* nulls
-    , const string_type::value_type* null_sentry
-    , param_direction direction)
-{
-    SQLSMALLINT data_type;
-    SQLSMALLINT param_type;
-    SQLULEN parameter_size;
-    prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
-
-    if(null_sentry)
-    {
-        const string_type rhs(null_sentry);
-        for(std::size_t i = 0; i < elements; ++i)
-        {
-            const string_type lhs(values + i * length, values + (i + 1) * length);
-            if(NANADBC_STRNCMP(lhs.c_str(), rhs.c_str(), length))
-                bind_len_or_null_[param][i] = parameter_size;
-        }
-    }
-    else if(nulls)
-    {
-        for(std::size_t i = 0; i < elements; ++i)
-        {
-            if(!nulls[i])
-                bind_len_or_null_[param][i] = SQL_NTS; // null terminated
-        }
-    }
-
-    bind_parameter(param, values, elements, data_type, param_type, parameter_size);
-}
-
-template<>
-bool statement::statement_impl::equals(const date& lhs, const date& rhs)
-{
-    return lhs.year == rhs.year
-        && lhs.month == rhs.month
-        && lhs.day == rhs.day;
-}
-
-template<>
-bool statement::statement_impl::equals(const timestamp& lhs, const timestamp& rhs)
-{
-    return lhs.year == rhs.year
-        && lhs.month == rhs.month
-        && lhs.day == rhs.day
-        && lhs.hour == rhs.hour
-        && lhs.min == rhs.min
-        && lhs.sec == rhs.sec
-        && lhs.fract == rhs.fract;
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Private Implementation: result
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-class result::result_impl
-{
-public:
-    result_impl(const result_impl&) =delete;
-    result_impl& operator=(const result_impl&) =delete;
-
-    result_impl(statement stmt, long rowset_size)
-    : stmt_(stmt)
-    , rowset_size_(rowset_size)
-    , row_count_(0)
-    , bound_columns_(0)
-    , bound_columns_size_(0)
-    , rowset_position_(0)
-    , bound_columns_by_name_()
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLSetStmtAttr
-            , rc
-            , stmt_.native_statement_handle()
-            , SQL_ATTR_ROW_ARRAY_SIZE
-            , (SQLPOINTER)rowset_size_
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-
-        NANODBC_CALL_RC(
-            SQLSetStmtAttr
-            , rc
-            , stmt_.native_statement_handle()
-            , SQL_ATTR_ROWS_FETCHED_PTR
-            , &row_count_
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-
-        auto_bind();
-    }
-
-    ~result_impl() noexcept
-    {
-        cleanup_bound_columns();
-    }
-
-    void* native_statement_handle() const
-    {
-        return stmt_.native_statement_handle();
-    }
-
-    long rowset_size() const
-    {
-        return rowset_size_;
-    }
-
-    long affected_rows() const
-    {
-        return stmt_.affected_rows();
-    }
-
-    long rows() const noexcept
-    {
-        return row_count_;
-    }
-
-    short columns() const
-    {
-        return stmt_.columns();
-    }
-
-    bool first()
-    {
-        rowset_position_ = 0;
-        return fetch(0, SQL_FETCH_FIRST);
-    }
-
-    bool last()
-    {
-        rowset_position_ = 0;
-        return fetch(0, SQL_FETCH_LAST);
-    }
-
-    bool next()
-    {
-        if(rows() && ++rowset_position_ < rowset_size_)
-            return rowset_position_ < rows();
-        rowset_position_ = 0;
-        return fetch(0, SQL_FETCH_NEXT);
-    }
-
-    bool prior()
-    {
-        if(rows() && --rowset_position_ >= 0)
-            return true;
-        rowset_position_ = 0;
-        return fetch(0, SQL_FETCH_PRIOR);
-    }
-
-    bool move(long row)
-    {
-        rowset_position_ = 0;
-        return fetch(row, SQL_FETCH_ABSOLUTE);
-    }
-
-    bool skip(long rows)
-    {
-        rowset_position_ += rows;
-        if(this->rows() && rowset_position_ < rowset_size_)
-            return rowset_position_ < this->rows();
-        rowset_position_ = 0;
-        return fetch(rows, SQL_FETCH_RELATIVE);
-    }
-
-    unsigned long position() const
-    {
-        SQLULEN pos = 0; // necessary to initialize to 0
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLGetStmtAttr
-            , rc
-            , stmt_.native_statement_handle()
-            , SQL_ATTR_ROW_NUMBER
-            , &pos
-            , SQL_IS_UINTEGER
-            , 0);
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-        return pos - 1 + rowset_position_;
-    }
-
-    bool end() const noexcept
-    {
-        SQLULEN pos = 0; // necessary to initialize to 0
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLGetStmtAttr
-            , rc
-            , stmt_.native_statement_handle()
-            , SQL_ATTR_ROW_NUMBER
-            , &pos
-            , SQL_IS_UINTEGER
-            , 0);
-        return (!success(rc) || rows() < 0 || pos - 1 > static_cast<unsigned long>(rows()));
-    }
-
-    bool is_null(short column) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        bound_column& col = bound_columns_[column];
-        if(rowset_position_ >= rows())
-            throw index_range_error();
-        return col.cbdata_[rowset_position_] == SQL_NULL_DATA;
-    }
-
-    bool is_null(const string_type& column_name) const
-    {
-        const short column = this->column(column_name);
-        return is_null(column);
-    }
-
-    string_type column_name(short column) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        return bound_columns_[column].name_;
-    }
-
-    long column_size(short column) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        bound_column& col = bound_columns_[column];
-        return col.sqlsize_;
-    }
-
-    short column(const string_type& column_name) const
-    {
-        typedef std::map<string_type, bound_column*>::const_iterator iter;
-        iter i = bound_columns_by_name_.find(column_name);
-        if(i == bound_columns_by_name_.end())
-            throw index_range_error();
-        return i->second->column_;
-    }
-
-    int column_datatype(short column) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        bound_column& col = bound_columns_[column];
-        return col.sqltype_;
-    }
-
-    int column_datatype(const string_type& column_name) const
-    {
-        const short column = this->column(column_name);
-        bound_column& col = bound_columns_[column];
-        return col.sqltype_;
-    }
-
-    int column_c_datatype(short column) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        bound_column& col = bound_columns_[column];
-        return col.ctype_;
-    }
-
-    int column_c_datatype(const string_type& column_name) const
-    {
-        const short column = this->column(column_name);
-        bound_column& col = bound_columns_[column];
-        return col.ctype_;
-    }
-
-    bool next_result()
-    {
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLMoreResults
-            , rc
-            , stmt_.native_statement_handle());
-        if(rc == SQL_NO_DATA)
-            return false;
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-        auto_bind();
-        return true;
-    }
-
-    template<class T>
-    void get_ref(short column, T& result) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        if(is_null(column))
-            throw null_access_error();
-        get_ref_impl<T>(column, result);
-    }
-
-    template<class T>
-    void get_ref(short column, const T& fallback, T& result) const
-    {
-        if(column >= bound_columns_size_)
-            throw index_range_error();
-        if(is_null(column))
-        {
-            result = fallback;
-            return;
-        }
-        get_ref_impl<T>(column, result);
-    }
-
-    template<class T>
-    void get_ref(const string_type& column_name, T& result) const
-    {
-        const short column = this->column(column_name);
-        if(is_null(column))
-            throw null_access_error();
-        get_ref_impl<T>(column, result);
-    }
-
-    template<class T>
-    void get_ref(const string_type& column_name, const T& fallback, T& result) const
-    {
-        const short column = this->column(column_name);
-        if(is_null(column))
-        {
-            result = fallback;
-            return;
-        }
-        get_ref_impl<T>(column, result);
-    }
-
-    template<class T>
-    T get(short column) const
-    {
-        T result;
-        get_ref(column, result);
-        return result;
-    }
-
-    template<class T>
-    T get(short column, const T& fallback) const
-    {
-        T result;
-        get_ref(column, fallback, result);
-        return result;
-    }
-
-    template<class T>
-    T get(const string_type& column_name) const
-    {
-        T result;
-        get_ref(column_name, result);
-        return result;
-    }
-
-    template<class T>
-    T get(const string_type& column_name, const T& fallback) const
-    {
-        T result;
-        get_ref(column_name, fallback, result);
-        return result;
-    }
-
-private:
-    template<class T>
-    void get_ref_impl(short column, T& result) const;
-
-    void before_move() noexcept
-    {
-        for(short i = 0; i < bound_columns_size_; ++i)
-        {
-            bound_column& col = bound_columns_[i];
-            for(long j = 0; j < col.rowset_size_; ++j)
-                col.cbdata_[j] = 0;
-            if(col.blob_ && col.pdata_)
-                release_bound_resources(i);
-        }
-    }
-
-    void release_bound_resources(short column) noexcept
-    {
-        assert(column < bound_columns_size_);
-        bound_column& col = bound_columns_[column];
-        delete[] col.pdata_;
-        col.pdata_ = 0;
-        col.clen_ = 0;
-    }
-
-    void cleanup_bound_columns() noexcept
-    {
-        before_move();
-        delete[] bound_columns_;
-        bound_columns_ = NULL;
-        bound_columns_size_ = 0;
-        bound_columns_by_name_.clear();
-    }
-
-    bool fetch(long rows, SQLUSMALLINT orientation)
-    {
-        before_move();
-        RETCODE rc;
-        NANODBC_CALL_RC(
-            SQLFetchScroll
-            , rc
-            , stmt_.native_statement_handle()
-            , orientation
-            , rows);
-        if(rc == SQL_NO_DATA)
-            return false;
-        if(!success(rc))
-            NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-        return true;
-    }
-
-    void auto_bind()
-    {
-        cleanup_bound_columns();
-
-        const short n_columns = columns();
-        if(n_columns < 1)
-            return;
-
-        assert(!bound_columns_);
-        assert(!bound_columns_size_);
-        bound_columns_ = new bound_column[n_columns];
-        bound_columns_size_ = n_columns;
-
-        RETCODE rc;
-        NANODBC_SQLCHAR column_name[1024];
-        SQLSMALLINT sqltype, scale, nullable, len;
-        SQLULEN sqlsize;
-
-        for(SQLSMALLINT i = 0; i < n_columns; ++i)
-        {
-            NANODBC_CALL_RC(
-                NANODBC_UNICODE(SQLDescribeCol)
-                , rc
-                , stmt_.native_statement_handle()
-                , i + 1
-                , (NANODBC_SQLCHAR*)column_name
-                , sizeof(column_name)/sizeof(NANODBC_SQLCHAR)
-                , &len
-                , &sqltype
-                , &sqlsize
-                , &scale
-                , &nullable);
-            if(!success(rc))
-                NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-
-            // Adjust the sqlsize parameter in case of "unlimited" data (varchar(max), nvarchar(max)).
-            bool is_blob = false;
-
-            if (sqlsize == 0)
-            {
-                switch (sqltype)
-                {
-                    case SQL_VARCHAR:
-                    case SQL_WVARCHAR:
-                    {
-                        //// Divide in half, due to sqlsize being 32-bit in Win32 (and 64-bit in x64)
-                        //sqlsize = std::numeric_limits<int32_t>::max() / 2 - 1;
-                        is_blob = true;
-                    }
-
-                }
-            }
-
-            bound_column& col = bound_columns_[i];
-            col.name_ = reinterpret_cast<string_type::value_type*>(column_name);
-            col.column_ = i;
-            col.sqltype_ = sqltype;
-            col.sqlsize_ = sqlsize;
-            col.scale_ = scale;
-            bound_columns_by_name_[col.name_] = &col;
-
-            using namespace std; // if int64_t is in std namespace (in c++11)
-            switch(col.sqltype_)
-            {
-                case SQL_BIT:
-                case SQL_TINYINT:
-                case SQL_SMALLINT:
-                case SQL_INTEGER:
-                    col.ctype_ = SQL_C_LONG;
-                    col.clen_ = sizeof(int32_t);
-                    break;
-                case SQL_BIGINT:
-                    col.ctype_ = SQL_C_SBIGINT;
-                    col.clen_ = sizeof(int64_t);
-                    break;
-                case SQL_DOUBLE:
-                case SQL_FLOAT:
-                case SQL_DECIMAL:
-                case SQL_REAL:
-                case SQL_NUMERIC:
-                    col.ctype_ = SQL_C_DOUBLE;
-                    col.clen_ = sizeof(double);
-                    break;
-                case SQL_DATE:
-                case SQL_TYPE_DATE:
-                    col.ctype_ = SQL_C_DATE;
-                    col.clen_ = sizeof(date);
-                    break;
-                case SQL_TIMESTAMP:
-                case SQL_TYPE_TIMESTAMP:
-                    col.ctype_ = SQL_C_TIMESTAMP;
-                    col.clen_ = sizeof(timestamp);
-                    break;
-                case SQL_CHAR:
-                case SQL_VARCHAR:
-                    col.ctype_ = SQL_C_CHAR;
-                    col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLCHAR);
-                    if (is_blob)
-                    {
-                        col.clen_ = 0;
-                        col.blob_ = true;
-                    }
-                    break;
-                case SQL_WCHAR:
-                case SQL_WVARCHAR:
-                    col.ctype_ = SQL_C_WCHAR;
-                    col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLWCHAR);
-                    if (is_blob)
-                    {
-                        col.clen_ = 0;
-                        col.blob_ = true;
-                    }
-                    break;
-                case SQL_LONGVARCHAR:
-                    col.ctype_ = SQL_C_CHAR;
-                    col.blob_ = true;
-                    col.clen_ = 0;
-                    break;
-                case SQL_BINARY:
-                case SQL_VARBINARY:
-                    col.ctype_ = SQL_C_BINARY;
-                    col.clen_ = col.sqlsize_ + sizeof(NANODBC_SQLCHAR);
-                    break;
-                case SQL_LONGVARBINARY:
-                    col.ctype_ = SQL_C_BINARY;
-                    col.blob_ = true;
-                    col.clen_ = 0;
-                    break;
-                default:
-                    col.ctype_ = sql_ctype<string_type>::value;
-                    col.clen_ = 128;
-                    break;
-            }
-        }
-
-        for(SQLSMALLINT i = 0; i < n_columns; ++i)
-        {
-            bound_column& col = bound_columns_[i];
-            col.cbdata_ = new null_type[rowset_size_];
-            if(col.blob_)
-            {
-                NANODBC_CALL_RC(
-                    SQLBindCol
-                    , rc
-                    , stmt_.native_statement_handle()
-                    , i + 1
-                    , col.ctype_
-                    , 0
-                    , 0
-                    , col.cbdata_);
-                if(!success(rc))
-                    NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-            }
-            else
-            {
-                col.rowset_size_ = rowset_size_;
-                col.pdata_ = new char[rowset_size_ * col.clen_];
-                NANODBC_CALL_RC(
-                    SQLBindCol
-                    , rc
-                    , stmt_.native_statement_handle()
-                    , i + 1         // ColumnNumber
-                    , col.ctype_    // TargetType
-                    , col.pdata_    // TargetValuePtr
-                    , col.clen_     // BufferLength
-                    , col.cbdata_); // StrLen_or_Ind
-                if(!success(rc))
-                    NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
-            }
-        }
-    }
-
-private:
-    statement stmt_;
-    const long rowset_size_;
-    SQLULEN row_count_;
-    bound_column* bound_columns_;
-    short bound_columns_size_;
-    long rowset_position_;
-    std::map<string_type, bound_column*> bound_columns_by_name_;
-};
-
-template<>
-inline void result::result_impl::get_ref_impl<date>(short column, date& result) const
-{
-    bound_column& col = bound_columns_[column];
-    switch(col.ctype_)
-    {
-        case SQL_C_DATE:
-            result = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
-            return;
-        case SQL_C_TIMESTAMP:
-        {
-            timestamp stamp = *( (timestamp*)( col.pdata_ + rowset_position_ * col.clen_ ) );
-            date d = { stamp.year, stamp.month, stamp.day };
-            result = d;
-            return;
-        }
-    }
-    throw type_incompatible_error();
-}
-
-template<>
-inline void result::result_impl::get_ref_impl<timestamp>(short column, timestamp& result) const
-{
-    bound_column& col = bound_columns_[column];
-    switch(col.ctype_)
-    {
-        case SQL_C_DATE:
-        {
-            date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
-            timestamp stamp = { d.year, d.month, d.day, 0, 0, 0, 0 };
-            result = stamp;
-            return;
-        }
-        case SQL_C_TIMESTAMP:
-            result = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
-            return;
-    }
-    throw type_incompatible_error();
-}
-
-template<>
-inline void result::result_impl::get_ref_impl<string_type>(short column, string_type& result) const
-{
-    const bound_column& col = bound_columns_[column];
-    const SQLULEN column_size = col.sqlsize_;
-
-    switch(col.ctype_)
-    {
-        case SQL_C_CHAR:
-        {
-            if(col.blob_)
-            {
-                // Input is always std::string, while output may be std::string or std::wstring
-                std::stringstream ss;
-                char buff[1024] = {0};
-                std::size_t buff_size = sizeof(buff);
-                SQLLEN ValueLenOrInd;
-                SQLRETURN rc;
-                void* handle = native_statement_handle();
-                do
-                {
-                    NANODBC_CALL_RC(
-                        SQLGetData
-                        , rc
-                        , handle            // StatementHandle
-                        , column + 1        // Col_or_Param_Num
-                        , SQL_C_CHAR        // TargetType
-                        , buff              // TargetValuePtr
-                        , buff_size         // BufferLength
-                        , &ValueLenOrInd);  // StrLen_or_IndPtr
-                    if (ValueLenOrInd > 0)
-                        ss << buff;
-                } while(rc > 0);
-                convert(ss.str(), result);
-            }
-            else
-            {
-                const char* s = col.pdata_ + rowset_position_ * col.clen_;
-                const std::string::size_type str_size = std::strlen(s);
-                result.assign(s, s + str_size);
-            }
-            return;
-        }
-
-        case SQL_C_WCHAR:
-        {
-            if (col.blob_)
-            {
-                // Input is always std::wstring, output might be std::string or std::wstring.
-                // Use a string builder to build the output string.
-                std::wstringstream ss;
-                wchar_t buffer[512] = {0};
-                std::size_t buffer_size = sizeof(buffer);
-                SQLLEN ValueLenOrInd;
-                SQLRETURN rc;
-                void* handle = native_statement_handle();
-                do
-                {
-                    NANODBC_CALL_RC(
-                        SQLGetData
-                        , rc
-                        , handle            // StatementHandle
-                        , column + 1        // Col_or_Param_Num
-                        , SQL_C_WCHAR       // TargetType
-                        , buffer            // TargetValuePtr
-                        , buffer_size       // BufferLength
-                        , &ValueLenOrInd);  // StrLen_or_IndPtr
-                    if (ValueLenOrInd > 0)
-                        ss << buffer;
-                } while (rc > 0);
-                convert(ss.str(), result);
-            }
-            else
-            {
-                // Type is unicode in the database, convert if necessary
-                const SQLWCHAR* s = reinterpret_cast<SQLWCHAR*>(col.pdata_ + rowset_position_ * col.clen_);
-                const string_type::size_type str_size = *col.cbdata_ / sizeof(SQLWCHAR);
-                std::wstring temp(s, s + str_size);
-                convert(temp, result);
-            }
-            return;
-        }
-
-        case SQL_C_GUID:
-        case SQL_C_BINARY:
-        {
-            if(col.blob_)
-                throw std::runtime_error("blob not implemented yet");
-            const char* s = col.pdata_ + rowset_position_ * col.clen_;
-            result.assign(s, s + column_size);
-            return;
-        }
-
-        case SQL_C_LONG:
-        {
-           result.resize(column_size);
-           if(NANODBC_SNPRINTF(
-                    const_cast<string_type::value_type*>(result.data())
-                    , column_size
-                    , NANODBC_TEXT("%d")
-                    , *(int32_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
-               throw type_incompatible_error();
-           result.resize(NANODBC_STRLEN(result.c_str()));
-           return;
-        }
-
-        case SQL_C_SBIGINT:
-        {
-            using namespace std; // in case intmax_t is in namespace std
-            result.resize(column_size);
-            if(NANODBC_SNPRINTF(
-                    const_cast<string_type::value_type*>(result.data())
-                    , column_size
-                    , NANODBC_TEXT("%jd")
-                    , (intmax_t) *(int64_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
-                throw type_incompatible_error();
-            result.resize(NANODBC_STRLEN(result.c_str()));
-            return;
-        }
-
-        case SQL_C_FLOAT:
-        {
-            result.resize(column_size);
-            if(NANODBC_SNPRINTF(
-                    const_cast<string_type::value_type*>(result.data())
-                    , column_size
-                    , NANODBC_TEXT("%f")
-                    , *(float*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
-                throw type_incompatible_error();
-            result.resize(NANODBC_STRLEN(result.c_str()));
-            return;
-        }
-
-        case SQL_C_DOUBLE:
-        {
-            result.resize(column_size);
-            if(NANODBC_SNPRINTF(
-                    const_cast<string_type::value_type*>(result.data())
-                    , column_size
-                    , NANODBC_TEXT("%lf")
-                    , *(double*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
-                throw type_incompatible_error();
-            result.resize(NANODBC_STRLEN(result.c_str()));
-            return;
-        }
-
-        case SQL_C_DATE:
-        {
-            date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
-            std::tm st = { 0 };
-            st.tm_year = d.year - 1900;
-            st.tm_mon = d.month - 1;
-            st.tm_mday = d.day;
-            char* old_lc_time = std::setlocale(LC_TIME, NULL);
-            std::setlocale(LC_TIME, "");
-            string_type::value_type date_str[512];
-            NANODBC_STRFTIME(
-                date_str
-                , sizeof(date_str) / sizeof(string_type::value_type)
-                , NANODBC_TEXT("%Y-%m-%d")
-                , &st);
-            std::setlocale(LC_TIME, old_lc_time);
-            result.assign(date_str);
-            return;
-        }
-
-        case SQL_C_TIMESTAMP:
-        {
-            timestamp stamp = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
-            std::tm st = { 0 };
-            st.tm_year = stamp.year - 1900;
-            st.tm_mon = stamp.month - 1;
-            st.tm_mday = stamp.day;
-            st.tm_hour = stamp.hour;
-            st.tm_min = stamp.min;
-            st.tm_sec = stamp.sec;
-            char* old_lc_time = std::setlocale(LC_TIME, NULL);
-            std::setlocale(LC_TIME, "");
-            string_type::value_type date_str[512];
-            NANODBC_STRFTIME(
-                date_str
-                , sizeof(date_str) / sizeof(string_type::value_type)
-                , NANODBC_TEXT("%Y-%m-%d %H:%M:%S %z")
-                , &st);
-            std::setlocale(LC_TIME, old_lc_time);
-           result.assign(date_str);
-           return;
-        }
-    }
-    throw type_incompatible_error();
-}
-
-template<class T>
-void result::result_impl::get_ref_impl(short column, T& result) const
-{
-    bound_column& col = bound_columns_[column];
-    using namespace std; // if int64_t is in std namespace (in c++11)
-    const char* s = col.pdata_ + rowset_position_ * col.clen_;
-    switch(col.ctype_)
-    {
-        case SQL_C_CHAR: result = (T)*(char*)(s); return;
-        case SQL_C_SSHORT: result = (T)*(short*)(s); return;
-        case SQL_C_USHORT: result = (T)*(unsigned short*)(s); return;
-        case SQL_C_LONG: result = (T)*(int32_t*)(s); return;
-        case SQL_C_SLONG: result = (T)*(int32_t*)(s); return;
-        case SQL_C_ULONG: result = (T)*(uint32_t*)(s); return;
-        case SQL_C_FLOAT: result = (T)*(float*)(s); return;
-        case SQL_C_DOUBLE: result = (T)*(double*)(s); return;
-        case SQL_C_SBIGINT: result = (T)*(int64_t*)(s); return;
-        case SQL_C_UBIGINT: result = (T)*(uint64_t*)(s); return;
-    }
-    throw type_incompatible_error();
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Free Functions
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-result execute(connection& conn, const string_type& query, long batch_operations, long timeout)
-{
-    class statement statement;
-    return statement.execute_direct(conn, query, batch_operations, timeout);
-}
-
-void just_execute(connection& conn, const string_type& query, long batch_operations, long timeout) {
-    class statement statement;
-    statement.just_execute_direct(conn, query, batch_operations, timeout);
-}
-
-result execute(statement& stmt, long batch_operations)
-{
-    return stmt.execute(batch_operations);
-}
-
-void just_execute(statement& stmt, long batch_operations)
-{
-    return stmt.just_execute(batch_operations);
-}
-
-result transact(statement& stmt, long batch_operations)
-{
-    class transaction transaction(stmt.connection());
-    result rvalue = stmt.execute(batch_operations);
-    transaction.commit();
-    return rvalue;
-}
-
-void just_transact(statement& stmt, long batch_operations)
-{
-    class transaction transaction(stmt.connection());
-    stmt.just_execute(batch_operations);
-    transaction.commit();
-}
-
-void prepare(statement& stmt, const string_type& query, long timeout)
-{
-    stmt.prepare(stmt.connection(), query, timeout);
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pimpl Forwards: connection
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-connection::connection()
-: impl_(new connection_impl())
-{
-
-}
-
-connection::connection(const connection& rhs)
-: impl_(rhs.impl_)
-{
-
-}
-
-connection& connection::operator=(connection rhs)
-{
-    swap(rhs);
-    return *this;
-}
-
-void connection::swap(connection& rhs) noexcept
-{
-    using std::swap;
-    swap(impl_, rhs.impl_);
-}
-
-connection::connection(
-    const string_type& dsn
-    , const string_type& user
-    , const string_type& pass
-    , long timeout)
-: impl_(new connection_impl(dsn, user, pass, timeout))
-{
-
-}
-
-connection::connection(const string_type& connection_string, long timeout)
-: impl_(new connection_impl(connection_string, timeout))
-{
-
-}
-
-connection::~connection() noexcept
-{
-
-}
-
-void connection::connect(
-    const string_type& dsn
-    , const string_type& user
-    , const string_type& pass
-    , long timeout)
-{
-    impl_->connect(dsn, user, pass, timeout);
-}
-
-void connection::connect(const string_type& connection_string, long timeout)
-{
-    impl_->connect(connection_string, timeout);
-}
-
-bool connection::connected() const
-{
-    return impl_->connected();
-}
-
-void connection::disconnect()
-{
-    impl_->disconnect();
-}
-
-std::size_t connection::transactions() const
-{
-    return impl_->transactions();
-}
-
-void* connection::native_dbc_handle() const
-{
-    return impl_->native_dbc_handle();
-}
-
-void* connection::native_env_handle() const
-{
-    return impl_->native_env_handle();
-}
-
-string_type connection::driver_name() const
-{
-    return impl_->driver_name();
-}
-
-std::size_t connection::ref_transaction()
-{
-    return impl_->ref_transaction();
-}
-
-std::size_t connection::unref_transaction()
-{
-    return impl_->unref_transaction();
-}
-
-bool connection::rollback() const
-{
-    return impl_->rollback();
-}
-
-void connection::rollback(bool onoff)
-{
-    impl_->rollback(onoff);
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pimpl Forwards: transaction
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-transaction::transaction(const class connection& conn)
-: impl_(new transaction_impl(conn))
-{
-
-}
-
-transaction::transaction(const transaction& rhs)
-: impl_(rhs.impl_)
-{
-
-}
-
-transaction& transaction::operator=(transaction rhs)
-{
-    swap(rhs);
-    return *this;
-}
-
-void transaction::swap(transaction& rhs) noexcept
-{
-    using std::swap;
-    swap(impl_, rhs.impl_);
-}
-
-transaction::~transaction() noexcept
-{
-
-}
-
-void transaction::commit()
-{
-    impl_->commit();
-}
-
-void transaction::rollback() noexcept
-{
-    impl_->rollback();
-}
-
-class connection& transaction::connection()
-{
-    return impl_->connection();
-}
-
-const class connection& transaction::connection() const
-{
-    return impl_->connection();
-}
-
-transaction::operator class connection&()
-{
-    return impl_->connection();
-}
-
-transaction::operator const class connection&() const
-{
-    return impl_->connection();
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pimpl Forwards: statement
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-statement::statement()
-: impl_(new statement_impl())
-{
-
-}
-
-statement::statement(class connection& conn)
-: impl_(new statement_impl(conn))
-{
-
-}
-
-statement::statement(class connection& conn, const string_type& query, long timeout)
-: impl_(new statement_impl(conn, query, timeout))
-{
-
-}
-
-statement::statement(const statement& rhs)
-: impl_(rhs.impl_)
-{
-
-}
-
-statement& statement::operator=(statement rhs)
-{
-    swap(rhs);
-    return *this;
-}
-
-void statement::swap(statement& rhs) noexcept
-{
-    using std::swap;
-    swap(impl_, rhs.impl_);
-}
-
-statement::~statement() noexcept
-{
-
-}
-
-void statement::open(class connection& conn)
-{
-    impl_->open(conn);
-}
-
-bool statement::open() const
-{
-    return impl_->open();
-}
-
-bool statement::connected() const
-{
-    return impl_->connected();
-}
-
-const class connection& statement::connection() const
-{
-    return impl_->connection();
-}
-
-class connection& statement::connection()
-{
-    return impl_->connection();
-}
-
-void* statement::native_statement_handle() const
-{
-    return impl_->native_statement_handle();
-}
-
-void statement::close()
-{
-    impl_->close();
-}
-
-void statement::cancel()
-{
-    impl_->cancel();
-}
-
-void statement::prepare(class connection& conn, const string_type& query, long timeout)
-{
-    impl_->prepare(conn, query, timeout);
-}
-
-void statement::prepare(const string_type& query, long timeout)
-{
-    impl_->prepare(query, timeout);
-}
-
-void statement::timeout(long timeout)
-{
-    impl_->timeout(timeout);
-}
-
-result statement::execute_direct(
-    class connection& conn
-    , const string_type& query
-    , long batch_operations
-    , long timeout)
-{
-    return impl_->execute_direct(conn, query, batch_operations, timeout, *this);
-}
-
-void statement::just_execute_direct(
-    class connection& conn
-    , const string_type& query
-    , long batch_operations
-    , long timeout)
-{
-    impl_->just_execute_direct(conn, query, batch_operations, timeout, *this);
-}
-
-result statement::execute(long batch_operations, long timeout)
-{
-    return impl_->execute(batch_operations, timeout, *this);
-}
-
-void statement::just_execute(long batch_operations, long timeout)
-{
-    impl_->just_execute(batch_operations, timeout, *this);
-}
-
-result statement::procedure_columns(
-    const string_type& catalog
-    , const string_type& schema
-    , const string_type& procedure
-    , const string_type& column)
-{
-    return impl_->procedure_columns(catalog, schema, procedure, column, *this);
-}
-
-long statement::affected_rows() const
-{
-    return impl_->affected_rows();
-}
-
-short statement::columns() const
-{
-    return impl_->columns();
-}
-
-void statement::reset_parameters() noexcept
-{
-    impl_->reset_parameters();
-}
-
-unsigned long statement::parameter_size(short param) const
-{
-    return impl_->parameter_size(param);
-}
-
-// We need to instantiate each form of bind() for each of our supported data types.
-#define NANODBC_INSTANTIATE_BINDS(type)                                                                                 \
-    template void statement::bind(short, const type*, param_direction); /* 1-ary */                                     \
-    template void statement::bind(short, const type*, std::size_t, param_direction); /* n-ary */                        \
-    template void statement::bind(short, const type*, std::size_t, const type*, param_direction); /* n-ary, sentry */   \
-    template void statement::bind(short, const type*, std::size_t, const bool*, param_direction) /* n-ary, flags */     \
-    /**/
-
-// The following are the only supported instantiations of statement::bind().
-NANODBC_INSTANTIATE_BINDS(string_type::value_type);
-NANODBC_INSTANTIATE_BINDS(short);
-NANODBC_INSTANTIATE_BINDS(unsigned short);
-NANODBC_INSTANTIATE_BINDS(int32_t);
-NANODBC_INSTANTIATE_BINDS(uint32_t);
-NANODBC_INSTANTIATE_BINDS(int64_t);
-NANODBC_INSTANTIATE_BINDS(uint64_t);
-NANODBC_INSTANTIATE_BINDS(float);
-NANODBC_INSTANTIATE_BINDS(double);
-NANODBC_INSTANTIATE_BINDS(date);
-NANODBC_INSTANTIATE_BINDS(timestamp);
-
-#undef NANODBC_INSTANTIATE_BINDS
-
-template<class T>
-void statement::bind(short param, const T* value, param_direction direction)
-{
-    impl_->bind(param, value, 1, direction);
-}
-
-template<class T>
-void statement::bind(short param, const T* values, std::size_t elements, param_direction direction)
-{
-    impl_->bind(param, values, elements, direction);
-}
-
-template<class T>
-void statement::bind(
-    short param
-    , const T* values
-    , std::size_t elements
-    , const T* null_sentry
-    , param_direction direction)
-{
-    impl_->bind(param, values, elements, 0, null_sentry, direction);
-}
-
-template<class T>
-void statement::bind(
-    short param
-    , const T* values
-    , std::size_t elements
-    , const bool* nulls
-    , param_direction direction)
-{
-    impl_->bind(param, values, elements, nulls, (T*)0, direction);
-}
-
-void statement::bind_strings(
-    short param
-    , const string_type::value_type* values
-    , std::size_t length
-    , std::size_t elements
-    , param_direction direction)
-{
-    impl_->bind_strings(param, values, length, elements, direction);
-}
-
-void statement::bind_strings(
-    short param
-    , const string_type::value_type* values
-    , std::size_t length
-    , std::size_t elements
-    , const string_type::value_type* null_sentry
-    , param_direction direction)
-{
-    impl_->bind_strings(param, values, length, elements, (bool*)0, null_sentry, direction);
-}
-
-void statement::bind_strings(
-    short param
-    , const string_type::value_type* values
-    , std::size_t length
-    , std::size_t elements
-    , const bool* nulls
-    , param_direction direction)
-{
-    impl_->bind_strings(
-        param
-        , values
-        , length
-        , elements
-        , nulls
-        , (string_type::value_type*)0
-        , direction);
-}
-
-void statement::bind_null(short param, std::size_t elements)
-{
-    impl_->bind_null(param, elements);
-}
-
-} // namespace nanodbc
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Pimpl Forwards: result
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-namespace nanodbc
-{
-
-result::result()
-: impl_()
-{
-
-}
-
-result::~result() noexcept
-{
-
-}
-
-result::result(statement stmt, long rowset_size)
-: impl_(new result_impl(stmt, rowset_size))
-{
-
-}
-
-result::result(const result& rhs)
-: impl_(rhs.impl_)
-{
-
-}
-
-result& result::operator=(result rhs)
-{
-    swap(rhs);
-    return *this;
-}
-
-void result::swap(result& rhs) noexcept
-{
-    using std::swap;
-    swap(impl_, rhs.impl_);
-}
-
-void* result::native_statement_handle() const
-{
-    return impl_->native_statement_handle();
-}
-
-long result::rowset_size() const noexcept
-{
-    return impl_->rowset_size();
-}
-
-long result::affected_rows() const
-{
-    return impl_->affected_rows();
-}
-
-long result::rows() const noexcept
-{
-    return impl_->rows();
-}
-
-short result::columns() const
-{
-    return impl_->columns();
-}
-
-bool result::first()
-{
-    return impl_->first();
-}
-
-bool result::last()
-{
-    return impl_->last();
-}
-
-bool result::next()
-{
-    return impl_->next();
-}
-
-bool result::prior()
-{
-    return impl_->prior();
-}
-
-bool result::move(long row)
-{
-    return impl_->move(row);
-}
-
-bool result::skip(long rows)
-{
-    return impl_->skip(rows);
-}
-
-unsigned long result::position() const
-{
-    return impl_->position();
-}
-
-bool result::end() const noexcept
-{
-    return impl_->end();
-}
-
-bool result::is_null(short column) const
-{
-    return impl_->is_null(column);
-}
-
-bool result::is_null(const string_type& column_name) const
-{
-    return impl_->is_null(column_name);
-}
-
-string_type result::column_name(short column) const
-{
-    return impl_->column_name(column);
-}
-
-long result::column_size(short column) const
-{
-    return impl_->column_size(column);
-}
-
-short result::column(const string_type& column_name) const
-{
-    return impl_->column(column_name);
-}
-
-int result::column_datatype(short column) const
-{
-    return impl_->column_datatype(column);
-}
-
-int result::column_datatype(const string_type& column_name) const
-{
-    return impl_->column_datatype(column_name);
-}
-
-int result::column_c_datatype(short column) const
-{
-    return impl_->column_c_datatype(column);
-}
-
-int result::column_c_datatype(const string_type& column_name) const
-{
-    return impl_->column_c_datatype(column_name);
-}
-
-bool result::next_result()
-{
-    return impl_->next_result();
-}
-
-template<class T>
-void result::get_ref(short column, T& result) const
-{
-    return impl_->get_ref<T>(column, result);
-}
-
-template<class T>
-void result::get_ref(short column, const T& fallback, T& result) const
-{
-    return impl_->get_ref<T>(column, fallback, result);
-}
-
-template<class T>
-void result::get_ref(const string_type& column_name, T& result) const
-{
-    return impl_->get_ref<T>(column_name, result);
-}
-
-template<class T>
-void result::get_ref(const string_type& column_name, const T& fallback, T& result) const
-{
-    return impl_->get_ref<T>(column_name, fallback, result);
-}
-
-template<class T>
-T result::get(short column) const
-{
-    return impl_->get<T>(column);
-}
-
-template<class T>
-T result::get(short column, const T& fallback) const
-{
-    return impl_->get<T>(column, fallback);
-}
-
-template<class T>
-T result::get(const string_type& column_name) const
-{
-    return impl_->get<T>(column_name);
-}
-
-template<class T>
-T result::get(const string_type& column_name, const T& fallback) const
-{
-    return impl_->get<T>(column_name, fallback);
-}
-
-result::operator bool() const
-{
-    return static_cast<bool>(impl_);
-}
-
-// The following are the only supported instantiations of result::get_ref().
-template void result::get_ref(short, string_type::value_type&) const;
-template void result::get_ref(short, short&) const;
-template void result::get_ref(short, unsigned short&) const;
-template void result::get_ref(short, int32_t&) const;
-template void result::get_ref(short, uint32_t&) const;
-template void result::get_ref(short, int64_t&) const;
-template void result::get_ref(short, uint64_t&) const;
-template void result::get_ref(short, float&) const;
-template void result::get_ref(short, double&) const;
-template void result::get_ref(short, string_type&) const;
-template void result::get_ref(short, date&) const;
-template void result::get_ref(short, timestamp&) const;
-
-template void result::get_ref(const string_type&, string_type::value_type&) const;
-template void result::get_ref(const string_type&, short&) const;
-template void result::get_ref(const string_type&, unsigned short&) const;
-template void result::get_ref(const string_type&, int32_t&) const;
-template void result::get_ref(const string_type&, uint32_t&) const;
-template void result::get_ref(const string_type&, int64_t&) const;
-template void result::get_ref(const string_type&, uint64_t&) const;
-template void result::get_ref(const string_type&, float&) const;
-template void result::get_ref(const string_type&, double&) const;
-template void result::get_ref(const string_type&, string_type&) const;
-template void result::get_ref(const string_type&, date&) const;
-template void result::get_ref(const string_type&, timestamp&) const;
-
-// The following are the only supported instantiations of result::get_ref() with fallback.
-template void result::get_ref(short, const string_type::value_type&, string_type::value_type&) const;
-template void result::get_ref(short, const short&, short&) const;
-template void result::get_ref(short, const unsigned short&, unsigned short&) const;
-template void result::get_ref(short, const int32_t&, int32_t&) const;
-template void result::get_ref(short, const uint32_t&, uint32_t&) const;
-template void result::get_ref(short, const int64_t&, int64_t&) const;
-template void result::get_ref(short, const uint64_t&, uint64_t&) const;
-template void result::get_ref(short, const float&, float&) const;
-template void result::get_ref(short, const double&, double&) const;
-template void result::get_ref(short, const string_type&, string_type&) const;
-template void result::get_ref(short, const date&, date&) const;
-template void result::get_ref(short, const timestamp&, timestamp&) const;
-
-template void result::get_ref(const string_type&, const string_type::value_type&, string_type::value_type&) const;
-template void result::get_ref(const string_type&, const short&, short&) const;
-template void result::get_ref(const string_type&, const unsigned short&, unsigned short&) const;
-template void result::get_ref(const string_type&, const int32_t&, int32_t&) const;
-template void result::get_ref(const string_type&, const uint32_t&, uint32_t&) const;
-template void result::get_ref(const string_type&, const int64_t&, int64_t&) const;
-template void result::get_ref(const string_type&, const uint64_t&, uint64_t&) const;
-template void result::get_ref(const string_type&, const float&, float&) const;
-template void result::get_ref(const string_type&, const double&, double&) const;
-template void result::get_ref(const string_type&, const string_type&, string_type&) const;
-template void result::get_ref(const string_type&, const date&, date&) const;
-template void result::get_ref(const string_type&, const timestamp&, timestamp&) const;
-
-// The following are the only supported instantiations of result::get().
-template string_type::value_type result::get(short) const;
-template short result::get(short) const;
-template unsigned short result::get(short) const;
-template int32_t result::get(short) const;
-template uint32_t result::get(short) const;
-template int64_t result::get(short) const;
-template uint64_t result::get(short) const;
-template float result::get(short) const;
-template double result::get(short) const;
-template string_type result::get(short) const;
-template date result::get(short) const;
-template timestamp result::get(short) const;
-
-template string_type::value_type result::get(const string_type&) const;
-template short result::get(const string_type&) const;
-template unsigned short result::get(const string_type&) const;
-template int32_t result::get(const string_type&) const;
-template uint32_t result::get(const string_type&) const;
-template int64_t result::get(const string_type&) const;
-template uint64_t result::get(const string_type&) const;
-template float result::get(const string_type&) const;
-template double result::get(const string_type&) const;
-template string_type result::get(const string_type&) const;
-template date result::get(const string_type&) const;
-template timestamp result::get(const string_type&) const;
-
-// The following are the only supported instantiations of result::get() with fallback.
-template string_type::value_type result::get(short, const string_type::value_type&) const;
-template short result::get(short, const short&) const;
-template unsigned short result::get(short, const unsigned short&) const;
-template int32_t result::get(short, const int32_t&) const;
-template uint32_t result::get(short, const uint32_t&) const;
-template int64_t result::get(short, const int64_t&) const;
-template uint64_t result::get(short, const uint64_t&) const;
-template float result::get(short, const float&) const;
-template double result::get(short, const double&) const;
-template string_type result::get(short, const string_type&) const;
-template date result::get(short, const date&) const;
-template timestamp result::get(short, const timestamp&) const;
-
-template string_type::value_type result::get(const string_type&, const string_type::value_type&) const;
-template short result::get(const string_type&, const short&) const;
-template unsigned short result::get(const string_type&, const unsigned short&) const;
-template int32_t result::get(const string_type&, const int32_t&) const;
-template uint32_t result::get(const string_type&, const uint32_t&) const;
-template int64_t result::get(const string_type&, const int64_t&) const;
-template uint64_t result::get(const string_type&, const uint64_t&) const;
-template float result::get(const string_type&, const float&) const;
-template double result::get(const string_type&, const double&) const;
-template string_type result::get(const string_type&, const string_type&) const;
-template date result::get(const string_type&, const date&) const;
-template timestamp result::get(const string_type&, const timestamp&) const;
-
-} // namespace nanodbc
-
-#undef NANODBC_THROW_DATABASE_ERROR
-#undef NANODBC_STRINGIZE
-#undef NANODBC_STRINGIZE_I
-#undef NANODBC_CALL_RC
-#undef NANODBC_CALL
-
-#endif // DOXYGEN

+ 0 - 979
Source/ThirdParty/nanodbc/src/nanodbc.h

@@ -1,979 +0,0 @@
-//! \file nanodbc.h The entirety of nanodbc can be found within this file and nanodbc.cpp.
-
-//! \mainpage
-//!
-//! \section synopsis Synopsis
-//! This library provides a wrapper API for the native ODBC API. It aims to do everything ODBC does, but with a \b much nicer interface.
-//! Anything it doesn't (yet) do can be done by retrieving the native ODBC handles and dropping down to straight ODBC C API code.
-//! For more propaganda, please see the <a href="http://lexicalunit.github.com/nanodbc/">project homepage</a>.
-//!
-//! \section toc Table of Contents
-//! - \ref license "License"
-//! - \ref credits "Credits"
-//! - Source level documentation:
-//!     - \ref nanodbc "nanodbc namespace"
-//!     - \ref exceptions
-//!     - \ref utility
-//!     - \ref mainc
-//!     - \ref mainf
-//!     - \ref binding
-//!     - \ref bind_multi
-//!     - \ref bind_strings
-//!
-//! \section license License
-//! <div class="license">
-//! Copyright (C) 2013 lexicalunit <[email protected]>
-//!
-//! The MIT License
-//!
-//! Permission is hereby granted, free of charge, to any person obtaining a copy
-//! of this software and associated documentation files (the "Software"), to deal
-//! in the Software without restriction, including without limitation the rights
-//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-//! copies of the Software, and to permit persons to whom the Software is
-//! furnished to do so, subject to the following conditions:
-//!
-//! The above copyright notice and this permission notice shall be included in
-//! all copies or substantial portions of the Software.
-//!
-//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-//! THE SOFTWARE.
-//! </div>
-//!
-//! \section credits Credits
-//! <div class="license">
-//! Much of the code in this file was originally derived from TinyODBC.
-//! TinyODBC is hosted at http://code.google.com/p/tiodbc/
-//! Copyright (C) 2008 SqUe [email protected]
-//! License: The MIT License
-//!
-//! The idea for using RAII for transactions was inspired by SimpleDB: C++ ODBC database API,
-//! however the code in nanodbc is original and not derived from SimpleDB. Therefore
-//! the LGPL license under which SimpleDB is distributed does NOT apply to nanodbc.
-//! SimpleDB is hosted at http://simpledb.sourceforge.net
-//! Copyright (C) 2006 Eminence Technology Pty Ltd
-//! Copyright (C) 2008-2010,2012 Russell Kliese [email protected]
-//! License: GNU Lesser General Public version 2.1
-//!
-//! Some improvements and features are based on The Python ODBC Library.
-//! The Python ODBC Library is hosted at http://code.google.com/p/pyodbc/
-//! License: The MIT License
-//!
-//! Implementation of column binding inspired by Nick E. Geht's source code posted to on CodeGuru.
-//! GSODBC hosted at http://www.codeguru.com/mfc_database/gsodbc.html
-//! Copyright (C) 2002 Nick E. Geht
-//! License: Perpetual license to reproduce, distribute, adapt, perform, display, and sublicense.
-//! See http://www.codeguru.com/submission-guidelines.php for details.
-//! </div>
-
-#ifndef NANODBC_H
-#define NANODBC_H
-
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-//! \brief The entirety of nanodbc can be found within this one namespace.
-//! \note This library does not make any exception safety guarantees, but should work just fine with a threading enabled ODBC driver. If you want to use nanodbc objects in threads I recommend each thread keep their own connection to the database. Otherwise you must synchronize any access to nanodbc objects.
-namespace nanodbc
-{
-
-// You must explicitly request Unicode support by defining NANODBC_USE_UNICODE at compile time.
-#ifndef DOXYGEN
-    #ifdef NANODBC_USE_UNICODE
-        typedef std::wstring string_type;
-    #else
-        typedef std::string string_type;
-    #endif // NANODBC_USE_UNICODE
-
-    #if defined(_WIN64)
-        // LLP64 machine, Windows
-        typedef std::int64_t null_type;
-    #elif !defined(_WIN64) && defined(__LP64__)
-        // LP64 machine, OS X or Linux
-        typedef long null_type;
-    #else
-        // 32-bit machine
-        typedef long null_type;
-    #endif
-#else
-    //! string_type will be std::wstring if NANODBC_USE_UNICODE is defined, otherwise std::string.
-    typedef unspecified-type string_type;
-    //! null_type will be int64_t for 64-bit compilations, otherwise long.
-    typedef unspecified-type null_type;
-#endif // DOXYGEN
-
-//! \addtogroup exceptions Exception types
-//! \brief Possible error conditions.
-//!
-//! Specific errors such as type_incompatible_error, null_access_error, and index_range_error can arise
-//! from improper use of the nanodbc library. The general database_error is for all other situations
-//! in which the ODBC driver or C API reports an error condition. The explanatory string for database_error
-//! will, if possible, contain a diagnostic message obtained from SQLGetDiagRec().
-//! @{
-
-//! \brief Type incompatible.
-//! \see exceptions
-class type_incompatible_error : public std::runtime_error
-{
-public:
-    type_incompatible_error();
-    const char* what() const throw();
-};
-
-//! \brief Accessed null data.
-//! \see exceptions
-class null_access_error : public std::runtime_error
-{
-public:
-    null_access_error();
-    const char* what() const throw();
-};
-
-//! \brief Index out of range.
-//! \see exceptions
-class index_range_error : public std::runtime_error
-{
-public:
-    index_range_error();
-    const char* what() const throw();
-};
-
-//! \brief Programming logic error.
-//! \see exceptions
-class programming_error : public std::runtime_error
-{
-public:
-    explicit programming_error(const std::string& info);
-    const char* what() const throw();
-};
-
-//! \brief General database error.
-//! \see exceptions
-class database_error : public std::runtime_error
-{
-public:
-    //! \brief Creates a runtime_error with a message describing the last ODBC error generated for the given handle and handle_type.
-    //! \param handle The native ODBC statement or connection handle.
-    //! \param handle_type The native ODBC handle type code for the given handle.
-    //! \param info Additional information that will be appended to the beginning of the error message.
-    database_error(void* handle, short handle_type, const std::string& info = "");
-    const char* what() const throw();
-};
-
-//! @}
-
-//! \addtogroup utility Utilities
-//! \brief Additional nanodbc utility classes and functions.
-//!
-//! \{
-
-//! \brief A type for representing date data.
-struct date
-{
-    std::int16_t year; //!< Year [0-inf).
-    std::int16_t month; //!< Month of the year [1-12].
-    std::int16_t day; //!< Day of the month [1-31].
-};
-
-//! \brief A type for representing timestamp data.
-struct timestamp
-{
-    std::int16_t year;   //!< Year [0-inf).
-    std::int16_t month;  //!< Month of the year [1-12].
-    std::int16_t day;    //!< Day of the month [1-31].
-    std::int16_t hour;   //!< Hours since midnight [0-23].
-    std::int16_t min;    //!< Minutes after the hour [0-59].
-    std::int16_t sec;    //!< Seconds after the minute.
-    std::int32_t fract;  //!< Fractional seconds.
-};
-
-//! \}
-
-//! \addtogroup mainc Main classes
-//! \brief Main nanodbc classes.
-//!
-//! @{
-
-//! \brief A resource for managing transaction commits and rollbacks.
-//!
-//! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-class transaction
-{
-public:
-    //! \brief Begin a transaction on the given connection object.
-    //! \post Operations that modify the database must now be committed before taking effect.
-    //! \throws database_error
-    explicit transaction(const class connection& conn);
-
-    //! Copy constructor.
-    transaction(const transaction& rhs);
-
-    //! Assignment.
-    transaction& operator=(transaction rhs);
-
-    //! Member swap.
-    void swap(transaction& rhs) throw();
-
-    //! \brief If this transaction has not been committed, will will rollback any modifying operations.
-    ~transaction() throw();
-
-    //! \brief Marks this transaction for commit.
-    //! \throws database_error
-    void commit();
-
-    //! \brief Marks this transaction for rollback.
-    void rollback() throw();
-
-    //! Returns the connection object.
-    class connection& connection();
-
-    //! Returns the connection object.
-    const class connection& connection() const;
-
-    //! Returns the connection object.
-    operator class connection&();
-
-    //! Returns the connection object.
-    operator const class connection&() const;
-
-private:
-    class transaction_impl;
-    friend class nanodbc::connection;
-
-private:
-    std::shared_ptr<transaction_impl> impl_;
-};
-
-//! \brief Represents a statement on the database.
-class statement
-{
-public:
-    //! \brief Provides support for retrieving output/return parameters.
-    //! \see binding
-    enum param_direction
-    {
-        PARAM_IN //!< Binding an input parameter.
-        , PARAM_OUT //!< Binding an output parameter.
-        , PARAM_INOUT //!< Binding an input/output parameter.
-        , PARAM_RETURN //!< Binding a return parameter.
-    };
-
-public:
-    //! \brief Creates a new un-prepared statement.
-    //! \see execute(), just_execute(), execute_direct(), just_execute_direct(), open(), prepare()
-    statement();
-
-    //! \brief Constructs a statement object and associates it to the given connection.
-    //! \param conn The connection to use.
-    //! \see open(), prepare()
-    explicit statement(class connection& conn);
-
-    //! \brief Constructs and prepares a statement using the given connection and query.
-    //! \param conn The connection to use.
-    //! \param query The SQL query statement.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \see execute(), just_execute(), execute_direct(), just_execute_direct(), open(), prepare()
-    statement(class connection& conn, const string_type& query, long timeout = 0);
-
-    //! Copy constructor.
-    statement(const statement& rhs);
-
-    //! Assignment.
-    statement& operator=(statement rhs);
-
-    //! Member swap.
-    void swap(statement& rhs) throw();
-
-    //! \brief Closes the statement.
-    //! \see close()
-    ~statement() throw();
-
-    //! \brief Creates a statement for the given connection.
-    //! \param conn The connection where the statement will be executed.
-    //! \throws database_error
-    void open(class connection& conn);
-
-    //! \brief Returns true if connection is open.
-    bool open() const;
-
-    //! \brief Returns true if connected to the database.
-    bool connected() const;
-
-    //! \brief Returns the associated connection object if any.
-    class connection& connection();
-
-    //! \brief Returns the associated connection object if any.
-    const class connection& connection() const;
-
-    //! \brief Returns the native ODBC statement handle.
-    void* native_statement_handle() const;
-
-    //! \brief Closes the statement and frees all associated resources.
-    void close();
-
-    //! \brief Cancels execution of the statement.
-    //! \throws database_error
-    void cancel();
-
-    //! \brief Opens and prepares the given statement to execute on the given connection.
-    //! \param conn The connection where the statement will be executed.
-    //! \param query The SQL query that will be executed.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \see open()
-    //! \throws database_error
-    void prepare(class connection& conn, const string_type& query, long timeout = 0);
-
-    //! \brief Prepares the given statement to execute its associated connection.
-    //! If the statement is not open throws programming_error.
-    //! \param query The SQL query that will be executed.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \see open()
-    //! \throws database_error, programming_error
-    void prepare(const string_type& query, long timeout = 0);
-
-    //! \brief Sets the number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    void timeout(long timeout = 0);
-
-    //! \brief Immediately opens, prepares, and executes the given query directly on the given connection.
-    //! \param conn The connection where the statement will be executed.
-    //! \param query The SQL query that will be executed.
-    //! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \return A result set object.
-    //! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-    //! \see open(), prepare(), execute(), result, transaction
-    class result execute_direct(class connection& conn, const string_type& query, long batch_operations = 1, long timeout = 0);
-
-    //! \brief Execute the previously prepared query now without constructing result object.
-    //! \param conn The connection where the statement will be executed.
-    //! \param query The SQL query that will be executed.
-    //! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \return A result set object.
-    //! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-    //! \see open(), prepare(), execute(), execute_direct(), result, transaction
-    void just_execute_direct(class connection& conn, const string_type& query, long batch_operations = 1, long timeout = 0);
-
-    //! \brief Execute the previously prepared query now.
-    //! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \return A result set object.
-    //! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-    //! \see open(), prepare(), result, transaction
-    class result execute(long batch_operations = 1, long timeout = 0);
-
-    //! \brief Execute the previously prepared query now without constructing result object.
-    //! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-    //! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \return A result set object.
-    //! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-    //! \see open(), prepare(), execute(), result, transaction
-    void just_execute(long batch_operations = 1, long timeout = 0);
-
-    //! \brief Returns the input and output paramters of the specified stored procedure.
-    //! \param catalog The catalog name of the procedure.
-    //! \param schema Pattern to use for schema names.
-    //! \param procedure The name of the procedure.
-    //! \param column Pattern to use for column names.
-    //! \throws database_error
-    //! \return A result set object.
-    class result procedure_columns(const string_type& catalog, const string_type& schema, const string_type& procedure, const string_type& column);
-
-    //! \brief Returns the number of rows affected by the request or -1 if the number of affected rows is not available.
-    //! \throws database_error
-    long affected_rows() const;
-
-    //! \brief Returns the number of columns in a result set.
-    //! \throws database_error
-    short columns() const;
-
-    //! \brief Resets all currently bound parameters.
-    void reset_parameters() throw();
-
-    //! \brief Returns the parameter size for the indicated parameter placeholder within a prepared statement.
-    unsigned long parameter_size(short param) const;
-
-    //! \addtogroup binding Binding parameters
-    //! \brief These functions are used to bind values to ODBC parameters.
-    //!
-    //! @{
-
-    //! \brief Binds the given value to the given parameter placeholder number in the prepared statement.
-    //!
-    //! If your prepared SQL query has any ? placeholders, this is how you bind values to them.
-    //! Placeholder numbers count from left to right and are 0-indexed.
-    //!
-    //! It is NOT possible to use these functions for bulk operations as number of elements is not specified here.
-    //!
-    //! \param param Placeholder position.
-    //! \param value Value to substitute into placeholder.
-    //! \param direction ODBC parameter direction.
-    //! \throws database_error
-    template<class T>
-    void bind(short param, const T* value, param_direction direction = PARAM_IN);
-
-    //! \addtogroup bind_multi Binding multiple non-string values
-    //! \brief Binds the given values to the given parameter placeholder number in the prepared statement.
-    //!
-    //! If your prepared SQL query has any ? placeholders, this is how you bind values to them.
-    //! Placeholder numbers count from left to right and are 0-indexed.
-    //!
-    //! It is possible to use these functions for bulk operations.
-    //!
-    //! \param param Placeholder position.
-    //! \param values Values to substitute into placeholder.
-    //! \param elements The number of elements being bound.
-    //! \param null_sentry Value which should represent a null value.
-    //! \param nulls Flags for values that should be set to a null value.
-    //! \param param_direciton ODBC parameter direction.
-    //! \throws database_error
-    //!
-    //! @{
-
-    //! \brief Binds multiple values.
-    //! \see bind_multi
-    template<class T>
-    void bind(short param, const T* values, std::size_t elements, param_direction direction = PARAM_IN);
-
-    //! \brief Binds multiple values.
-    //! \see bind_multi
-    template<class T>
-    void bind(short param, const T* values, std::size_t elements, const T* null_sentry, param_direction direction = PARAM_IN);
-
-    //! \brief Binds multiple values.
-    //! \see bind_multi
-    template<class T>
-    void bind(short param, const T* values, std::size_t elements, const bool* nulls, param_direction direction = PARAM_IN);
-
-    //! @}
-
-    //! \addtogroup bind_strings Binding multiple string values
-    //! \brief Binds the given string values to the given parameter placeholder number in the prepared statement.
-    //!
-    //! If your prepared SQL query has any ? placeholders, this is how you bind values to them.
-    //! Placeholder numbers count from left to right and are 0-indexed.
-    //!
-    //! It is possible to use these functions for bulk operations.
-    //!
-    //! \param param Placeholder position.
-    //! \param values Values to substitute into placeholder.
-    //! \param length Maximum length of string elements.
-    //! \param elements The number of elements being bound. Otherwise the value N is taken as the number of elements.
-    //! \param null_sentry Value which should represent a null value.
-    //! \param nulls Flags for values that should be set to a null value.
-    //! \param param_direciton ODBC parameter direction.
-    //! \throws database_error
-    //!
-    //! @{
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    void bind_strings(
-        short param
-        , const string_type::value_type* values
-        , std::size_t length
-        , std::size_t elements
-        , param_direction direction = PARAM_IN);
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    template<std::size_t N, std::size_t M>
-    void bind_strings(
-        short param
-        , const string_type::value_type(&values)[N][M]
-        , param_direction direction = PARAM_IN)
-    {
-        bind_strings(
-            param
-            , reinterpret_cast<const string_type::value_type*>(values)
-            , M
-            , N
-            , direction);
-    }
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    void bind_strings(
-        short param
-        , const string_type::value_type* values
-        , std::size_t length
-        , std::size_t elements
-        , const string_type::value_type* null_sentry
-        , param_direction direction = PARAM_IN);
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    template<std::size_t N, std::size_t M>
-    void bind_strings(
-        short param
-        , const string_type::value_type(&values)[N][M]
-        , const string_type::value_type* null_sentry
-        , param_direction direction = PARAM_IN)
-    {
-        bind_strings(
-            param
-            , reinterpret_cast<const string_type::value_type*>(values)
-            , M
-            , N
-            , null_sentry
-            , direction);
-    }
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    void bind_strings(
-        short param
-        , const string_type::value_type* values
-        , std::size_t length
-        , std::size_t elements
-        , const bool* nulls
-        , param_direction direction = PARAM_IN);
-
-    //! \brief Binds multiple string values.
-    //! \see bind_strings
-    template<std::size_t N, std::size_t M>
-    void bind_strings(
-        short param
-        , const string_type::value_type(&values)[N][M]
-        , const bool* nulls
-        , param_direction direction = PARAM_IN)
-    {
-        bind_strings(
-            param
-            , reinterpret_cast<const string_type::value_type*>(values)
-            , M
-            , N
-            , nulls
-            , direction);
-    }
-
-    //! @}
-
-    //! \brief Binds null values to the given parameter placeholder number in the prepared statement.
-    //!
-    //! If your prepared SQL query has any ? placeholders, this is how you bind values to them.
-    //! Placeholder numbers count from left to right and are 0-indexed.
-    //!
-    //! It is possible to use this function for bulk operations.
-    //!
-    //! \param param Placeholder position.
-    //! \param elements The number of elements being bound.
-    //! \throws database_error
-    void bind_null(short param, std::size_t elements = 1);
-
-    //! @}
-
-private:
-    typedef std::function<bool (std::size_t)> null_predicate_type;
-
-private:
-    class statement_impl;
-    friend class nanodbc::result;
-
-private:
-    std::shared_ptr<statement_impl> impl_;
-};
-
-//! \brief Manages and encapsulates ODBC resources such as the connection and environment handles.
-class connection
-{
-public:
-    //! \brief Create new connection object, initially not connected.
-    connection();
-
-    //! Copy constructor.
-    connection(const connection& rhs);
-
-    //! Assignment.
-    connection& operator=(connection rhs);
-
-    //! Member swap.
-    void swap(connection&) throw();
-
-    //! \brief Create new connection object and immediately connect to the given data source.
-    //! \param dsn The name of the data source.
-    //! \param user The username for authenticating to the data source.
-    //! \param pass The password for authenticating to the data source.
-    //! \param timeout The number in seconds before connection timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \see connected(), connect()
-    connection(
-        const string_type& dsn
-        , const string_type& user
-        , const string_type& pass
-        , long timeout = 0);
-
-    //! \brief Create new connection object and immediately connect using the given connection string.
-    //! \param connection_string The connection string for establishing a connection.
-    //! \param timeout The number in seconds before connection timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \see connected(), connect()
-    connection(const string_type& connection_string, long timeout = 0);
-
-    //! \brief Automatically disconnects from the database and frees all associated resources.
-    //!
-    //! Will not throw even if disconnecting causes some kind of error and raises an exception.
-    //! If you explicitly need to know if disconnect() succeeds, call it directly.
-    ~connection() throw();
-
-    //! \brief Create new connection object and immediately connect to the given data source.
-    //! \param dsn The name of the data source.
-    //! \param user The username for authenticating to the data source.
-    //! \param pass The password for authenticating to the data source.
-    //! \param timeout The number in seconds before connection timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \see connected()
-    void connect(
-        const string_type& dsn
-        , const string_type& user
-        , const string_type& pass
-        , long timeout = 0);
-
-    //! \brief Create new connection object and immediately connect using the given connection string.
-    //! \param connection_string The connection string for establishing a connection.
-    //! \param timeout The number in seconds before connection timeout. Default is 0 indicating no timeout.
-    //! \throws database_error
-    //! \see connected()
-    void connect(const string_type& connection_string, long timeout = 0);
-
-    //! \brief Returns true if connected to the database.
-    bool connected() const;
-
-    //! \brief Disconnects from the database, but maintains environment and handle resources.
-    void disconnect();
-
-    //! \brief Returns the number of transactions currently held for this connection.
-    std::size_t transactions() const;
-
-    //! \brief Returns the native ODBC database connection handle.
-    void* native_dbc_handle() const;
-
-    //! \brief Returns the native ODBC environment handle.
-    void* native_env_handle() const;
-
-    //! \brief Returns the name of the ODBC driver.
-    //! \throws database_error
-    string_type driver_name() const;
-
-private:
-    std::size_t ref_transaction();
-    std::size_t unref_transaction();
-    bool rollback() const;
-    void rollback(bool onoff);
-
-private:
-    class connection_impl;
-    friend class nanodbc::transaction::transaction_impl;
-
-private:
-    std::shared_ptr<connection_impl> impl_;
-};
-
-//! \brief A resource for managing result sets from statement execution.
-//!
-//! \see statement::execute(), statement::execute_direct()
-//! \note result objects may be copied, however all copies will refer to the same underlying ODBC result set.
-class result
-{
-public:
-    //! Empty result set.
-    result();
-
-    //! Free result set.
-    ~result() throw();
-
-    //! Copy constructor.
-    result(const result& rhs);
-
-    //! Assignment.
-    result& operator=(result rhs);
-
-    //! Member swap.
-    void swap(result& rhs) throw();
-
-    //! \brief Returns the native ODBC statement handle.
-    void* native_statement_handle() const;
-
-    //! \brief The rowset size for this result set.
-    long rowset_size() const throw();
-
-    //! \brief Returns the number of rows affected by the request or -1 if the number of affected rows is not available.
-    //! \throws database_error
-    long affected_rows() const;
-
-    //! \brief Returns the number of rows in the current rowset or 0 if the number of rows is not available.
-    long rows() const throw();
-
-    //! \brief Returns the number of columns in a result set.
-    //! \throws database_error
-    short columns() const;
-
-    //! \brief Fetches the first row in the current result set.
-    //! \return true if there are more results or false otherwise.
-    //! \throws database_error
-    bool first();
-
-    //! \brief Fetches the last row in the current result set.
-    //! \return true if there are more results or false otherwise.
-    //! \throws database_error
-    bool last();
-
-    //! \brief Fetches the next row in the current result set.
-    //! \return true if there are more results or false otherwise.
-    //! \throws database_error
-    bool next();
-
-    //! \brief Fetches the prior row in the current result set.
-    //! \return true if there are more results or false otherwise.
-    //! \throws database_error
-    bool prior();
-
-    //! \brief Moves to and fetches the specified row in the current result set.
-    //! \return true if there are results or false otherwise.
-    //! \throws database_error
-    bool move(long row);
-
-    //! \brief Skips a number of rows and then fetches the resulting row in the current result set.
-    //! \return true if there are results or false otherwise.
-    //! \throws database_error
-    bool skip(long rows);
-
-    //! \brief Returns the row position in the current result set.
-    unsigned long position() const;
-
-    //! \brief Returns true if there are no more results in the current result set.
-    bool end() const throw();
-
-    //! \brief Gets data from the given column of the current rowset.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \param result The column's value will be written to this parameter.
-    //! \throws database_error, index_range_error, type_incompatible_error, null_access_error
-    template<class T>
-    void get_ref(short column, T& result) const;
-
-    //! \brief Gets data from the given column of the current rowset.
-    //! If the data is null, fallback is returned instead.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \param fallback if value is null, return fallback instead.
-    //! \param result The column's value will be written to this parameter.
-    //! \throws database_error, index_range_error, type_incompatible_error
-    template<class T>
-    void get_ref(short column, const T& fallback, T& result) const;
-
-    //! \brief Gets data from the given column by name of the current rowset.
-    //!
-    //! \param column_name column's name.
-    //! \param result The column's value will be written to this parameter.
-    //! \throws database_error, index_range_error, type_incompatible_error, null_access_error
-    template<class T>
-    void get_ref(const string_type& column_name, T& result) const;
-
-    //! \brief Gets data from the given column by name of the current rowset.
-    //! If the data is null, fallback is returned instead.
-    //!
-    //! \param column_name column's name.
-    //! \param fallback if value is null, return fallback instead.
-    //! \param result The column's value will be written to this parameter.
-    //! \throws database_error, index_range_error, type_incompatible_error
-    template<class T>
-    void get_ref(const string_type& column_name, const T& fallback, T& result) const;
-
-    //! \brief Gets data from the given column of the current rowset.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \throws database_error, index_range_error, type_incompatible_error, null_access_error
-    template<class T>
-    T get(short column) const;
-
-    //! \brief Gets data from the given column of the current rowset.
-    //! If the data is null, fallback is returned instead.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \param fallback if value is null, return fallback instead.
-    //! \throws database_error, index_range_error, type_incompatible_error
-    template<class T>
-    T get(short column, const T& fallback) const;
-
-    //! \brief Gets data from the given column by name of the current rowset.
-    //!
-    //! \param column_name column's name.
-    //! \throws database_error, index_range_error, type_incompatible_error, null_access_error
-    template<class T>
-    T get(const string_type& column_name) const;
-
-    //! \brief Gets data from the given column by name of the current rowset.
-    //! If the data is null, fallback is returned instead.
-    //!
-    //! \param column_name column's name.
-    //! \param fallback if value is null, return fallback instead.
-    //! \throws database_error, index_range_error, type_incompatible_error
-    template<class T>
-    T get(const string_type& column_name, const T& fallback) const;
-
-    //! \brief Returns true if and only if the given column of the current rowset is null.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \throws database_error, index_range_error
-    bool is_null(short column) const;
-
-    //! \brief Returns true if and only if the given column by name of the current rowset is null.
-    //!
-    //! \param column_name column's name.
-    //! \throws database_error, index_range_error
-    bool is_null(const string_type& column_name) const;
-
-    //! \brief Returns the name of the specified column.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \throws index_range_error
-    string_type column_name(short column) const;
-
-    //! \brief Returns the size of the specified column.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column position.
-    //! \throws index_range_error
-    long column_size(short column) const;
-
-    //! \brief Returns the column number of the specified column name.
-    //!
-    //! Columns are numbered from left to right and 0-indexed.
-    //! \param column_name column's name.
-    //! \throws index_range_error
-    short column(const string_type& column_name) const;
-
-    //! Returns a identifying integer value representing the SQL type of this column.
-    int column_datatype(short column) const;
-
-    //! Returns a identifying integer value representing the SQL type of this column by name.
-    int column_datatype(const string_type& column_name) const;
-
-    //! Returns a identifying integer value representing the C type of this column.
-    int column_c_datatype(short column) const;
-
-    //! Returns a identifying integer value representing the C type of this column by name.
-    int column_c_datatype(const string_type& column_name) const;
-
-    //! Returns the next result, for example when stored procedure returns multiple result sets.
-    bool next_result();
-
-    //! If and only if result object is valid, returns true.
-    explicit operator bool() const;
-
-private:
-    result(statement statement, long rowset_size);
-
-private:
-    class result_impl;
-    friend class nanodbc::statement::statement_impl;
-
-private:
-    std::shared_ptr<result_impl> impl_;
-};
-
-//! @}
-
-//! \addtogroup mainf Free Functions
-//! \brief Convenience functions.
-//!
-//! @{
-
-//! \brief Immediately opens, prepares, and executes the given query directly on the given connection.
-//! \param conn The connection where the statement will be executed.
-//! \param query The SQL query that will be executed.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-//! \return A result set object.
-//! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-//! \see open(), prepare(), execute(), result, transaction
-result execute(
-    connection& conn
-    , const string_type& query
-    , long batch_operations = 1
-    , long timeout = 0);
-
-//! \brief Immediately opens, prepares, and executes the given query directly on the given connection without creating result object.
-//! \param conn The connection where the statement will be executed.
-//! \param query The SQL query that will be executed.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-//! \return A result set object.
-//! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-//! \see open(), prepare(), execute(), result, transaction
-void just_execute(
-    connection& conn
-    , const string_type& query
-    , long batch_operations = 1
-    , long timeout = 0);
-
-//! \brief Execute the previously prepared query now.
-//! \param stmt The prepared statement that will be executed.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \throws database_error
-//! \return A result set object.
-//! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-//! \see open(), prepare(), execute(), result
-result execute(statement& stmt, long batch_operations = 1);
-
-//! \brief Execute the previously prepared query now and without creating result object.
-//! \param stmt The prepared statement that will be executed.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \throws database_error
-//! \return A result set object.
-//! \attention You will want to use transactions if you are doing batch operations because it will prevent auto commits from occurring after each individual operation is executed.
-//! \see open(), prepare(), execute(), result
-void just_execute(statement& stmt, long batch_operations = 1);
-
-//! \brief Execute the previously prepared query now.
-//! Executes within the context of a transaction object and commits the transaction directly after execution.
-//! \param stmt The prepared statement that will be executed in batch.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \throws database_error
-//! \return A result set object.
-//! \see open(), prepare(), execute(), result, transaction
-result transact(statement& stmt, long batch_operations);
-
-//! \brief Execute the previously prepared query now and without creating result object.
-//! Executes within the context of a transaction object and commits the transaction directly after execution.
-//! \param stmt The prepared statement that will be executed in batch.
-//! \param batch_operations Numbers of rows to fetch per rowset, or the number of batch parameters to process.
-//! \throws database_error
-//! \return A result set object.
-//! \see open(), prepare(), execute(), result, transaction
-void just_transact(statement& stmt, long batch_operations);
-
-//! \brief Prepares the given statement to execute on it associated connection.
-//! If the statement is not open throws programming_error.
-//! \param stmt The prepared statement that will be executed in batch.
-//! \param query The SQL query that will be executed.
-//! \param timeout The number in seconds before query timeout. Default is 0 indicating no timeout.
-//! \see open()
-//! \throws database_error, programming_error
-void prepare(statement& stmt, const string_type& query, long timeout = 0);
-
-//! @}
-
-} // namespace nanodbc
-
-#endif // NANODBC_H