//! \file nanodbc.h The entirety of nanodbc can be found within this file and nanodbc.cpp. //! \mainpage //! //! \section toc Table of Contents //! \li \ref license "License" //! \li \ref credits "Credits" //! \li \ref examples "Example Usage" //! \li \ref nanodbc "Namespace Reference" //! \li Project Homepage //! //! \section license License //! Copyright (C) 2013 lexicalunit //! //! 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. //! //! \section credits Credits //! 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 squarious@gmail.com
//! License: The MIT License
//! //! Transaction support was based on the implementation in SimpleDB: C++ ODBC database API.
//! SimpleDB is hosted at http://simpledb.sourceforge.net
//! Copyright (C) 2006 Eminence Technology Pty Ltd
//! Copyright (C) 2008-2010,2012 Russell Kliese russell@kliese.id.au
//! 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.
//! \page examples Example Usage //! \brief Example library usage. //! \include example.cpp #ifndef NANODBC_H #define NANODBC_H #include #include #ifdef NANODBC_USE_BOOST #define NANODBC_TR1_STD std:: #include #include namespace std { using namespace boost; } #else // You must explicitly request C++11 support by defining NANODBC_USE_CPP11 at compile time // , otherwise nanodbc will assume it must use tr1 instead. #ifndef NANODBC_USE_CPP11 #include #include #define NANODBC_TR1_STD std::tr1:: #else #include #include #define NANODBC_TR1_STD std:: #endif #endif #ifndef DOXYGEN namespace detail { // Safe Bool idiom from http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool#Solution_and_Sample_Code // Creative Commons Attribution/Share-Alike License: http://creativecommons.org/licenses/by-sa/3.0/ // In C++11 and on we can simply use explicit conversion operators instead of all this boilerplate class safe_bool_base { public: typedef void (safe_bool_base::*bool_type)() const; void this_type_does_not_support_comparisons() const {} protected: safe_bool_base() {} safe_bool_base(const safe_bool_base&) {} safe_bool_base& operator=(const safe_bool_base&) {return *this;} ~safe_bool_base() {} }; template class safe_bool : private safe_bool_base { // private or protected inheritance is very important here as it triggers the // access control violation in main. public: operator bool_type() const { return (static_cast(this))->boolean_test() ? &safe_bool_base::this_type_does_not_support_comparisons : 0; } protected: ~safe_bool() {} }; } // namespace detail #endif // DOXYGEN //! \brief The entirety of nanodbc can be found within this one namespace. //! \todo Implement retrieval of blob data. //! \todo Implement reflective features for columns, such as type, to enable visitation. 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 NANODBC_TR1_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 Utility Classes //! \brief Additional nanodbc utility classes. //! //! \{ //! \brief A type for representing date data. struct date { NANODBC_TR1_STD int16_t year; //!< Year [0-inf). NANODBC_TR1_STD int16_t month; //!< Month of the year [1-12]. NANODBC_TR1_STD int16_t day; //!< Day of the month [1-31]. }; //! \brief A type for representing timestamp data. struct timestamp { NANODBC_TR1_STD int16_t year; //!< Year [0-inf). NANODBC_TR1_STD int16_t month; //!< Month of the year [1-12]. NANODBC_TR1_STD int16_t day; //!< Day of the month [1-31]. NANODBC_TR1_STD int16_t hour; //!< Hours since midnight [0-23]. NANODBC_TR1_STD int16_t min; //!< Minutes after the hour [0-59]. NANODBC_TR1_STD int16_t sec; //!< Seconds after the minute. NANODBC_TR1_STD int32_t fract; //!< Fractional seconds. }; //! \} //! \addtogroup main 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: NANODBC_TR1_STD shared_ptr impl_; }; //! \brief Represents a statement on the database. class statement { public: //! \brief Provides support for retrieving output/return parameters. //! \see bind_parameter() 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(), 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(), 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. //! \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 class result execute(long batch_operations = 1, long timeout = 0); //! \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; //! \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 possible to use this function for bulk operations. //! //! \param param Placeholder position. //! \param value Value to substitute into placeholder. //! \param nulls Used to batch insert nulls into the database. //! \param param_direciton ODBC parameter direction. //! \throws database_error template void bind_parameter(short param, const T* value, null_type* nulls = 0, param_direction direction = PARAM_IN); //! \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. //! //! Typically you would use bulk operations with a row size of N when executing a statement bound this way. //! //! \param param Placeholder position. //! \param values Values to bulk substitute into placeholder. //! \param nulls Used to batch insert nulls into the database. //! \param param_direciton ODBC parameter direction. //! \throws database_error template void bind_parameter(short param, const T(*values)[N], null_type* nulls = 0, param_direction direction = PARAM_IN) { bind_parameter(param, reinterpret_cast(values), nulls, direction); } //! \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. //! //! This function is dedicated to bulk operations involving strings. //! //! \param param Placeholder position. //! \param values Values to bulk substitute into placeholder. //! \param nulls Used to batch insert nulls into the database. //! \param param_direciton ODBC parameter direction. //! \throws database_error template void bind_strings(long param, const string_type::value_type (&values)[N][M], null_type* nulls = 0, param_direction direction = PARAM_IN) { bind_parameter(param, reinterpret_cast(values), nulls, direction); } private: class statement_impl; friend class nanodbc::result; private: NANODBC_TR1_STD shared_ptr 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: NANODBC_TR1_STD shared_ptr impl_; }; #ifndef DOXYGEN #ifdef NANODBC_USE_CPP11 class result #else class result : public detail::safe_bool #endif #else //! \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 #endif // doxygen { 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. //! \throws database_error, index_range_error, type_incompatible_error, null_access_error template 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 T get(short column, const T& fallback) const; //! \brief Gets data from the given column by name of the current rowset. //! //! \param column column's name. //! \param row If there are multiple rows in this rowset, get from the specified row. //! \throws database_error, index_range_error, type_incompatible_error, null_access_error template 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 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 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 column's name. //! \throws index_range_error short column(const string_type& column_name) const; //! Returns a identifying integer value representing the C type of this column. int column_datatype(short column) const; //! Returns a identifying integer value representing the C type of this column by name. int column_datatype(const string_type& column_name) const; //! Returns the next result, for example when stored procedure returns multiple result sets. bool next_result() const; #ifndef DOXYGEN #ifdef NANODBC_USE_CPP11 explicit operator bool() const; #else bool boolean_test() const; #endif // NANODBC_USE_CPP11 #else //! If and only if result object is valid, returns true. //! \note A safe-bool idiom is used if NANODBC_USE_CPP11 is not defined, otherwise an explicit conversion operator is used. operator bool() const; #endif // DOXYGEN private: result(statement statement, long rowset_size); private: class result_impl; friend class nanodbc::statement::statement_impl; private: NANODBC_TR1_STD shared_ptr impl_; }; //! @} //! \addtogroup main 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 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. //! 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 Prepares the given statement to execute on it associated connection. //! If the statement is not open throws programming_error. //! \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, programming_error void prepare(statement& stmt, const string_type& query, long timeout = 0); //! @} } // namespace nanodbc #undef NANODBC_TR1_STD #endif // NANODBC_H