Add odbc-cpp-wrapper library to external folder

This commit is contained in:
Maxim Rylov 2021-01-12 15:57:00 +01:00
parent 7f7bbae1d0
commit d4494e5b43
55 changed files with 8525 additions and 70 deletions

View File

@ -155,19 +155,6 @@ RUN curl -v -j -k -L -H "Cookie: eula_3_1_agreed=tools.hana.ondemand.com/develop
&& rm -rf client \
&& rm hanaclient*
ENV PATH="/usr/sap/hdbclient:${PATH}"
# Install cmake version 3.12 required by odbc-cpp-wrapper
RUN curl -LJO https://cmake.org/files/v3.12/cmake-3.12.4-Linux-x86_64.tar.gz \
&& tar -xvf cmake-3.12.4-Linux-x86_64.tar.gz && cp cmake-3.12.4-Linux-x86_64/bin/cmake /usr/local/bin \
&& mkdir -p /usr/local/share/cmake-3.12 \
&& cp -r cmake-3.12.4-Linux-x86_64/share/cmake-3.12/* /usr/local/share/cmake-3.12 \
&& rm -rf cmake-3.12.4-Linux-x86_64*
# Download and compile odbc-cpp-wrapper
RUN git clone --branch v0.2 --depth 1 https://github.com/SAP/odbc-cpp-wrapper.git \
&& mkdir odbc-cpp-wrapper/build \
&& cd odbc-cpp-wrapper/build \
&& cmake .. \
&& make -j 2 \
&& make install
# MSSQL: client side
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -

View File

@ -240,10 +240,17 @@ if(WITH_CORE)
set(ORACLE_LIBDIR "" CACHE STRING "Path to OCI libraries")
endif()
SET (WITH_HANA FALSE CACHE BOOL "Determines whether SAP HANA Spatial support should be built")
IF(WITH_HANA)
SET(HAVE_HANA TRUE)
ENDIF(WITH_HANA)
set (WITH_HANA FALSE CACHE BOOL "Determines whether SAP HANA Spatial support should be built")
if(WITH_HANA)
find_package(ODBC)
if(ODBC_FOUND)
set(HAVE_HANA TRUE)
add_subdirectory(external/odbccpp)
set_target_properties(odbccpp odbccpp_static PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
else()
message(STATUS "Couldn't find ODBC library")
endif()
endif(WITH_HANA)
set (WITH_PDAL FALSE CACHE BOOL "Determines whether PDAL support should be built")
if(WITH_PDAL)

View File

@ -1,48 +0,0 @@
# Find odbc-cpp-wrapper
# ~~~~~~~~~~~~~~~
# CMake module to search for C++ Wrapper for ODBC from:
# https://github.com/SAP/odbc-cpp-wrapper
FIND_PACKAGE(ODBC REQUIRED)
IF (NOT ODBCCPP_INCLUDE_DIR)
FIND_PATH(ODBCCPP_INCLUDE_DIR odbc/Environment.h
PATHS
/usr/local/include
/usr/include
c:/msys/local/include
"$ENV{LIB_DIR}/include"
$ENV{INCLUDE}
"$ENV{ODBCCPP_PATH}/include"
)
ENDIF (NOT ODBCCPP_INCLUDE_DIR)
IF (NOT ODBCCPP_LIBRARY)
FIND_LIBRARY(ODBCCPP_LIBRARY odbccpp_static
PATHS
/usr/lib
/usr/local/lib
c:/msys/local/lib
"$ENV{LIB_DIR}/lib"
$ENV{LIB}
"$ENV{ODBCCPP_PATH}/lib"
)
ENDIF (NOT ODBCCPP_LIBRARY)
IF (ODBCCPP_INCLUDE_DIR AND ODBCCPP_LIBRARY)
SET(ODBCCPP_FOUND TRUE)
ENDIF (ODBCCPP_INCLUDE_DIR AND ODBCCPP_LIBRARY)
IF (ODBCCPP_FOUND)
IF (NOT ODBCCPP_FIND_QUIETLY)
MESSAGE(STATUS "Found odbc-cpp: ${ODBCCPP_LIBRARY}")
ENDIF (NOT ODBCCPP_FIND_QUIETLY)
ELSE (ODBCCPP_FOUND)
IF (ODBCCPP_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find odbc-cpp library")
ELSE (ODBCCPP_FIND_REQUIRED)
IF (NOT ODBCCPP_FIND_QUIETLY)
MESSAGE(STATUS "Could not find odbc-cpp library")
ENDIF (NOT ODBCCPP_FIND_QUIETLY)
ENDIF (ODBCCPP_FIND_REQUIRED)
ENDIF (ODBCCPP_FOUND)

10
external/odbccpp/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,10 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
SET(CMAKE_CXX_STANDARD 11)
PROJECT(odbccpp)
FIND_PACKAGE(ODBC REQUIRED)
ADD_SUBDIRECTORY(src)

212
external/odbccpp/LICENSE vendored Normal file
View File

@ -0,0 +1,212 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--- Exceptions to the Apache 2.0 License ---
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision
(Section 3), the indemnity provision (Section 9) or other Section of the
License conflicts with the conditions of the GPLv2, you may retroactively
and prospectively choose to deem waived or otherwise exclude such Section(s)
of the License, but only in their entirety and only with respect to the
Combined Software.

1
external/odbccpp/NOTICE vendored Normal file
View File

@ -0,0 +1 @@
Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.

191
external/odbccpp/README.md vendored Normal file
View File

@ -0,0 +1,191 @@
# C++ Wrapper for ODBC
odbc-cpp-wrapper is an object-oriented C++-wrapper of the ODBC API. It takes
care of
- managing the lifetime of ODBC resources,
- allocating and managing resources needed for ODBC operations and
- converting ODBC errors to exceptions and throwing them.
The odbc-cpp-wrapper API attempts to make usage of ODBC as simple as possible.
The API was designed to make wrong usage almost impossible and to ensure proper
object lifetime management.
odbc-cpp-wrapper was originally developed for exchanging spatial data with
databases. It focuses on batch operations of variable-sized data, which is not
very well supported by other ODBC wrappers.
## Requirements
To build odbc-cpp-wrapper you need
- A C++-11-standard-compliant compiler
- [The Git command line client](https://git-scm.com/)
- [CMake 3.12 or newer](https://cmake.org/)
On Linux platforms you additionally need
- [unixODBC](http://www.unixodbc.org/)
To generate the API's documentation, you need
- [Doxygen 1.8.0 or later](http://www.doxygen.nl/)
## Building and Installation
### Linux
- Clone the repository:
```
git clone https://github.com/SAP/odbc-cpp-wrapper.git
```
- Create a build directory and change to it:
```
mkdir odbc-cpp-wrapper/build && cd odbc-cpp-wrapper/build
```
- Create the makefiles with CMake:
```
cmake ..
```
- Build the library:
```
make -j <number of parallel build jobs>
```
The build will create a shared library `libodbccpp.so` and a static library `libodbccpp_static.a`.
- To build the documentation (optional):
```
make doc
```
The mainpage of the documentation can be found at `doc/html/index.html`.
- Install the library:
```
sudo make install
```
This will install the library and header files. CMake will install them to `usr/local/lib` and `usr/local/include` by default. If you prefer different locations, you can set CMake's install prefix to a different path. See
https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html for details.
### Windows
- Clone the repository:
```
git clone https://github.com/SAP/odbc-cpp-wrapper.git
```
- Create a build directory and change to it:
```
mkdir odbc-cpp-wrapper\build && cd odbc-cpp-wrapper\build
```
#### Visual Studio 2015 and later
- Generate a Visual Studio solution
```
cmake ..
```
You can then open the `odbccpp.sln` file and build the desired targets in Visual Studio.
#### MSBuild (nmake)
- Start the Visual Studio Native Tools Command Prompt for the desired target and change the directory to the build directory. Create the makefiles for nmake:
```
cmake -G "NMake Makefiles" ..
```
> Optionally you can use CMAKE_BUILD_TYPE to define if you'd like to build a Debug or Release build. See
https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html for details.
- Build the library:
```
nmake
```
The build will create a dynamic link library `odbccpp.dll` and a static library `odbccpp_static.lib`.
- Build the documentation (optional):
```
nmake doc
```
The mainpage of the documentation can be found at `doc\html\index.html`.
- Install the library (optional):
```
nmake install
```
This will install the library and header files. CMake will install them to `C:\Program Files\odbccpp` by default. If you prefer a different location, you can set CMake's install prefix to a different path. See
https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html for details.
## Using the library
You can just link against the shared/dynamic or the static library. If you are linking against the static library, you have to additionally define `ODBC_STATIC` when compiling.
Usage of the library should be pretty straight-forward if you are familiar with ODBC and/or other database connectors.
### Example
The following code gives an example how working with odbc-cpp-wrapper looks like. It connects to a database, batch inserts two rows and executes a query.
```cpp
#include <iostream>
#include <odbc/Connection.h>
#include <odbc/Environment.h>
#include <odbc/Exception.h>
#include <odbc/PreparedStatement.h>
#include <odbc/ResultSet.h>
int main()
{
try
{
odbc::EnvironmentRef env = odbc::Environment::create();
odbc::ConnectionRef conn = env->createConnection();
conn->connect("DSN", "user", "pass");
conn->setAutoCommit(false);
odbc::PreparedStatementRef psInsert =
conn->prepareStatement("INSERT INTO TAB (ID, DATA) VALUES (?, ?)");
psInsert->setInt(1, 101);
psInsert->setCString(2, "One hundred one");
psInsert->addBatch();
psInsert->setInt(1, 102);
psInsert->setCString(2, "One hundred two");
psInsert->addBatch();
psInsert->executeBatch();
conn->commit();
odbc::PreparedStatementRef psSelect =
conn->prepareStatement("SELECT ID, DATA FROM TAB WHERE ID > ?");
psSelect->setInt(1, 100);
odbc::ResultSetRef rs = psSelect->executeQuery();
while (rs->next())
{
std::cout << rs->getInt(1) << ", " << rs->getString(2) << std::endl;
}
}
catch (const odbc::Exception& e)
{
std::cerr << e.what() << std::endl;
}
}
```
## How to obtain support
If you experience issues with using the library, please file a report in the GitHub bug tracking system.
## License
Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.
This file is licensed under the Apache Software License 2.0 except as noted otherwise in the [LICENSE](LICENSE) file.

2
external/odbccpp/src/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,2 @@
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
ADD_SUBDIRECTORY(odbc)

View File

@ -0,0 +1,79 @@
# Public Header Files
SET(public_headers
Config.h
Connection.h
DatabaseMetaData.h
DatabaseMetaDataBase.h
DatabaseMetaDataUnicode.h
Environment.h
Exception.h
Forwards.h
ParameterMetaData.h
PreparedStatement.h
RefCounted.h
ResultSet.h
ResultSetMetaData.h
ResultSetMetaDataBase.h
ResultSetMetaDataUnicode.h
Statement.h
StatementBase.h
Types.h
Util.h
)
# Sources
SET(odbccpp_sources
Connection.cpp
DatabaseMetaData.cpp
DatabaseMetaDataBase.cpp
DatabaseMetaDataUnicode.cpp
Environment.cpp
Exception.cpp
ParameterMetaData.cpp
PreparedStatement.cpp
RefCounted.cpp
ResultSet.cpp
ResultSetMetaData.cpp
ResultSetMetaDataBase.cpp
ResultSetMetaDataUnicode.cpp
Statement.cpp
StatementBase.cpp
Types.cpp
Util.cpp
internal/Batch.cpp
internal/ParameterData.cpp
internal/UtilInternal.cpp
)
# Static library
ADD_LIBRARY(odbccpp_static
STATIC
${odbccpp_sources}
)
TARGET_COMPILE_DEFINITIONS(odbccpp_static
PUBLIC
ODBC_STATIC
)
SET_PROPERTY(TARGET odbccpp_static PROPERTY POSITION_INDEPENDENT_CODE ON)
# Shared library
ADD_LIBRARY(odbccpp
SHARED
${odbccpp_sources}
)
TARGET_COMPILE_DEFINITIONS(odbccpp
PRIVATE
ODBC_EXPORTS
)
TARGET_LINK_LIBRARIES(odbccpp
PUBLIC
${ODBC_LIBRARIES}
)
SET_PROPERTY(TARGET odbccpp PROPERTY public_headers ${public_headers})
# Installation
INSTALL(TARGETS odbccpp_static DESTINATION lib)
INSTALL(TARGETS odbccpp DESTINATION lib)
INSTALL(FILES ${public_headers} DESTINATION include/odbc)

24
external/odbccpp/src/odbc/Config.h vendored Normal file
View File

@ -0,0 +1,24 @@
#ifndef ODBC_CONFIG_H_INCLUDED
#define ODBC_CONFIG_H_INCLUDED
//------------------------------------------------------------------------------
#ifndef ODBC_EXPORT
# ifdef ODBC_STATIC
# define ODBC_EXPORT
# else
# if defined(__clang__) || defined(__GNUC__)
# define ODBC_EXPORT __attribute__ ((visibility("default")))
# elif defined(_MSC_VER)
# ifdef ODBC_EXPORTS
# define ODBC_EXPORT __declspec(dllexport)
# else
# define ODBC_EXPORT __declspec(dllimport)
# endif
# endif
# endif
#endif
//------------------------------------------------------------------------------
#ifdef _WIN32
# pragma warning(disable: 4251 4275)
#endif
//------------------------------------------------------------------------------
#endif

265
external/odbccpp/src/odbc/Connection.cpp vendored Normal file
View File

@ -0,0 +1,265 @@
#include <limits>
#include <odbc/Connection.h>
#include <odbc/DatabaseMetaData.h>
#include <odbc/DatabaseMetaDataUnicode.h>
#include <odbc/Environment.h>
#include <odbc/Exception.h>
#include <odbc/PreparedStatement.h>
#include <odbc/ResultSet.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
Connection::Connection(Environment* parent)
: parent_(parent, true)
, hdbc_(SQL_NULL_HANDLE)
, connected_(false)
{
}
//------------------------------------------------------------------------------
Connection::~Connection()
{
if (connected_)
SQLDisconnect(hdbc_);
if (hdbc_)
SQLFreeHandle(SQL_HANDLE_DBC, hdbc_);
}
//------------------------------------------------------------------------------
void Connection::setHandle(void* hdbc)
{
hdbc_ = hdbc;
}
//------------------------------------------------------------------------------
void Connection::connect(const char* dsn, const char* user,
const char* password)
{
EXEC_DBC(SQLConnectA, hdbc_,
(SQLCHAR*)dsn, SQL_NTS,
(SQLCHAR*)user, SQL_NTS,
(SQLCHAR*)password, SQL_NTS);
connected_ = true;
}
//------------------------------------------------------------------------------
void Connection::connect(const char* connString)
{
SQLCHAR outString[1024];
SQLSMALLINT outLength;
EXEC_DBC(SQLDriverConnectA, hdbc_, NULL,
(SQLCHAR*)connString, SQL_NTS,
outString, sizeof(outString),
&outLength, SQL_DRIVER_NOPROMPT);
connected_ = true;
}
//------------------------------------------------------------------------------
void Connection::disconnect()
{
SQLRETURN rc = SQLDisconnect(hdbc_);
connected_ = false;
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
}
//------------------------------------------------------------------------------
bool Connection::connected() const
{
return connected_;
}
//------------------------------------------------------------------------------
bool Connection::isValid()
{
SQLULEN ret = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_CONNECTION_DEAD, &ret, 0, NULL);
return ret == SQL_CD_FALSE;
}
//------------------------------------------------------------------------------
unsigned long Connection::getConnectionTimeout()
{
SQLULEN ret = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_CONNECTION_TIMEOUT, &ret, 0,
NULL);
return (unsigned long)ret;
}
//------------------------------------------------------------------------------
void Connection::setConnectionTimeout(unsigned long seconds)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, SQL_ATTR_CONNECTION_TIMEOUT,
(SQLPOINTER)(ptrdiff_t)seconds, SQL_IS_UINTEGER);
}
//------------------------------------------------------------------------------
unsigned long Connection::getLoginTimeout()
{
SQLULEN ret = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_LOGIN_TIMEOUT, &ret, 0, NULL);
return (unsigned long)ret;
}
//------------------------------------------------------------------------------
void Connection::setLoginTimeout(unsigned long seconds)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, SQL_ATTR_LOGIN_TIMEOUT,
(SQLPOINTER)(ptrdiff_t)seconds, SQL_IS_UINTEGER);
}
//------------------------------------------------------------------------------
void Connection::setAttribute(int attr, int value)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, attr, (SQLPOINTER)(ptrdiff_t)value,
SQL_IS_INTEGER);
}
//------------------------------------------------------------------------------
void Connection::setAttribute(int attr, unsigned int value)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, attr, (SQLPOINTER)(ptrdiff_t)value,
SQL_IS_UINTEGER);
}
//------------------------------------------------------------------------------
void Connection::setAttribute(int attr, const char* value)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, attr, (SQLPOINTER)value, SQL_NTS);
}
//------------------------------------------------------------------------------
void Connection::setAttribute(int attr, const char* value, std::size_t size)
{
if (size > (size_t)numeric_limits<SQLINTEGER>::max())
throw Exception("The attribute value is too long");
EXEC_DBC(SQLSetConnectAttr, hdbc_, attr, (SQLPOINTER)value,
(SQLINTEGER)size);
}
//------------------------------------------------------------------------------
void Connection::setAttribute(int attr, const void* value, std::size_t size)
{
if (size > (size_t)numeric_limits<SQLINTEGER>::max())
throw Exception("The attribute value is too long");
EXEC_DBC(SQLSetConnectAttr, hdbc_, attr, (SQLPOINTER)value,
(SQLINTEGER)size);
}
//------------------------------------------------------------------------------
bool Connection::getAutoCommit() const
{
SQLULEN ret = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_AUTOCOMMIT, &ret, 0, NULL);
return ret == SQL_AUTOCOMMIT_ON;
}
//------------------------------------------------------------------------------
void Connection::setAutoCommit(bool autoCommit)
{
EXEC_DBC(SQLSetConnectAttr, hdbc_, SQL_ATTR_AUTOCOMMIT,
autoCommit ?
(SQLPOINTER)SQL_AUTOCOMMIT_ON: (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
SQL_IS_INTEGER);
}
//------------------------------------------------------------------------------
void Connection::commit()
{
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_COMMIT);
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
}
//------------------------------------------------------------------------------
void Connection::rollback()
{
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_ROLLBACK);
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
}
//------------------------------------------------------------------------------
bool Connection::isReadOnly()
{
SQLULEN ret = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_ACCESS_MODE, &ret, 0, NULL);
return ret == SQL_MODE_READ_ONLY;
}
//------------------------------------------------------------------------------
void Connection::setReadOnly(bool readOnly)
{
unsigned int mode = readOnly ? SQL_MODE_READ_ONLY : SQL_MODE_READ_WRITE;
setAttribute(SQL_ATTR_ACCESS_MODE, mode);
}
//------------------------------------------------------------------------------
TransactionIsolationLevel Connection::getTransactionIsolation()
{
SQLULEN txn = 0;
EXEC_DBC(SQLGetConnectAttr, hdbc_, SQL_ATTR_TXN_ISOLATION, &txn, 0, NULL);
switch (txn)
{
case SQL_TXN_READ_COMMITTED:
return TransactionIsolationLevel::READ_COMMITTED;
case SQL_TXN_READ_UNCOMMITTED:
return TransactionIsolationLevel::READ_UNCOMMITTED;
case SQL_TXN_REPEATABLE_READ:
return TransactionIsolationLevel::REPEATABLE_READ;
case SQL_TXN_SERIALIZABLE:
return TransactionIsolationLevel::SERIALIZABLE;
case 0:
return TransactionIsolationLevel::NONE;
default:
throw Exception("Unknown transaction isolation level.");
}
}
//------------------------------------------------------------------------------
void Connection::setTransactionIsolation(TransactionIsolationLevel level)
{
unsigned int txn = 0;
switch (level)
{
case TransactionIsolationLevel::READ_COMMITTED:
txn = SQL_TXN_READ_COMMITTED;
break;
case TransactionIsolationLevel::READ_UNCOMMITTED:
txn = SQL_TXN_READ_UNCOMMITTED;
break;
case TransactionIsolationLevel::REPEATABLE_READ:
txn = SQL_TXN_REPEATABLE_READ;
break;
case TransactionIsolationLevel::SERIALIZABLE:
txn = SQL_TXN_SERIALIZABLE;
break;
case TransactionIsolationLevel::NONE:
throw Exception("NONE transaction isolation level cannot be set.");
}
setAttribute(SQL_ATTR_TXN_ISOLATION, txn);
}
//------------------------------------------------------------------------------
StatementRef Connection::createStatement()
{
SQLHANDLE hstmt;
StatementRef ret(new Statement(this));
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc_, &hstmt);
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
ret->setHandle(hstmt);
return ret;
}
//------------------------------------------------------------------------------
PreparedStatementRef Connection::prepareStatement(const char* sql)
{
SQLHANDLE hstmt;
PreparedStatementRef ret(new PreparedStatement(this));
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc_, &hstmt);
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
ret->setHandleAndQuery(hstmt, sql);
return ret;
}
//------------------------------------------------------------------------------
PreparedStatementRef Connection::prepareStatement(const char16_t* sql)
{
SQLHANDLE hstmt;
PreparedStatementRef ret(new PreparedStatement(this));
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc_, &hstmt);
Exception::checkForError(rc, SQL_HANDLE_DBC, hdbc_);
ret->setHandleAndQuery(hstmt, sql);
return ret;
}
//------------------------------------------------------------------------------
DatabaseMetaDataRef Connection::getDatabaseMetaData()
{
DatabaseMetaDataRef ret(new DatabaseMetaData(this));
return ret;
}
//------------------------------------------------------------------------------
DatabaseMetaDataUnicodeRef Connection::getDatabaseMetaDataUnicode()
{
DatabaseMetaDataUnicodeRef ret(new DatabaseMetaDataUnicode(this));
return ret;
}
//------------------------------------------------------------------------------
} // namespace odbc

328
external/odbccpp/src/odbc/Connection.h vendored Normal file
View File

@ -0,0 +1,328 @@
#ifndef ODBC_CONNECTION_H_INCLUDED
#define ODBC_CONNECTION_H_INCLUDED
//------------------------------------------------------------------------------
#include <cstddef>
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* ODBC Connection Wrapper.
*
* A new Connection object can be created using Environment::createConnection().
*
* If a Connection object is connected to a database, it will disconnect
* automatically when destroyed. However, it is not possible to check if an
* error occurred during the disconnect. Therefore, it is recommended to use the
* disconnect() member function to disconnect.
*/
class ODBC_EXPORT Connection : public RefCounted
{
friend class Environment;
friend class DatabaseMetaDataBase;
friend class DatabaseMetaDataUnicode;
private:
Connection(Environment* parent);
~Connection();
private:
void setHandle(void* hdbc);
public:
/**
* Attempts to establish a database connection using a data source name.
*
* The method will throw an exception if a connection could not be
* established.
*
* @param dsn The data source name.
* @param user The user name to use.
* @param password The password to use.
*/
void connect(const char* dsn, const char* user, const char* password);
/**
* Attempts to establish a database connection using an ODBC driver-specific
* connection string.
*
* Refer to the documentation of your ODBC driver for details on the
* connection string.
*
* The method will throw an exception if a connection could not be
* established.
*
* @param connString The driver-specific connection string.
*/
void connect(const char* connString);
/**
* Disconnects from the database.
*/
void disconnect();
/**
* Checks whether this Connection object is currently connected to the
* database.
*
* This method checks only whether a successful connection has been made in
* the past and has not been actively disconnected by the client. It will
* not check if the connection is still alive. Use function isValid() to
* check if a connection is still alive.
*
* @return Returns true if the Connection object is connected to the
* database, false otherwise.
*/
bool connected() const;
/**
* Checks whether the connection is still alive or not.
*
* @return Returns true if the connection is alive, false otherwise.
*/
bool isValid();
/**
* Gets the number of seconds that a driver can wait for any connection
* request to complete.
*
* If the returned value equals 0, the driver will wait infinitely. The
* default value is driver-specific.
*
* @return Returns the connection timeout.
*/
unsigned long getConnectionTimeout();
/**
* Sets the numer of seconds that a driver will wait for a connection
* request to complete.
*
* If the value is set to 0, the driver can wait infinitely.
*
* @param seconds The connection timeout.
*/
void setConnectionTimeout(unsigned long seconds);
/**
* Gets the number of seconds that a driver can wait for a login attempt to
* complete.
*
* If the returned value equals 0, the driver will wait infinitely. The
* default value is driver-specific.
*
* @return Returns the login timeout.
*/
unsigned long getLoginTimeout();
/**
* Sets the numer of seconds that a driver will wait for a login attempt to
* complete.
*
* If the value is set to 0, the driver can wait infinitely.
*
* @param seconds The login timeout.
*/
void setLoginTimeout(unsigned long seconds);
/**
* Sets a connection attribute of type integer.
*
* This method is intended for setting driver-specific connection
* attributes. Generic connection attributes like the auto-commit attribute
* should be set via the corresponding member function. Refer to your ODBC
* driver documentation for a list of available attributes.
*
* Some connection attributes can only be set before a connection has been
* made, others can only be set after a connection has been made. Refer
* to the documentation of the attribute to find out if it must been set
* before or after making a connection.
*
* @param attr The attribute to set.
* @param value The value to set.
*/
void setAttribute(int attr, int value);
/**
* Sets a connection attribute of type unsigned integer.
*
* This method is intended for setting driver-specific connection
* attributes. Generic connection attributes like the auto-commit attribute
* should be set via the corresponding member function. Refer to your ODBC
* driver documentation for a list of available attributes.
*
* Some connection attributes can only be set before a connection has been
* made, others can only be set after a connection has been made. Refer
* to the documentation of the attribute to find out if it must been set
* before or after making a connection.
*
* @param attr The attribute to set.
* @param value The value to set.
*/
void setAttribute(int attr, unsigned int value);
/**
* Sets a connection attribute of type string.
*
* This method is intended for setting driver-specific connection
* attributes. Generic connection attributes like the auto-commit attribute
* should be set via the corresponding member function. Refer to your ODBC
* driver documentation for a list of available attributes.
*
* Some connection attributes can only be set before a connection has been
* made, others can only be set after a connection has been made. Refer
* to the documentation of the attribute to find out if it must been set
* before or after making a connection.
*
* @param attr The attribute to set.
* @param value The value to set.
*/
void setAttribute(int attr, const char* value);
/**
* Sets a connection attribute of type string.
*
* This method is intended for setting driver-specific connection
* attributes. Generic connection attributes like the auto-commit attribute
* should be set via the corresponding member function. Refer to your ODBC
* driver documentation for a list of available attributes.
*
* Some connection attributes can only be set before a connection has been
* made, others can only be set after a connection has been made. Refer
* to the documentation of the attribute to find out if it must been set
* before or after making a connection.
*
* @param attr The attribute to set.
* @param value The value to set.
* @param length The length of the string.
*/
void setAttribute(int attr, const char* value, std::size_t length);
/**
* Sets a connection attribute of type binary.
*
* This method is intended for setting driver-specific connection
* attributes. Generic connection attributes like the auto-commit attribute
* should be set via the corresponding member function. Refer to your ODBC
* driver documentation for a list of available attributes.
*
* Some connection attributes can only be set before a connection has been
* made, others can only be set after a connection has been made. Refer
* to the documentation of the attribute to find out if it must been set
* before or after making a connection.
*
* @param attr The attribute to set.
* @param value The value to set.
* @param size The size of the binary data in bytes.
*/
void setAttribute(int attr, const void* value, std::size_t size);
/**
* Checks whether auto-commit mode is enabled.
*
* @return Returns true if auto-commit is enabled, false otherwise.
*/
bool getAutoCommit() const;
/**
* Enables or disables auto-commit mode for this connection.
*
* @param autoCommit Set to true to enable auto-commit, set to false to
* disable auto-commit.
*/
void setAutoCommit(bool autoCommit);
/**
* Commits all changes made since the previous commit or rollback.
*/
void commit();
/**
* Undoes all changes made since the previous commit or rollback.
*/
void rollback();
/**
* Checks whether the Connection object allows statements performing any
* updates on data.
*
* @return Returns true if the Connection is in read only mode.
*/
bool isReadOnly();
/**
* Sets the Connection object either in read-only or read-write mode.
*
* By default, the read-only mode is set to false.
*
* @param readOnly The value indicating read-only mode.
*/
void setReadOnly(bool readOnly);
/**
* Retrieves the transaction isolation level of this Connection object.
*
* @return Returns the transaction isolation level.
*/
TransactionIsolationLevel getTransactionIsolation();
/**
* Attempts to set the isolation level on which the transactions should be
* executed.
*
* This method can be called only if there are no open transactions on the
* current Connection object.
*
* @param level The transaction isolation level.
*/
void setTransactionIsolation(TransactionIsolationLevel level);
/**
* Creates a new Statement object.
*
* @return Returns a reference to the newly created Statement object.
*/
StatementRef createStatement();
/**
* Creates a new PreparedStatement object.
*
* @param sql The SQL statement to prepare. Use '?' for placeholders.
* @return Returns a reference to the newly created PreparedStatement
* object.
*/
PreparedStatementRef prepareStatement(const char* sql);
/**
* Creates a new PreparedStatement object.
*
* @param sql The SQL statement to prepare. Use '?' for placeholders.
* @return Returns a reference to the newly created PreparedStatement
* object.
*/
PreparedStatementRef prepareStatement(const char16_t* sql);
/**
* Retrieves metadata information of the database.
*
* @return Returns a reference to the DatabaseMetaData object.
*/
DatabaseMetaDataRef getDatabaseMetaData();
/**
* Retrieves metadata information of the database.
*
* @return Returns a reference to the DatabaseMetaDataUnicode object.
*/
DatabaseMetaDataUnicodeRef getDatabaseMetaDataUnicode();
private:
EnvironmentRef parent_;
void* hdbc_;
bool connected_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,183 @@
#include <cstring>
#include <odbc/Connection.h>
#include <odbc/DatabaseMetaData.h>
#include <odbc/Exception.h>
#include <odbc/ResultSet.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
DatabaseMetaData::DatabaseMetaData(Connection* parent)
: DatabaseMetaDataBase(parent)
{
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getColumns(const char* catalogName,
const char* schemaName, const char* tableName, const char* columnName)
{
size_t catalogLen = catalogName ? strlen(catalogName) : 0;
size_t schemaLen = schemaName ? strlen(schemaName) : 0;
size_t tableLen = tableName ? strlen(tableName) : 0;
size_t columnLen = columnName ? strlen(columnName) : 0;
size_t maxLen = (1 << 8*sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (columnLen > maxLen)
throw Exception("The column name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLColumnsA, stmt->hstmt_,
(SQLCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLCHAR*)columnName, (SQLSMALLINT)columnLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getColumnPrivileges(const char* catalogName,
const char* schemaName, const char* tableName, const char* columnName)
{
size_t catalogLen = catalogName ? strlen(catalogName) : 0;
size_t schemaLen = schemaName ? strlen(schemaName) : 0;
size_t tableLen = tableName ? strlen(tableName) : 0;
size_t columnLen = columnName ? strlen(columnName) : 0;
size_t maxLen = (1 << 8 * sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (columnLen > maxLen)
throw Exception("The column name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLColumnPrivilegesA, stmt->hstmt_,
(SQLCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLCHAR*)columnName, (SQLSMALLINT)columnLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getPrimaryKeys(const char* catalogName,
const char* schemaName, const char* tableName)
{
size_t catalogLen = catalogName ? strlen(catalogName) : 0;
size_t schemaLen = schemaName ? strlen(schemaName) : 0;
size_t tableLen = tableName ? strlen(tableName) : 0;
size_t maxLen = (1 << 8*sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLPrimaryKeysA, stmt->hstmt_,
(SQLCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLCHAR*)tableName, (SQLSMALLINT)tableLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getTables(const char* catalogName,
const char* schemaName, const char* tableName, const char* tableType)
{
size_t catalogLen = catalogName ? strlen(catalogName) : 0;
size_t schemaLen = schemaName ? strlen(schemaName) : 0;
size_t tableLen = tableName ? strlen(tableName) : 0;
size_t tableTypeLen = tableType ? strlen(tableType) : 0;
size_t maxLen = (1 << 8 * sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (tableTypeLen > maxLen)
throw Exception("The table type is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLTablesA, stmt->hstmt_,
(SQLCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLCHAR*)tableType, (SQLSMALLINT)tableTypeLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getTypeInfo()
{
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLGetTypeInfoA, stmt->hstmt_, SQL_ALL_TYPES);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaData::getTypeInfo(int type)
{
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLGetTypeInfoA, stmt->hstmt_, type);
return ret;
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDataSourceName()
{
return getStringTypeInfoA(SQL_DATA_SOURCE_NAME);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDatabaseName()
{
return getStringTypeInfoA(SQL_DATABASE_NAME);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDBMSName()
{
return getStringTypeInfoA(SQL_DBMS_NAME);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDBMSVersion()
{
return getStringTypeInfoA(SQL_DBMS_VER);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDriverName()
{
return getStringTypeInfoA(SQL_DRIVER_NAME);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getDriverVersion()
{
return getStringTypeInfoA(SQL_DRIVER_VER);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getServerName()
{
return getStringTypeInfoA(SQL_SERVER_NAME);
}
//------------------------------------------------------------------------------
string DatabaseMetaData::getUserName()
{
return getStringTypeInfoA(SQL_USER_NAME);
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,268 @@
#ifndef ODBC_DATABASEMETADATA_H_INCLUDED
#define ODBC_DATABASEMETADATA_H_INCLUDED
//------------------------------------------------------------------------------
#include <string>
#include <odbc/Config.h>
#include <odbc/DatabaseMetaDataBase.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Provides information about the database.
*
* A DatabaseMetaData object can be created using
* Connection::getDatabaseMetaData().
*
* Most functions provide only a rudementary description of the data that is
* returned. Refer to the ODBC documentation and your ODBC driver documentation
* for further details.
*/
class ODBC_EXPORT DatabaseMetaData : public DatabaseMetaDataBase
{
friend class Connection;
private:
DatabaseMetaData(Connection* parent);
public:
/**
* Retrieves a list of columns in specified tables.
*
* The list of columns is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Column name
* 5. Data type
* 6. Type name
* 7. Column size
* 8. Buffer length
* 9. Decimal digits
* 10. Numeric radix
* 11. Nullability
* 12. Remarks
* 13. Default value (ODBC 3.0)
* 14. SQL data type (ODBC 3.0)
* 15. Subtype code for date, time and interval data types (ODBC 3.0)
* 16. Maximum length of bytes in a character or binary column type
* (ODBC 3.0)
* 17. The ordinal position of the column in the table (1-based)
* (ODBC 3.0)
* 18. Nullability as "NO" or "YES" string (ODBC 3.0)
*
* This function uses the ODBC function SQLColumns. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string search pattern for schema names.
* @param tableName A string search pattern for table names.
* @param columnName A string search pattern for column names.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getColumns(
const char* catalogName,
const char* schemaName,
const char* tableName,
const char* columnName);
/**
* Retrieves a list of columns and associated privileges for the specified
* table.
*
* The list of columns is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Column name
* 5. Grantor
* 6. Grantee
* 7. Privilege
* 8. Grantable
*
* This function uses the ODBC function SQLColumnPrivileges. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string indicating the schema name.
* @param tableName A string indicating the table name.
* @param columnName A string search pattern for column names.
* @return Returns a ResultSet object containing the requested
* information about privileges.
*/
ResultSetRef getColumnPrivileges(
const char* catalogName,
const char* schemaName,
const char* tableName,
const char* columnName);
/**
* Retrieves a list of primary keys in the specified table.
*
* The list of primary keys is returned as a ResultSet object, in which
* each returned row has the following columns:
* 1. Table catalog name
* 2. Table schema name
* 3. Table name
* 4. Primary key column name
* 5. Primary column sequence number in key (1-based)
* 6. Primary key name
*
* This functions uses the ODBC function SQLPrimaryKeys. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string indicating the schema name.
* @param tableName A string indicating the table name.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getPrimaryKeys(
const char* catalogName,
const char* schemaName,
const char* tableName);
/**
* Retrieves a description of the tables that are available in the connected
* database.
*
* The list of tables is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Table type
* 5. Remarks
*
* This function uses the ODBC function SQLTables. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string search pattern for schema names.
* @param tableName A string search pattern for table names.
* @param tableType A list of table types to be searched. The list must
* be empty or must contain a list of a comma-separated
* values. These values are "TABLE", "VIEW",
* "SYSTEM TABLE", "GLOBAL TEMPORARY",
* "LOCAL TEMPORARY", "ALIAS", "SYNONYM", or a data
* source-specific type name.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getTables(
const char* catalogName,
const char* schemaName,
const char* tableName,
const char* tableType);
/**
* Retrieves information about all data types.
*
* The information is returned as a ResultSet object, in which each returned
* row has the following columns:
* 1. Type name
* 2. Data type
* 3. Maximum column size for the data type.
* 4. Characters used to prefix a literal of that data type.
* 5. Characters used to suffix a literal of that data type.
* 6. A list of keywords, separated by commas, corresponding to each
* parameter that the application may specify in parentheses when
* using the name that is returned in field 1.
* 7. Nullability of the type.
* 8. Case-sensitiveness.
* 9. Searchability.
* 10. Unsignedness.
* 11. Flag indicating if the type has a predefined fixed precision and
* scale.
* 12. Auto-incrementing flag.
* 13. Localized type name.
* 14. Minimum scale.
* 15. Maximum scale.
* 16. SQL data type code.
* 17. Date/time subcode.
* 18. The radix used by a numeric type.
* 19. Interval leading precision.
*
* This function uses the ODBC function SQLGetTypeInfo. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @return Returns a ResultSet object containing the requested data
* type information.
*/
ResultSetRef getTypeInfo();
/**
* Retrieves information about a specific data type.
*
* See the documentation of getTypeInfo() for further details.
*
* @param type The data type to retrieve the type information of.
* @return Returns a ResultSet object containing the requested data
* type information.
*/
ResultSetRef getTypeInfo(int type);
/**
* Retrieves the name of the data source.
*
* @return Returns the name of the data source.
*/
std::string getDataSourceName();
/**
* Retrieves the current database in use.
*
* @return Returns the current database in use.
*/
std::string getDatabaseName();
/**
* Retrieves the name of the DBMS system.
*
* @return Returns the name of the DBMS system.
*/
std::string getDBMSName();
/**
* Retrieves the version of the DBMS system.
*
* @return Returns the version of the DBMS system.
*/
std::string getDBMSVersion();
/**
* Retrieves the name of the ODBC driver.
*
* @return Returns the name of the ODBC driver.
*/
std::string getDriverName();
/**
* Retrieves the version of the ODBC driver.
*
* @return Returns the version of the ODBC driver.
*/
std::string getDriverVersion();
/**
* Retrieves the server name.
*
* @return Returns the server name.
*/
std::string getServerName();
/**
* Retrieves the name used in the database.
*
* @return Returns the name used in the database.
*/
std::string getUserName();
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,126 @@
#include <odbc/Connection.h>
#include <odbc/DatabaseMetaDataBase.h>
#include <odbc/Exception.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
DatabaseMetaDataBase::DatabaseMetaDataBase(Connection* parent)
: parent_(parent, true)
{
}
//------------------------------------------------------------------------------
unsigned short DatabaseMetaDataBase::getMaxConnections()
{
return getUSmallIntTypeInfo(SQL_MAX_DRIVER_CONNECTIONS);
}
//------------------------------------------------------------------------------
unsigned long DatabaseMetaDataBase::getMaxStatementLength()
{
return getUIntTypeInfo(SQL_MAX_STATEMENT_LEN);
}
//------------------------------------------------------------------------------
bool DatabaseMetaDataBase::isReadOnly()
{
return (getStringTypeInfoA(SQL_DATA_SOURCE_READ_ONLY) == "Y");
}
//------------------------------------------------------------------------------
bool DatabaseMetaDataBase::supportsAlterTableWithAddColumn()
{
SQLUINTEGER val = getUIntTypeInfo(SQL_ALTER_TABLE);
return IS_FLAG_SET(val, SQL_AT_ADD_COLUMN_COLLATION) ||
IS_FLAG_SET(val, SQL_AT_ADD_COLUMN_DEFAULT) ||
IS_FLAG_SET(val, SQL_AT_ADD_COLUMN_SINGLE);
}
//------------------------------------------------------------------------------
bool DatabaseMetaDataBase::supportsAlterTableWithDropColumn()
{
SQLUINTEGER val = getUIntTypeInfo(SQL_ALTER_TABLE);
return IS_FLAG_SET(val, SQL_AT_DROP_COLUMN_CASCADE) ||
IS_FLAG_SET(val, SQL_AT_DROP_COLUMN_DEFAULT) ||
IS_FLAG_SET(val, SQL_AT_DROP_COLUMN_RESTRICT);
}
//------------------------------------------------------------------------------
TransactionIsolationLevel DatabaseMetaDataBase::getDefaultTransactionIsolation()
{
SQLUINTEGER txn = getUIntTypeInfo(SQL_DEFAULT_TXN_ISOLATION);
switch (txn)
{
case SQL_TXN_READ_COMMITTED:
return TransactionIsolationLevel::READ_COMMITTED;
case SQL_TXN_READ_UNCOMMITTED:
return TransactionIsolationLevel::READ_UNCOMMITTED;
case SQL_TXN_REPEATABLE_READ:
return TransactionIsolationLevel::REPEATABLE_READ;
case SQL_TXN_SERIALIZABLE:
return TransactionIsolationLevel::SERIALIZABLE;
case 0:
return TransactionIsolationLevel::NONE;
default:
throw Exception("Unknown transaction isolation level.");
}
}
//------------------------------------------------------------------------------
bool DatabaseMetaDataBase::supportsTransactionIsolation(
TransactionIsolationLevel level)
{
SQLUINTEGER txn = getUIntTypeInfo(SQL_TXN_ISOLATION_OPTION);
switch (level)
{
case TransactionIsolationLevel::READ_COMMITTED:
return IS_FLAG_SET(txn, SQL_TXN_READ_COMMITTED);
case TransactionIsolationLevel::READ_UNCOMMITTED:
return IS_FLAG_SET(txn, SQL_TXN_READ_UNCOMMITTED);
case TransactionIsolationLevel::REPEATABLE_READ:
return IS_FLAG_SET(txn, SQL_TXN_REPEATABLE_READ);
case TransactionIsolationLevel::SERIALIZABLE:
return IS_FLAG_SET(txn, SQL_TXN_SERIALIZABLE);
default:
return false;
}
}
//------------------------------------------------------------------------------
StatementRef DatabaseMetaDataBase::createStatement()
{
return parent_->createStatement();
}
//------------------------------------------------------------------------------
string DatabaseMetaDataBase::getStringTypeInfoA(unsigned short typeInfo)
{
vector<char> buffer;
buffer.resize(256);
while (true)
{
SQLPOINTER ptr = &buffer[0];
SQLSMALLINT bufLen = (SQLSMALLINT)buffer.size();
SQLSMALLINT dataLen;
EXEC_DBC(SQLGetInfoA, parent_->hdbc_, typeInfo, ptr, bufLen, &dataLen);
if (dataLen < bufLen)
break;
buffer.resize(dataLen + 1);
}
return string(&buffer[0]);
}
//------------------------------------------------------------------------------
unsigned long DatabaseMetaDataBase::getUIntTypeInfo(unsigned short typeInfo)
{
SQLUINTEGER ret;
SQLSMALLINT len;
EXEC_DBC(SQLGetInfo, parent_->hdbc_, typeInfo, &ret, sizeof(ret), &len);
return ret;
}
//------------------------------------------------------------------------------
unsigned short DatabaseMetaDataBase::getUSmallIntTypeInfo(
unsigned short typeInfo)
{
SQLUSMALLINT ret;
SQLSMALLINT len;
EXEC_DBC(SQLGetInfo, parent_->hdbc_, typeInfo, &ret, sizeof(ret), &len);
return ret;
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,93 @@
#ifndef ODBC_DATABASEMETADATABASE_H_INCLUDED
#define ODBC_DATABASEMETADATABASE_H_INCLUDED
//------------------------------------------------------------------------------
#include <string>
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Base class for DatabaseMetaData and DatabaseMetaDataUnicode.
*/
class ODBC_EXPORT DatabaseMetaDataBase : public RefCounted
{
protected:
DatabaseMetaDataBase(Connection* parent);
public:
/**
* Retrieves the maximum number of active connections the driver can
* support.
*
* @return Returns the maximum number of active connections the driver can
* support. If there is no limit or if the limit is not known,
* the function returns 0.
*/
unsigned short getMaxConnections();
/**
* Retrieves the maximum length of a statement string.
*
* @return Returns the maximum length of a statement string. If there is no
* limit or if the limit is not known, the function returns 0.
*/
unsigned long getMaxStatementLength();
/**
* Checks whether the database is in read-only mode.
*
* @return Returns true if the database is in read-only mode, false
* otherwise.
*/
bool isReadOnly();
/**
* Checks whether the database supports adding columns to existing tables.
*
* @returns Returns true if the database supports adding columns to
* existing tables, false otherwise.
*/
bool supportsAlterTableWithAddColumn();
/**
* Checks whether the database supports dropping columns from existing
* tables.
*
* @returns Returns true if the database supports dropping columns to
* existing tables, false otherwise.
*/
bool supportsAlterTableWithDropColumn();
/**
* Retrieves the default transaction isolation level of the database.
*
* @return Returns the default transaction isolation level.
*/
TransactionIsolationLevel getDefaultTransactionIsolation();
/**
* Checks whether the database supports the given transaction isolation
* level or not.
*
* @return Returns true if the given transaction isolation level is
* supported, otherwise false.
*/
bool supportsTransactionIsolation(TransactionIsolationLevel level);
protected:
StatementRef createStatement();
std::string getStringTypeInfoA(unsigned short typeInfo);
private:
unsigned long getUIntTypeInfo(unsigned short typeInfo);
unsigned short getUSmallIntTypeInfo(unsigned short typeInfo);
protected:
ConnectionRef parent_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,213 @@
#include <string>
#include <odbc/Connection.h>
#include <odbc/DatabaseMetaDataUnicode.h>
#include <odbc/Exception.h>
#include <odbc/ResultSet.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------------
size_t strlen16(const char16_t* str)
{
return char_traits<char16_t>::length(str);
}
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
DatabaseMetaDataUnicode::DatabaseMetaDataUnicode(Connection* parent)
: DatabaseMetaDataBase(parent)
{
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getColumns(const char16_t* catalogName,
const char16_t* schemaName, const char16_t* tableName,
const char16_t* columnName)
{
size_t catalogLen = catalogName ? strlen16(catalogName) : 0;
size_t schemaLen = schemaName ? strlen16(schemaName) : 0;
size_t tableLen = tableName ? strlen16(tableName) : 0;
size_t columnLen = columnName ? strlen16(columnName) : 0;
size_t maxLen = (1 << 8*sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (columnLen > maxLen)
throw Exception("The column name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLColumnsW, stmt->hstmt_,
(SQLWCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLWCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLWCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLWCHAR*)columnName, (SQLSMALLINT)columnLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getColumnPrivileges(
const char16_t* catalogName, const char16_t* schemaName,
const char16_t* tableName, const char16_t* columnName)
{
size_t catalogLen = catalogName ? strlen16(catalogName) : 0;
size_t schemaLen = schemaName ? strlen16(schemaName) : 0;
size_t tableLen = tableName ? strlen16(tableName) : 0;
size_t columnLen = columnName ? strlen16(columnName) : 0;
size_t maxLen = (1 << 8 * sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (columnLen > maxLen)
throw Exception("The column name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLColumnPrivilegesW, stmt->hstmt_,
(SQLWCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLWCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLWCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLWCHAR*)columnName, (SQLSMALLINT)columnLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getPrimaryKeys(
const char16_t* catalogName, const char16_t* schemaName,
const char16_t* tableName)
{
size_t catalogLen = catalogName ? strlen16(catalogName) : 0;
size_t schemaLen = schemaName ? strlen16(schemaName) : 0;
size_t tableLen = tableName ? strlen16(tableName) : 0;
size_t maxLen = (1 << 8*sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLPrimaryKeysW, stmt->hstmt_,
(SQLWCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLWCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLWCHAR*)tableName, (SQLSMALLINT)tableLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getTables(const char16_t* catalogName,
const char16_t* schemaName, const char16_t* tableName,
const char16_t* tableType)
{
size_t catalogLen = catalogName ? strlen16(catalogName) : 0;
size_t schemaLen = schemaName ? strlen16(schemaName) : 0;
size_t tableLen = tableName ? strlen16(tableName) : 0;
size_t tableTypeLen = tableType ? strlen16(tableType) : 0;
size_t maxLen = (1 << 8 * sizeof(SQLSMALLINT)) - 1;
if (catalogLen > maxLen)
throw Exception("The catalog name is too long");
if (schemaLen > maxLen)
throw Exception("The schema name is too long");
if (tableLen > maxLen)
throw Exception("The table name is too long");
if (tableTypeLen > maxLen)
throw Exception("The table type is too long");
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLTablesW, stmt->hstmt_,
(SQLWCHAR*)catalogName, (SQLSMALLINT)catalogLen,
(SQLWCHAR*)schemaName, (SQLSMALLINT)schemaLen,
(SQLWCHAR*)tableName, (SQLSMALLINT)tableLen,
(SQLWCHAR*)tableType, (SQLSMALLINT)tableTypeLen);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getTypeInfo()
{
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLGetTypeInfoW, stmt->hstmt_, SQL_ALL_TYPES);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef DatabaseMetaDataUnicode::getTypeInfo(int type)
{
StatementRef stmt = createStatement();
ResultSetRef ret(new ResultSet(stmt.get()));
EXEC_STMT(SQLGetTypeInfoW, stmt->hstmt_, type);
return ret;
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDataSourceName()
{
return getStringTypeInfoW(SQL_DATA_SOURCE_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDatabaseName()
{
return getStringTypeInfoW(SQL_DATABASE_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDBMSName()
{
return getStringTypeInfoW(SQL_DBMS_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDBMSVersion()
{
return getStringTypeInfoW(SQL_DBMS_VER);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDriverName()
{
return getStringTypeInfoW(SQL_DRIVER_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getDriverVersion()
{
return getStringTypeInfoW(SQL_DRIVER_VER);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getServerName()
{
return getStringTypeInfoW(SQL_SERVER_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getUserName()
{
return getStringTypeInfoW(SQL_USER_NAME);
}
//------------------------------------------------------------------------------
u16string DatabaseMetaDataUnicode::getStringTypeInfoW(unsigned short typeInfo)
{
vector<char16_t> buffer;
buffer.resize(256);
while (true)
{
SQLPOINTER ptr = &buffer[0];
SQLSMALLINT bufLen = (SQLSMALLINT)(buffer.size() * sizeof(char16_t));
SQLSMALLINT dataLen;
EXEC_DBC(SQLGetInfoW, parent_->hdbc_, typeInfo, ptr, bufLen, &dataLen);
if (dataLen < bufLen)
break;
buffer.resize(dataLen / sizeof(char16_t) + 1);
}
return u16string(&buffer[0]);
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,271 @@
#ifndef ODBC_DATABASEMETADATAUNICODE_H_INCLUDED
#define ODBC_DATABASEMETADATAUNICODE_H_INCLUDED
//------------------------------------------------------------------------------
#include <string>
#include <odbc/Config.h>
#include <odbc/DatabaseMetaDataBase.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Provides information about the database.
*
* A DatabaseMetaDataUnicode object can be created using
* Connection::getDatabaseMetaDataUnicode().
*
* Most functions provide only a rudementary description of the data that is
* returned. Refer to the ODBC documentation and your ODBC driver documentation
* for further details.
*/
class ODBC_EXPORT DatabaseMetaDataUnicode : public DatabaseMetaDataBase
{
friend class Connection;
private:
DatabaseMetaDataUnicode(Connection* parent);
public:
/**
* Retrieves a list of columns in specified tables.
*
* The list of columns is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Column name
* 5. Data type
* 6. Type name
* 7. Column size
* 8. Buffer length
* 9. Decimal digits
* 10. Numeric radix
* 11. Nullability
* 12. Remarks
* 13. Default value (ODBC 3.0)
* 14. SQL data type (ODBC 3.0)
* 15. Subtype code for date, time and interval data types (ODBC 3.0)
* 16. Maximum length of bytes in a character or binary column type
* (ODBC 3.0)
* 17. The ordinal position of the column in the table (1-based)
* (ODBC 3.0)
* 18. Nullability as "NO" or "YES" string (ODBC 3.0)
*
* This function uses the ODBC function SQLColumns. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string search pattern for schema names.
* @param tableName A string search pattern for table names.
* @param columnName A string search pattern for column names.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getColumns(
const char16_t* catalogName,
const char16_t* schemaName,
const char16_t* tableName,
const char16_t* columnName);
/**
* Retrieves a list of columns and associated privileges for the specified
* table.
*
* The list of columns is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Column name
* 5. Grantor
* 6. Grantee
* 7. Privilege
* 8. Grantable
*
* This function uses the ODBC function SQLColumnPrivileges. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string indicating the schema name.
* @param tableName A string indicating the table name.
* @param columnName A string search pattern for column names.
* @return Returns a ResultSet object containing the requested
* information about privileges.
*/
ResultSetRef getColumnPrivileges(
const char16_t* catalogName,
const char16_t* schemaName,
const char16_t* tableName,
const char16_t* columnName);
/**
* Retrieves a list of primary keys in the specified table.
*
* The list of primary keys is returned as a ResultSet object, in which
* each returned row has the following columns:
* 1. Table catalog name
* 2. Table schema name
* 3. Table name
* 4. Primary key column name
* 5. Primary column sequence number in key (1-based)
* 6. Primary key name
*
* This functions uses the ODBC function SQLPrimaryKeys. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string indicating the schema name.
* @param tableName A string indicating the table name.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getPrimaryKeys(
const char16_t* catalogName,
const char16_t* schemaName,
const char16_t* tableName);
/**
* Retrieves a description of the tables that are available in the connected
* database.
*
* The list of tables is returned as a ResultSet object, in which each
* returned row has the following columns:
* 1. Catalog name
* 2. Schema name
* 3. Table name
* 4. Table type
* 5. Remarks
*
* This function uses the ODBC function SQLTables. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @param catalogName A string indicating the catalog name.
* @param schemaName A string search pattern for schema names.
* @param tableName A string search pattern for table names.
* @param tableType A list of table types to be searched. The list must
* be empty or must contain a list of a comma-separated
* values. These values are "TABLE", "VIEW",
* "SYSTEM TABLE", "GLOBAL TEMPORARY",
* "LOCAL TEMPORARY", "ALIAS", "SYNONYM", or a data
* source-specific type name.
* @return Returns a ResultSet object containing the requested
* table description.
*/
ResultSetRef getTables(
const char16_t* catalogName,
const char16_t* schemaName,
const char16_t* tableName,
const char16_t* tableType);
/**
* Retrieves information about all data types.
*
* The information is returned as a ResultSet object, in which each returned
* row has the following columns:
* 1. Type name
* 2. Data type
* 3. Maximum column size for the data type.
* 4. Characters used to prefix a literal of that data type.
* 5. Characters used to suffix a literal of that data type.
* 6. A list of keywords, separated by commas, corresponding to each
* parameter that the application may specify in parentheses when
* using the name that is returned in field 1.
* 7. Nullability of the type.
* 8. Case-sensitiveness.
* 9. Searchability.
* 10. Unsignedness.
* 11. Flag indicating if the type has a predefined fixed precision and
* scale.
* 12. Auto-incrementing flag.
* 13. Localized type name.
* 14. Minimum scale.
* 15. Maximum scale.
* 16. SQL data type code.
* 17. Date/time subcode.
* 18. The radix used by a numeric type.
* 19. Interval leading precision.
*
* This function uses the ODBC function SQLGetTypeInfo. Refer to its
* documentation for further details on the data in the ResultSet object.
*
* @return Returns a ResultSet object containing the requested data
* type information.
*/
ResultSetRef getTypeInfo();
/**
* Retrieves information about a specific data type.
*
* See the documentation of getTypeInfo() for further details.
*
* @param type The data type to retrieve the type information of.
* @return Returns a ResultSet object containing the requested data
* type information.
*/
ResultSetRef getTypeInfo(int type);
/**
* Retrieves the name of the data source.
*
* @return Returns the name of the data source.
*/
std::u16string getDataSourceName();
/**
* Retrieves the current database in use.
*
* @return Returns the current database in use.
*/
std::u16string getDatabaseName();
/**
* Retrieves the name of the DBMS system.
*
* @return Returns the name of the DBMS system.
*/
std::u16string getDBMSName();
/**
* Retrieves the version of the DBMS system.
*
* @return Returns the version of the DBMS system.
*/
std::u16string getDBMSVersion();
/**
* Retrieves the name of the ODBC driver.
*
* @return Returns the name of the ODBC driver.
*/
std::u16string getDriverName();
/**
* Retrieves the version of the ODBC driver.
*
* @return Returns the version of the ODBC driver.
*/
std::u16string getDriverVersion();
/**
* Retrieves the server name.
*
* @return Returns the server name.
*/
std::u16string getServerName();
/**
* Retrieves the name used in the database.
*
* @return Returns the name used in the database.
*/
std::u16string getUserName();
private:
std::u16string getStringTypeInfoW(unsigned short typeInfo);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,194 @@
#include <cstring>
#include <odbc/Connection.h>
#include <odbc/Environment.h>
#include <odbc/Exception.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
EnvironmentRef Environment::create()
{
return EnvironmentRef(new Environment);
}
//------------------------------------------------------------------------------
Environment::Environment()
{
SQLRETURN rc;
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv_);
if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
throw Exception("Could not allocate environment");
EXEC_ENV(SQLSetEnvAttr, henv_, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, 0);
}
//------------------------------------------------------------------------------
Environment::~Environment()
{
SQLFreeHandle(SQL_HANDLE_ENV, henv_);
}
//------------------------------------------------------------------------------
ConnectionRef Environment::createConnection()
{
SQLHANDLE hdbc;
ConnectionRef ret(new Connection(this));
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_DBC, henv_, &hdbc);
Exception::checkForError(rc, SQL_HANDLE_ENV, henv_);
ret->setHandle(hdbc);
return ret;
}
//------------------------------------------------------------------------------
vector<DataSourceInformation> Environment::getDataSources()
{
return getDataSources(DSNType::ALL);
}
//------------------------------------------------------------------------------
vector<DataSourceInformation> Environment::getDataSources(DSNType dsnType)
{
vector<DataSourceInformation> ret;
SQLCHAR nameBuf[SQL_MAX_DSN_LENGTH + 1];
vector<SQLCHAR> descBuf;
descBuf.resize(256);
SQLSMALLINT nameLen;
SQLSMALLINT descLen;
SQLUSMALLINT direction;
switch (dsnType)
{
case DSNType::ALL:
direction = SQL_FETCH_FIRST;
break;
case DSNType::SYSTEM:
direction = SQL_FETCH_FIRST_SYSTEM;
break;
case DSNType::USER:
direction = SQL_FETCH_FIRST_USER;
break;
default:
ODBC_FAIL("Unknown DSN type.");
}
for (;;)
{
for (;;)
{
SQLRETURN rc = SQLDataSourcesA(henv_, direction,
nameBuf, sizeof(nameBuf), &nameLen,
descBuf.data(), (SQLSMALLINT)descBuf.size(), &descLen);
if (rc == SQL_NO_DATA)
return ret;
Exception::checkForError(rc, SQL_HANDLE_ENV, henv_);
if (descLen < (SQLSMALLINT)descBuf.size())
break;
descBuf.resize(descLen + 1);
}
ret.push_back({ string((const char*)nameBuf, nameLen),
string((const char*)descBuf.data(), descLen) });
direction = SQL_FETCH_NEXT;
}
return ret;
}
//------------------------------------------------------------------------------
vector<DriverInformation> Environment::getDrivers()
{
vector<DriverInformation> ret;
vector<SQLCHAR> descBuf;
descBuf.resize(256);
vector<SQLCHAR> attrBuf;
attrBuf.resize(256);
SQLSMALLINT descLen;
SQLSMALLINT attrLen;
SQLUSMALLINT direction = SQL_FETCH_FIRST;
for (;;)
{
for (;;)
{
SQLRETURN rc = SQLDriversA(henv_, direction,
descBuf.data(), (SQLSMALLINT)descBuf.size(), &descLen,
attrBuf.data(), (SQLSMALLINT)attrBuf.size(), &attrLen);
if (rc == SQL_NO_DATA)
return ret;
Exception::checkForError(rc, SQL_HANDLE_ENV, henv_);
if (descLen < (SQLSMALLINT)descBuf.size() &&
attrLen < (SQLSMALLINT)attrBuf.size())
break;
if (descLen >= (SQLSMALLINT)descBuf.size())
descBuf.resize(descLen + 1);
if (attrLen >= (SQLSMALLINT)attrBuf.size())
attrBuf.resize(attrLen + 1);
}
DriverInformation driverInfo;
driverInfo.description = string((const char*)descBuf.data(), descLen);
if (attrLen > 0)
{
// Parse keyword-value pairs given in the following format
// FileUsage=1\0FileExtns=*.dbf\0\0
const char* start = (const char*)attrBuf.data();
size_t attrBufLen = (size_t)attrLen;
size_t totalLen = 0;
while (totalLen < attrBufLen)
{
const char* end = strchr(start, '\0');
if (end == nullptr)
throw Exception("Unable to parse driver attribute value.");
size_t len = end - start;
const char* sep = strchr(start, '=');
if (sep == nullptr)
throw Exception("Unable to parse driver attribute value.");
size_t pos = sep - start;
driverInfo.attributes.push_back(
{ string(start, pos),
string(start + pos + 1, len - pos - 1) });
totalLen += len + 1;
start = end + 1;
}
}
ret.push_back(move(driverInfo));
direction = SQL_FETCH_NEXT;
}
return ret;
}
//------------------------------------------------------------------------------
bool Environment::isDriverInstalled(const char* name)
{
vector<SQLCHAR> descBuf;
descBuf.resize(256);
SQLSMALLINT descLen;
SQLSMALLINT attrLen;
SQLUSMALLINT direction = SQL_FETCH_FIRST;
for (;;)
{
for (;;)
{
SQLRETURN rc = SQLDriversA(henv_, direction,
descBuf.data(), (SQLSMALLINT)descBuf.size(), &descLen,
nullptr, 0, &attrLen);
if (rc == SQL_NO_DATA)
return false;
Exception::checkForError(rc, SQL_HANDLE_ENV, henv_);
if (descLen < (SQLSMALLINT)descBuf.size())
break;
descBuf.resize(descLen + 1);
}
if (strcmp(name, (const char*)descBuf.data()) == 0)
return true;
direction = SQL_FETCH_NEXT;
}
return false;
}
//------------------------------------------------------------------------------
} // namespace odbc

132
external/odbccpp/src/odbc/Environment.h vendored Normal file
View File

@ -0,0 +1,132 @@
#ifndef ODBC_ENVIRONMENT_H_INCLUDED
#define ODBC_ENVIRONMENT_H_INCLUDED
//------------------------------------------------------------------------------
#include <string>
#include <vector>
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
struct DataSourceInformation;
struct DriverInformation;
//------------------------------------------------------------------------------
/**
* ODBC Environment Wrapper.
*
* Represents an environment context that manages connection creations.
*/
class ODBC_EXPORT Environment : public RefCounted
{
public:
/**
* Creates a new Environment object.
*
* @return Returns a reference to the newly created Environment object.
*/
static EnvironmentRef create();
private:
Environment();
~Environment();
public:
/**
* Creates a new Connection object.
*
* @return Returns a reference to the newly created Connection object.
*/
ConnectionRef createConnection();
/**
* Retrieves information about available data sources.
*
* @return Returns a full list of DataSourceInformation objects.
*/
std::vector<DataSourceInformation> getDataSources();
/**
* Retrieves information about available data sources of the specified type.
*
* @param dsnType Specifies the type of the returned ODBC DSNs.
* @return Returns a list of DataSourceInformation objects.
*/
std::vector<DataSourceInformation> getDataSources(DSNType dsnType);
/**
* Retrieves information about available drivers.
*
* @return Returns a list of DriverInformation objects.
*/
std::vector<DriverInformation> getDrivers();
/**
* Gets a value indicating whether the given driver is installed or not.
*
* @param name The driver name.
* @return Returns true if the driver is installed, otherwise false.
*/
bool isDriverInstalled(const char* name);
private:
void* henv_;
};
//------------------------------------------------------------------------------
/**
* Stores the name and description of a data source.
*/
struct DataSourceInformation
{
/**
* The data source name.
*
* For example, dBASE Files or SQL Server.
*/
std::string name;
/**
* The description of the driver associated with the data source.
*
* For example, Microsoft Access dBASE Driver (*.dbf, *.ndx, *.mdx).
*/
std::string description;
};
//------------------------------------------------------------------------------
/**
* Stores the description and additional information about the driver.
*/
struct DriverInformation
{
/**
* Stores the name and the value of a driver attribute.
*
* For example, DriverODBCVer=03.51.
*/
struct Attribute
{
/**
* The attribute's name.
*/
std::string name;
/**
* The attribute's value.
*/
std::string value;
};
/**
* The driver description.
*
* For example, PostgreSQL Unicode(x64) or SQL Server.
*/
std::string description;
/**
* The list of driver attributes.
*/
std::vector<Attribute> attributes;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

83
external/odbccpp/src/odbc/Exception.cpp vendored Normal file
View File

@ -0,0 +1,83 @@
#include <sstream>
#include <odbc/Exception.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------------
bool appendRecord(short handleType, void* handle, SQLSMALLINT recNumber,
ostringstream& out)
{
SQLCHAR sqlState[6];
SQLINTEGER nativeError;
SQLCHAR messageText[2048];
SQLSMALLINT textLength;
SQLRETURN rc = SQLGetDiagRecA(handleType, handle, recNumber, sqlState,
&nativeError, messageText, sizeof(messageText)/sizeof(SQLCHAR),
&textLength);
switch (rc)
{
case SQL_SUCCESS:
case SQL_SUCCESS_WITH_INFO:
if (recNumber > 1)
out << endl;
out << "ERROR: " << nativeError << ": " << sqlState << " : "
<< messageText << endl;
return true;
case SQL_ERROR:
if (recNumber > 1)
out << endl;
out << "An error occurred while calling SQLGetDiagRec" << endl;
return false;
case SQL_INVALID_HANDLE:
if (recNumber > 1)
out << endl;
out << "The handle passed to SQLGetDiagRec is not valid" << endl;
return false;
case SQL_NO_DATA:
return false;
default:
if (recNumber > 1)
out << endl;
out << "An unknown return code was returned by SQLGetDiagRec" << endl;
return false;
}
}
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
namespace odbc
{
//------------------------------------------------------------------------------
void Exception::checkForError(short rc, short handleType, void* handle)
{
if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
throw create(handleType, handle);
}
//------------------------------------------------------------------------------
Exception Exception::create(short handleType, void* handle)
{
ostringstream out;
SQLSMALLINT recNumber = 1;
while (appendRecord(handleType, handle, recNumber, out))
{
++recNumber;
}
return Exception(out.str());
}
//------------------------------------------------------------------------------
Exception::Exception(const char* s) : msg_(s)
{
}
//------------------------------------------------------------------------------
Exception::Exception(const std::string& s) : msg_(s)
{
}
//------------------------------------------------------------------------------
const char* Exception::what() const noexcept
{
return msg_.c_str();
}
//------------------------------------------------------------------------------
} // namespace odbc

59
external/odbccpp/src/odbc/Exception.h vendored Normal file
View File

@ -0,0 +1,59 @@
#ifndef ODBC_EXCEPTION_H_INCLUDED
#define ODBC_EXCEPTION_H_INCLUDED
//------------------------------------------------------------------------------
#include <stdexcept>
#include <string>
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* ODBC-related exception.
*/
class ODBC_EXPORT Exception : public std::exception
{
private:
friend class Batch;
friend class Connection;
friend class DatabaseMetaData;
friend class DatabaseMetaDataBase;
friend class DatabaseMetaDataUnicode;
friend class date;
friend class decimal;
friend class Environment;
friend class ParameterMetaData;
friend class PreparedStatement;
friend class ResultSet;
friend class ResultSetMetaData;
friend class ResultSetMetaDataBase;
friend class ResultSetMetaDataUnicode;
friend class Statement;
friend class StatementBase;
friend class time;
friend class timestamp;
friend class ValueBuffer;
friend class Util;
static void checkForError(short rc, short handleType, void* handle);
static Exception create(short handleType, void* handle);
Exception(const char* s);
Exception(const std::string& s);
public:
/**
* Describes the reason of the exception.
*
* @return Returns a description of the error that occurred.
*/
virtual const char* what() const noexcept;
private:
std::string msg_;
};
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
#endif

39
external/odbccpp/src/odbc/Forwards.h vendored Normal file
View File

@ -0,0 +1,39 @@
#ifndef ODBC_FORWARDS_H_INCLUDED
#define ODBC_FORWARDS_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/RefCounted.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
class Connection;
class DatabaseMetaData;
class DatabaseMetaDataBase;
class DatabaseMetaDataUnicode;
class Environment;
class Exception;
class ParameterMetaData;
class PreparedStatement;
class ResultSet;
class ResultSetMetaData;
class ResultSetMetaDataBase;
class ResultSetMetaDataUnicode;
class StatementBase;
class Statement;
class ValueBuffer;
//------------------------------------------------------------------------------
typedef Reference<Connection> ConnectionRef;
typedef Reference<DatabaseMetaData> DatabaseMetaDataRef;
typedef Reference<DatabaseMetaDataUnicode> DatabaseMetaDataUnicodeRef;
typedef Reference<Environment> EnvironmentRef;
typedef Reference<ParameterMetaData> ParameterMetaDataRef;
typedef Reference<PreparedStatement> PreparedStatementRef;
typedef Reference<ResultSet> ResultSetRef;
typedef Reference<ResultSetMetaData> ResultSetMetaDataRef;
typedef Reference<ResultSetMetaDataUnicode> ResultSetMetaDataUnicodeRef;
typedef Reference<StatementBase> StatementBaseRef;
typedef Reference<Statement> StatementRef;
typedef Reference<ValueBuffer> ValueBufferRef;
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,76 @@
#include <odbc/Exception.h>
#include <odbc/ParameterMetaData.h>
#include <odbc/PreparedStatement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ParameterMetaData::ParameterMetaData(PreparedStatement* ps)
: ps_(ps, true)
{
}
//------------------------------------------------------------------------------
unsigned short ParameterMetaData::getParameterCount()
{
SQLSMALLINT ret;
EXEC_STMT(SQLNumParams, ps_->hstmt_, &ret);
return ret;
}
//------------------------------------------------------------------------------
short ParameterMetaData::getParameterType(unsigned short paramIndex)
{
SQLSMALLINT dataType;
SQLULEN parameterSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
EXEC_STMT(SQLDescribeParam, ps_->hstmt_, paramIndex, &dataType,
&parameterSize, &decimalDigits, &nullable);
return dataType;
}
//------------------------------------------------------------------------------
size_t ParameterMetaData::getParameterSize(unsigned short paramIndex)
{
SQLSMALLINT dataType;
SQLULEN parameterSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
EXEC_STMT(SQLDescribeParam, ps_->hstmt_, paramIndex, &dataType,
&parameterSize, &decimalDigits, &nullable);
return (size_t)parameterSize;
}
//------------------------------------------------------------------------------
unsigned short ParameterMetaData::getPrecision(unsigned short paramIndex)
{
SQLSMALLINT dataType;
SQLULEN parameterSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
EXEC_STMT(SQLDescribeParam, ps_->hstmt_, paramIndex, &dataType,
&parameterSize, &decimalDigits, &nullable);
return (unsigned short)parameterSize;
}
//------------------------------------------------------------------------------
unsigned short ParameterMetaData::getScale(unsigned short paramIndex)
{
SQLSMALLINT dataType;
SQLULEN parameterSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
EXEC_STMT(SQLDescribeParam, ps_->hstmt_, paramIndex, &dataType,
&parameterSize, &decimalDigits, &nullable);
return (decimalDigits >= 0) ? decimalDigits : 0;
}
//------------------------------------------------------------------------------
bool ParameterMetaData::isNullable(unsigned short paramIndex)
{
SQLSMALLINT dataType;
SQLULEN parameterSize;
SQLSMALLINT decimalDigits;
SQLSMALLINT nullable;
EXEC_STMT(SQLDescribeParam, ps_->hstmt_, paramIndex, &dataType,
&parameterSize, &decimalDigits, &nullable);
return nullable == SQL_NULLABLE;
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,82 @@
#ifndef ODBC_PARAMETERMETADATA_H_INCLUDED
#define ODBC_PARAMETERMETADATA_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Metadata on parameters in a prepared statement.
*/
class ODBC_EXPORT ParameterMetaData : public RefCounted
{
friend class PreparedStatement;
private:
ParameterMetaData(PreparedStatement* ps);
public:
/**
* Retrieves the number of parameters in the associated PreparedStatement
* object.
*
* @return The number of parameters.
*/
unsigned short getParameterCount();
/**
* Retrieves the SQL type of the specified parameter.
*
* @param paramIndex The parameter index starting from 1.
* @return The SQL type.
*/
short getParameterType(unsigned short paramIndex);
/**
* Retrieves the size of the column or expression measured in characters.
*
* @param paramIndex The parameter index starting from 1.
* @return The size in characters.
*/
std::size_t getParameterSize(unsigned short paramIndex);
/**
* Retrieves the precision of the type assigned to the parameter.
*
* For numeric data, the returned value denotes the maximum number of digits
* in a number. For character data, this is the length of a character
* sequence. For date, time and timestamp types, this is the length of their
* string representation.
*
* @param paramIndex The parameter index starting from 1.
* @return The precision value.
*/
unsigned short getPrecision(unsigned short paramIndex);
/**
* Retrieves the scale of numeric data, which is the maximum number of
* decimal digits.
*
* The function returns 0 for non-numeric data.
*
* @param paramIndex The parameter index starting from 1.
* @return The scale value.
*/
unsigned short getScale(unsigned short paramIndex);
/**
* Determines whether the corresponding column accepts null values or not.
*
* @param paramIndex The parameter index starting from 1.
* @return True if the parameter accepts nulls, false otherwise.
*/
bool isNullable(unsigned short paramIndex);
private:
PreparedStatementRef ps_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,405 @@
#include <string>
#include <odbc/Connection.h>
#include <odbc/Exception.h>
#include <odbc/ParameterMetaData.h>
#include <odbc/PreparedStatement.h>
#include <odbc/ResultSet.h>
#include <odbc/ResultSetMetaData.h>
#include <odbc/ResultSetMetaDataUnicode.h>
#include <odbc/internal/Batch.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
#include <odbc/internal/ParameterData.h>
#include <odbc/internal/TypeInfo.h>
#include <odbc/internal/UtilInternal.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
PreparedStatement::PreparedStatement(Connection* parent) : StatementBase(parent)
{
}
//------------------------------------------------------------------------------
PreparedStatement::~PreparedStatement()
{
}
//------------------------------------------------------------------------------
void PreparedStatement::setHandleAndQuery(void* hstmt, const char* query)
{
hstmt_ = hstmt;
EXEC_STMT(SQLPrepareA, hstmt, (SQLCHAR*)query, SQL_NTS);
SQLSMALLINT count;
EXEC_STMT(SQLNumParams, hstmt_, &count);
parameterData_.resize(count);
batch_.reset(new Batch(parameterData_));
}
//------------------------------------------------------------------------------
void PreparedStatement::setHandleAndQuery(void* hstmt, const char16_t* query)
{
hstmt_ = hstmt;
EXEC_STMT(SQLPrepareW, hstmt, (SQLWCHAR*)query, SQL_NTS);
SQLSMALLINT count;
EXEC_STMT(SQLNumParams, hstmt_, &count);
parameterData_.resize(count);
batch_.reset(new Batch(parameterData_));
}
//------------------------------------------------------------------------------
ParameterMetaDataRef PreparedStatement::getParameterMetaData()
{
ParameterMetaDataRef ret(new ParameterMetaData(this));
return ret;
}
//------------------------------------------------------------------------------
void PreparedStatement::setBoolean(unsigned short paramIndex,
const Boolean& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setByte(unsigned short paramIndex, const Byte& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setUByte(unsigned short paramIndex, const UByte& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setShort(unsigned short paramIndex, const Short& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setUShort(unsigned short paramIndex,
const UShort& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setInt(unsigned short paramIndex, const Int& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setUInt(unsigned short paramIndex, const UInt& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setLong(unsigned short paramIndex, const Long& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setULong(unsigned short paramIndex, const ULong& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setDecimal(unsigned short paramIndex,
const Decimal& value)
{
if (value.isNull())
{
parameterData_[paramIndex - 1].setNull(TypeToOdbc<decimal>::VALUETYPE);
}
else
{
SQL_NUMERIC_STRUCT ns;
UtilInternal::decimalToNumeric(*value, ns);
ParameterData& pd = parameterData_[paramIndex - 1];
pd.setValue(TypeToOdbc<decimal>::VALUETYPE, &ns, sizeof(ns));
pd.setColumnSize(ns.precision);
pd.setDecimalDigits(ns.scale);
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setFloat(unsigned short paramIndex, const Float& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setDouble(unsigned short paramIndex,
const Double& value)
{
setFixedSizeData(paramIndex, value);
}
//------------------------------------------------------------------------------
void PreparedStatement::setString(unsigned short paramIndex,
const String& value)
{
if (value.isNull())
setCString(paramIndex, nullptr, 0);
else
setCString(paramIndex, value->c_str(), value->length());
}
//------------------------------------------------------------------------------
void PreparedStatement::setCString(unsigned short paramIndex, const char* s)
{
if (s == nullptr)
setCString(paramIndex, nullptr, 0);
else
setCString(paramIndex, s, char_traits<char>::length(s));
}
//------------------------------------------------------------------------------
void PreparedStatement::setCString(unsigned short paramIndex, const char* s,
std::size_t len)
{
verifyValidParamIndex(paramIndex);
ParameterData& pd = parameterData_[paramIndex - 1];
if (s == nullptr)
{
pd.setNull(SQL_C_CHAR);
}
else
{
pd.setValue(SQL_C_CHAR, s, len);
pd.setColumnSize(len);
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setNString(unsigned short paramIndex,
const NString& value)
{
if (value.isNull())
setNCString(paramIndex, nullptr, 0);
else
setNCString(paramIndex, value->c_str(), value->length());
}
//------------------------------------------------------------------------------
void PreparedStatement::setNCString(unsigned short paramIndex,
const char16_t* s)
{
if (s == nullptr)
setNCString(paramIndex, nullptr, 0);
else
setNCString(paramIndex, s, char_traits<char16_t>::length(s));
}
//------------------------------------------------------------------------------
void PreparedStatement::setNCString(unsigned short paramIndex,
const char16_t* s, std::size_t len)
{
verifyValidParamIndex(paramIndex);
ParameterData& pd = parameterData_[paramIndex - 1];
if (s == nullptr)
{
pd.setNull(SQL_C_WCHAR);
}
else
{
pd.setValue(SQL_C_WCHAR, s, len*sizeof(char16_t));
pd.setColumnSize(len);
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setBinary(unsigned short paramIndex,
const Binary& value)
{
if (value.isNull())
setBytes(paramIndex, nullptr, 0);
else
setBytes(paramIndex, value->data(), value->size());
}
//------------------------------------------------------------------------------
void PreparedStatement::setBytes(unsigned short paramIndex, const void* data,
size_t size)
{
verifyValidParamIndex(paramIndex);
ParameterData& pd = parameterData_[paramIndex - 1];
if (data == nullptr)
{
pd.setNull(SQL_C_BINARY);
}
else
{
pd.setValue(SQL_C_BINARY, data, size);
pd.setColumnSize(size);
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setDate(unsigned short paramIndex, const Date& value)
{
verifyValidParamIndex(paramIndex);
if (value.isNull())
{
parameterData_[paramIndex - 1].setNull(TypeToOdbc<date>::VALUETYPE);
}
else
{
SQL_DATE_STRUCT ds = {
(SQLSMALLINT)value->year(),
(SQLUSMALLINT)value->month(),
(SQLUSMALLINT)value->day()
};
parameterData_[paramIndex - 1].setValue(
TypeToOdbc<date>::VALUETYPE, &ds, sizeof(ds));
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setTime(unsigned short paramIndex, const Time& value)
{
verifyValidParamIndex(paramIndex);
if (value.isNull())
{
parameterData_[paramIndex - 1].setNull(TypeToOdbc<time>::VALUETYPE);
}
else
{
SQL_TIME_STRUCT ts = {
(SQLUSMALLINT)value->hour(),
(SQLUSMALLINT)value->minute(),
(SQLUSMALLINT)value->second()
};
parameterData_[paramIndex - 1].setValue(
TypeToOdbc<time>::VALUETYPE, &ts, sizeof(ts));
}
}
//------------------------------------------------------------------------------
void PreparedStatement::setTimestamp(unsigned short paramIndex,
const Timestamp& value)
{
verifyValidParamIndex(paramIndex);
if (value.isNull())
{
parameterData_[paramIndex - 1].setNull(
TypeToOdbc<timestamp>::VALUETYPE);
}
else
{
SQL_TIMESTAMP_STRUCT tss = {
(SQLSMALLINT)value->year(),
(SQLUSMALLINT)value->month(),
(SQLUSMALLINT)value->day(),
(SQLUSMALLINT)value->hour(),
(SQLUSMALLINT)value->minute(),
(SQLUSMALLINT)value->second(),
(SQLUINTEGER)value->milliseconds() * 1000000
};
parameterData_[paramIndex - 1].setValue(
TypeToOdbc<timestamp>::VALUETYPE, &tss, sizeof(tss));
}
}
//------------------------------------------------------------------------------
void PreparedStatement::clearParameters()
{
for (ParameterData& pd : parameterData_)
pd.clear();
}
//------------------------------------------------------------------------------
ResultSetMetaDataRef PreparedStatement::getMetaData()
{
ResultSetMetaDataRef ret(new ResultSetMetaData(this));
return ret;
}
//------------------------------------------------------------------------------
ResultSetMetaDataUnicodeRef PreparedStatement::getMetaDataUnicode()
{
ResultSetMetaDataUnicodeRef ret(new ResultSetMetaDataUnicode(this));
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef PreparedStatement::executeQuery()
{
ResultSetRef ret(new ResultSet(this));
EXEC_STMT(SQLFreeStmt, hstmt_, SQL_CLOSE);
bindParameters();
EXEC_STMT(SQLExecute, hstmt_);
return ret;
}
//------------------------------------------------------------------------------
size_t PreparedStatement::executeUpdate()
{
EXEC_STMT(SQLFreeStmt, hstmt_, SQL_CLOSE);
bindParameters();
SQLRETURN rc = SQLExecute(hstmt_);
if (rc == SQL_NO_DATA)
return 0;
Exception::checkForError(rc, SQL_HANDLE_STMT, hstmt_);
SQLLEN numRows;
EXEC_STMT(SQLRowCount, hstmt_, &numRows);
return numRows;
}
//------------------------------------------------------------------------------
void PreparedStatement::addBatch()
{
verifyAllParametersValid();
batch_->addRow();
}
//------------------------------------------------------------------------------
void PreparedStatement::executeBatch()
{
batch_->execute(hstmt_);
}
//------------------------------------------------------------------------------
void PreparedStatement::clearBatch()
{
batch_->clear();
}
//------------------------------------------------------------------------------
size_t PreparedStatement::getBatchDataSize() const
{
return batch_->getDataSize();
}
//------------------------------------------------------------------------------
void PreparedStatement::bindParameters()
{
verifyAllParametersValid();
for (size_t i = 1; i <= parameterData_.size(); ++i)
{
const ParameterData& pd = parameterData_[i - 1];
if (pd.isNull())
{
EXEC_STMT(SQLBindParameter, hstmt_, (SQLUSMALLINT)i,
SQL_PARAM_INPUT, pd.getValueType(),
TypeInfo::getParamTypeForValueType(pd.getValueType()),
0, 0, 0, 0, (SQLLEN*)pd.getLenIndPtr());
}
else
{
EXEC_STMT(SQLBindParameter, hstmt_, (SQLUSMALLINT)i,
SQL_PARAM_INPUT, pd.getValueType(),
TypeInfo::getParamTypeForValueType(pd.getValueType()),
pd.getColumnSize(), pd.getDecimalDigits(),
(SQLPOINTER)pd.getData(), pd.getSize(),
(SQLLEN*)pd.getLenIndPtr());
}
}
}
//------------------------------------------------------------------------------
template<typename T>
void PreparedStatement::setFixedSizeData(
unsigned short paramIndex, const Nullable<T>& value)
{
verifyValidParamIndex(paramIndex);
if (value.isNull())
parameterData_[paramIndex - 1].setNull(TypeToOdbc<T>::VALUETYPE);
else
parameterData_[paramIndex - 1].setValue(
TypeToOdbc<T>::VALUETYPE, &(*value), sizeof(T));
}
//------------------------------------------------------------------------------
void PreparedStatement::verifyValidParamIndex(unsigned short paramIndex) const
{
ODBC_CHECK(
(paramIndex >= 1) && ((size_t)paramIndex <= parameterData_.size()),
"Invalid parameter index (" << paramIndex << ")");
}
//------------------------------------------------------------------------------
void PreparedStatement::verifyAllParametersValid() const
{
for (size_t i = 0; i < parameterData_.size(); ++i)
{
ODBC_CHECK(parameterData_[i].isInitialized(),
"Parameter " << (i+1) << " has not been set");
}
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,298 @@
#ifndef ODBC_PREPAREDSTATEMENT_H_INCLUDED
#define ODBC_PREPAREDSTATEMENT_H_INCLUDED
//------------------------------------------------------------------------------
#include <memory>
#include <vector>
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/StatementBase.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
class Batch;
class ParameterData;
//------------------------------------------------------------------------------
/**
* Represents a precompiled SQL statement.
*/
class ODBC_EXPORT PreparedStatement : public StatementBase
{
friend class Connection;
friend class ParameterMetaData;
private:
PreparedStatement(Connection* parent);
public:
~PreparedStatement();
private:
void setHandleAndQuery(void* hstmt, const char* query);
void setHandleAndQuery(void* hstmt, const char16_t* query);
public:
/**
* Retrieves the number, types and properties of the parameters.
*
* @return Returns a ParameterMetaData object that contains information
* about the number, types and properties for each parameter.
*/
ParameterMetaDataRef getParameterMetaData();
/**
* Sets the specified parameter to the given boolean value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setBoolean(unsigned short paramIndex, const Boolean& value);
/**
* Sets the specified parameter to the given signed byte value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setByte(unsigned short paramIndex, const Byte& value);
/**
* Sets the specified parameter to the given unsigned byte value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setUByte(unsigned short paramIndex, const UByte& value);
/**
* Sets the specified parameter to the given signed short integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setShort(unsigned short paramIndex, const Short& value);
/**
* Sets the specified parameter to the given unsigned short integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setUShort(unsigned short paramIndex, const UShort& value);
/**
* Sets the specified parameter to the given signed integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setInt(unsigned short paramIndex, const Int& value);
/**
* Sets the specified parameter to the given unsigned integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setUInt(unsigned short paramIndex, const UInt& value);
/**
* Sets the specified parameter to the given signed long integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setLong(unsigned short paramIndex, const Long& value);
/**
* Sets the specified parameter to the given unsigned long integer value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setULong(unsigned short paramIndex, const ULong& value);
/**
* Sets the specified parameter to the given decimal value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setDecimal(unsigned short paramIndex, const Decimal& value);
/**
* Sets the specified parameter to the given float value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setFloat(unsigned short paramIndex, const Float& value);
/**
* Sets the specified parameter to the given double value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setDouble(unsigned short paramIndex, const Double& value);
/**
* Sets the specified parameter to the given string value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setString(unsigned short paramIndex, const String& value);
/**
* Sets the specified parameter to the given null-terminated character
* sequence (C-String).
*
* @param paramIndex The parameter index starting from 1.
* @param s The null-terminated character sequence.
*/
void setCString(unsigned short paramIndex, const char* s);
/**
* Sets the specified parameter to the given character sequence of the
* desired length.
*
* @param paramIndex The parameter index starting from 1.
* @param s The sequence of characters.
* @param len The length of the sequence.
*/
void setCString(unsigned short paramIndex, const char* s, std::size_t len);
/**
* Sets the specified parameter to the given string with 16-bit characters.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setNString(unsigned short paramIndex, const NString& value);
/**
* Sets the specified parameter to the given null-terminated 16-bit
* character sequence (C-String).
*
* @param paramIndex The parameter index starting from 1.
* @param s The null-terminated character sequence.
*/
void setNCString(unsigned short paramIndex, const char16_t* s);
/**
* Sets the specified parameter to the given 16-bit character sequence of
* the desired length.
*
* @param paramIndex The parameter index starting from 1.
* @param s The sequence of characters.
* @param len The length of the sequence.
*/
void setNCString(unsigned short paramIndex, const char16_t* s,
std::size_t len);
/**
* Sets the specified parameter to the given Binary object.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setBinary(unsigned short paramIndex, const Binary& value);
/**
* Sets the specified parameter to the given byte sequence of the desired
* length.
*
* @param paramIndex The parameter index starting from 1.
* @param data The sequence of bytes.
* @param size The length of the sequence.
*/
void setBytes(unsigned short paramIndex, const void* data,
std::size_t size);
/**
* Sets the specified parameter to the given Date value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setDate(unsigned short paramIndex, const Date& value);
/**
* Sets the specified parameter to the given Time value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setTime(unsigned short paramIndex, const Time& value);
/**
* Sets the specified parameter to the given Timestamp value.
*
* @param paramIndex The parameter index starting from 1.
* @param value The parameter value.
*/
void setTimestamp(unsigned short paramIndex, const Timestamp& value);
/**
* Clears the parameter values.
*/
void clearParameters();
/**
* Retrieves a ResultSetMetaData object that contains information about a
* ResultSet object returned by executeQuery method.
*
* @return Returns a ResultSetMetaData object.
*/
ResultSetMetaDataRef getMetaData();
/**
* Retrieves a ResultSetMetaDataUnicode object that contains information
* about a ResultSet object returned by executeQuery method.
*
* @return Returns a ResultSetMetaDataUnicode object.
*/
ResultSetMetaDataUnicodeRef getMetaDataUnicode();
/**
* Executes the SQL query of this PreparedStatement object and returns a
* ResultSet object.
*
* @return Returns a ResultSet object that contains the data produced
* by the given SQL statement.
*/
ResultSetRef executeQuery();
/**
* Executes the SQL query of this PreparedStatement object and returns
* the number of rows affected by an UPDATE, INSERT, or DELETE statement.
*
* @return Returns the number of rows affected by an UPDATE, INSERT, or
* DELETE statement.
*/
std::size_t executeUpdate();
/**
* Add the current set of parameters to the batch of commands.
*/
void addBatch();
/**
* Executes the batch of commands.
*/
void executeBatch();
/**
* Clears the batch of commands.
*/
void clearBatch();
/**
* Retrieves the number of bytes required by the batch of commands.
*
* @return Returns the number of bytes required by the batch of commands.
*/
std::size_t getBatchDataSize() const;
private:
void bindParameters();
template<typename T>
void setFixedSizeData(unsigned short paramIndex, const Nullable<T>& value);
void verifyValidParamIndex(unsigned short paramIndex) const;
void verifyAllParametersValid() const;
private:
std::vector<ParameterData> parameterData_;
std::unique_ptr<Batch> batch_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,28 @@
#include <odbc/RefCounted.h>
#include <cstddef>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
RefCounted::RefCounted() : refcount_(1)
{
}
//------------------------------------------------------------------------------
RefCounted::~RefCounted()
{
}
//------------------------------------------------------------------------------
void RefCounted::incRef() const
{
refcount_.fetch_add(1, std::memory_order_relaxed);
}
//------------------------------------------------------------------------------
void RefCounted::decRef() const
{
if (refcount_.fetch_sub(1, std::memory_order_release) == 1)
{
std::atomic_thread_fence(std::memory_order_acquire);
delete this;
}
}
//------------------------------------------------------------------------------
} // namespace odbc

219
external/odbccpp/src/odbc/RefCounted.h vendored Normal file
View File

@ -0,0 +1,219 @@
#ifndef ODBC_REFCOUNTED_H_INCLUDED
#define ODBC_REFCOUNTED_H_INCLUDED
//------------------------------------------------------------------------------
#include <atomic>
#include <odbc/Config.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
template<typename T>
class Reference;
//------------------------------------------------------------------------------
/**
* Base class for reference-counted objects.
*
* Reference count management is done by the Reference class.
*/
class ODBC_EXPORT RefCounted
{
template<typename T>
friend class Reference;
public:
/**
* Constructor initializing the reference-count to 1.
*/
RefCounted();
RefCounted(const RefCounted& other) = delete;
RefCounted& operator=(const RefCounted& other) = delete;
protected:
/**
* Destructor.
*/
virtual ~RefCounted();
private:
void incRef() const;
void decRef() const;
private:
mutable std::atomic_ptrdiff_t refcount_;
};
//------------------------------------------------------------------------------
/**
* Manager for reference-counted objects.
*
* Takes care of properly incrementing and decrementing reference counts of the
* managed object.
*
* @tparam T Type offering methods incRef() and decRef().
*/
template<typename T>
class Reference
{
public:
/**
* Constructs a NULL reference.
*/
Reference() : ptr_(0) {}
/**
* Constructs a reference to a given reference-counted object.
*
* @param ptr A pointer to a reference-counted object.
* @param incref Increases the reference-count of the passed object if set
* to true.
*/
Reference(T* ptr, bool incref = false) : ptr_(ptr)
{
if (ptr && incref)
ptr->incRef();
}
/**
* Copy constructs a reference.
*
* The reference-count of the managed object is increased by 1.
*
* @param other Another reference.
*/
Reference(const Reference<T>& other) { set_(other.ptr_); }
/**
* Destructor decreasing the reference-count of the managed object.
*/
~Reference() { free_(); }
public:
/**
* Assigns another reference to this reference.
*
* The reference-count of the currently managed object is decremented and
* the reference-count of the newly managed object is incremented.
*
* @param other Another reference.
*/
Reference& operator=(const Reference<T>& other)
{
reset_(other.ptr_);
return *this;
}
/**
* Checks whether this reference manages an object.
*
* @return Returns true if this reference manages an object, false
* otherwise.
*/
bool isNull() const { return ptr_ == 0; }
/**
* Returns a reference to the managed object.
*
* @return Returns a reference to the managed object.
*/
T& operator*() { return *ptr_; }
/**
* Returns a constant reference to the managed object.
*
* @return Returns a constant reference to the managed object.
*/
const T& operator*() const { return *ptr_; }
/**
* Returns a pointer to the managed object.
*
* @return Returns a pointer to the managed object.
*/
T* operator->() { return ptr_; }
/**
* Returns a constant pointer to the managed object.
*
* @return Returns a constant pointer to the managed object.
*/
const T* operator->() const { return ptr_; }
/**
* Returns a pointer to the managed object.
*
* @return Returns a pointer to the managed object.
*/
T* get() { return ptr_; }
/**
* Returns a constant pointer to the managed object.
*
* @return Returns a constant pointer to the managed object.
*/
const T* get() const { return ptr_; }
/**
* Releases the currently managed object.
*
* Decreases the reference-count of the managed object and does not
* reference it anymore.
*/
void reset() { reset_(0); }
/**
* Releases the currently managed object and manages a new object.
*
* Decreases the reference-count of the managed object and increases the
* reference-count of the newly managed object.
*
* @param newPtr The newly managed object.
*/
void reset(T* newPtr) { reset_(newPtr); }
/**
* Checks whether the managed object is less than the object managed by
* another reference.
*
* NULL references are never considered less than another reference.
*
* @param other Another reference.
* @return Returns true if the managed object is less than the object
* manages by the other reference, false otherwise.
*/
bool operator<(const Reference<T>& other) const
{
if (ptr_)
{
if (other.ptr_)
return *ptr_ < *other.ptr_;
return true;
}
return false;
}
private:
void reset_(T* newPtr)
{
if (newPtr != ptr_)
{
free_();
set_(newPtr);
}
}
void free_()
{
if (ptr_)
ptr_->decRef();
}
void set_(T* ptr)
{
ptr_ = ptr;
if (ptr_)
ptr_->incRef();
}
private:
T* ptr_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

400
external/odbccpp/src/odbc/ResultSet.cpp vendored Normal file
View File

@ -0,0 +1,400 @@
#include <algorithm>
#include <string>
#include <utility>
#include <odbc/Exception.h>
#include <odbc/ResultSet.h>
#include <odbc/ResultSetMetaData.h>
#include <odbc/ResultSetMetaDataUnicode.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
#include <odbc/internal/UtilInternal.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ResultSet::ResultSet(StatementBase* parent)
: parent_(parent, true)
{
}
//------------------------------------------------------------------------------
ResultSet::~ResultSet()
{
}
//------------------------------------------------------------------------------
bool ResultSet::next()
{
SQLRETURN rc = SQLFetch(parent_->hstmt_);
if (rc == SQL_NO_DATA)
return false;
Exception::checkForError(rc, SQL_HANDLE_STMT, parent_->hstmt_);
return true;
}
//------------------------------------------------------------------------------
void ResultSet::close()
{
EXEC_STMT(SQLFreeStmt, parent_->hstmt_, SQL_CLOSE);
}
//------------------------------------------------------------------------------
ResultSetMetaDataRef ResultSet::getMetaData()
{
ResultSetMetaDataRef ret(new ResultSetMetaData(parent_.get()));
return ret;
}
//------------------------------------------------------------------------------
ResultSetMetaDataUnicodeRef ResultSet::getMetaDataUnicode()
{
ResultSetMetaDataUnicodeRef ret(
new ResultSetMetaDataUnicode(parent_.get()));
return ret;
}
//------------------------------------------------------------------------------
Boolean ResultSet::getBoolean(unsigned short columnIndex)
{
bool val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_BIT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Boolean();
return Boolean(val);
}
//------------------------------------------------------------------------------
Byte ResultSet::getByte(unsigned short columnIndex)
{
int8_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_STINYINT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Byte();
return Byte(val);
}
//------------------------------------------------------------------------------
UByte ResultSet::getUByte(unsigned short columnIndex)
{
uint8_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_UTINYINT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return UByte();
return UByte(val);
}
//------------------------------------------------------------------------------
Short ResultSet::getShort(unsigned short columnIndex)
{
int16_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_SSHORT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Short();
return Short(val);
}
//------------------------------------------------------------------------------
UShort ResultSet::getUShort(unsigned short columnIndex)
{
uint16_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_USHORT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return UShort();
return UShort(val);
}
//------------------------------------------------------------------------------
Int ResultSet::getInt(unsigned short columnIndex)
{
int32_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_SLONG, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Int();
return Int(val);
}
//------------------------------------------------------------------------------
UInt ResultSet::getUInt(unsigned short columnIndex)
{
uint32_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_ULONG, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return UInt();
return UInt(val);
}
//------------------------------------------------------------------------------
Long ResultSet::getLong(unsigned short columnIndex)
{
int64_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_SBIGINT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Long();
return Long(val);
}
//------------------------------------------------------------------------------
ULong ResultSet::getULong(unsigned short columnIndex)
{
uint64_t val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_UBIGINT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return ULong();
return ULong(val);
}
//------------------------------------------------------------------------------
Decimal ResultSet::getDecimal(unsigned short columnIndex)
{
SQL_NUMERIC_STRUCT num;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_NUMERIC, &num,
sizeof(num), &ind);
if (ind == SQL_NULL_DATA)
return Decimal();
char str[64];
odbc::UtilInternal::numericToString(num, str);
return Decimal(decimal(str, num.precision, num.scale));
}
//------------------------------------------------------------------------------
Float ResultSet::getFloat(unsigned short columnIndex)
{
float val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_FLOAT, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Float();
return Float(val);
}
//------------------------------------------------------------------------------
Double ResultSet::getDouble(unsigned short columnIndex)
{
double val;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_DOUBLE, &val,
sizeof(val), &ind);
if (ind == SQL_NULL_DATA)
return Double();
return Double(val);
}
//------------------------------------------------------------------------------
Date ResultSet::getDate(unsigned short columnIndex)
{
DATE_STRUCT ds;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_TYPE_DATE, &ds,
sizeof(ds), &ind);
if (ind == SQL_NULL_DATA)
return Date();
return makeNullable<date>(ds.year, ds.month, ds.day);
}
//------------------------------------------------------------------------------
Time ResultSet::getTime(unsigned short columnIndex)
{
TIME_STRUCT ts;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_TYPE_TIME, &ts,
sizeof(ts), &ind);
if (ind == SQL_NULL_DATA)
return Time();
return makeNullable<time>(ts.hour, ts.minute, ts.second);
}
//------------------------------------------------------------------------------
Timestamp ResultSet::getTimestamp(unsigned short columnIndex)
{
TIMESTAMP_STRUCT ts;
SQLLEN ind;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex, SQL_C_TYPE_TIMESTAMP,
&ts, sizeof(ts), &ind);
if (ind == SQL_NULL_DATA)
return Timestamp();
return makeNullable<timestamp>(ts.year, ts.month, ts.day, ts.hour,
ts.minute, ts.second, ts.fraction / 1000000);
}
//------------------------------------------------------------------------------
String ResultSet::getString(unsigned short columnIndex)
{
SQLRETURN rc;
SQLLEN ind;
char dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_CHAR, &dummy, sizeof(dummy), &ind);
if (ind == SQL_NULL_DATA)
return String();
if (ind == 0)
return String("");
string ret;
if (ind == SQL_NO_TOTAL)
{
char buffer[1024];
for (;;)
{
rc = SQLGetData(parent_->hstmt_, columnIndex, SQL_C_CHAR,
buffer, sizeof(buffer), &ind);
Exception::checkForError(rc, SQL_HANDLE_STMT, parent_->hstmt_);
size_t len = (ind == SQL_NO_TOTAL)
? sizeof(buffer) - 1
: min<size_t>(sizeof(buffer) - 1, ind);
ret.append(buffer, len);
if (rc == SQL_SUCCESS)
break;
}
}
else
{
ret.resize(ind + 1);
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_CHAR, &ret[0], ret.size(), &ind);
ret.resize(ind);
}
return String(move(ret));
}
//------------------------------------------------------------------------------
NString ResultSet::getNString(unsigned short columnIndex)
{
SQLRETURN rc;
SQLLEN ind;
char16_t dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_WCHAR, &dummy, sizeof(dummy), &ind);
if (ind == SQL_NULL_DATA)
return NString();
if (ind == 0)
return NString(u"");
u16string ret;
if (ind == SQL_NO_TOTAL)
{
char16_t buffer[1024];
for (;;)
{
rc = SQLGetData(parent_->hstmt_, columnIndex, SQL_C_WCHAR,
buffer, sizeof(buffer), &ind);
Exception::checkForError(rc, SQL_HANDLE_STMT, parent_->hstmt_);
size_t len = (ind == SQL_NO_TOTAL)
? sizeof(buffer)/sizeof(char16_t) - 1
: min<size_t>(sizeof(buffer)/sizeof(char16_t) - 1, ind/2);
ret.append(buffer, len);
if (rc == SQL_SUCCESS)
break;
}
}
else
{
ret.resize(ind/2 + 1);
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_WCHAR, &ret[0], ret.size()*sizeof(char16_t), &ind);
ret.resize(ind/2);
}
return NString(move(ret));
}
//------------------------------------------------------------------------------
Binary ResultSet::getBinary(unsigned short columnIndex)
{
SQLRETURN rc;
SQLLEN ind;
char dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_BINARY, &dummy, 0, &ind);
if (ind == SQL_NULL_DATA)
return Binary();
if (ind == 0)
return Binary(vector<char>());
vector<char> ret;
if (ind == SQL_NO_TOTAL)
{
char buffer[1024];
for (;;)
{
rc = SQLGetData(parent_->hstmt_, columnIndex, SQL_C_BINARY,
buffer, sizeof(buffer), &ind);
Exception::checkForError(rc, SQL_HANDLE_STMT, parent_->hstmt_);
if (ind == SQL_NO_TOTAL)
ret.insert(ret.end(), buffer, buffer + sizeof(buffer));
else
ret.insert(ret.end(), buffer, buffer + ind);
if (rc == SQL_SUCCESS)
break;
}
}
else
{
ret.resize(ind);
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_BINARY, ret.data(), ret.size(), &ind);
}
return Binary(move(ret));
}
//------------------------------------------------------------------------------
static size_t convertLength(SQLLEN ind)
{
switch (ind)
{
case SQL_NULL_DATA:
return ResultSet::NULL_DATA;
case SQL_NO_TOTAL:
return ResultSet::UNKNOWN_LENGTH;
}
return ind;
}
//------------------------------------------------------------------------------
size_t ResultSet::getBinaryLength(unsigned short columnIndex)
{
SQLLEN ind;
char dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_BINARY, &dummy, 0, &ind);
return convertLength(ind);
}
//------------------------------------------------------------------------------
void ResultSet::getBinaryData(unsigned short columnIndex, void* data,
size_t size)
{
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_BINARY, (SQLPOINTER)data, size, NULL);
}
//------------------------------------------------------------------------------
size_t ResultSet::getStringLength(unsigned short columnIndex)
{
SQLLEN ind;
char dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_CHAR, &dummy, sizeof(dummy), &ind);
return convertLength(ind);
}
//------------------------------------------------------------------------------
void ResultSet::getStringData(unsigned short columnIndex, void* data,
size_t size)
{
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_CHAR, (SQLPOINTER)data, size, NULL);
}
//------------------------------------------------------------------------------
size_t ResultSet::getNStringLength(unsigned short columnIndex)
{
SQLLEN ind;
char16_t dummy;
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_WCHAR, &dummy, sizeof(dummy), &ind);
size_t ret = convertLength(ind);
if (ret == ResultSet::NULL_DATA || ret == ResultSet::UNKNOWN_LENGTH)
return ret;
return ret / sizeof(char16_t);
}
//------------------------------------------------------------------------------
void ResultSet::getNStringData(unsigned short columnIndex, void* data,
size_t size)
{
EXEC_STMT(SQLGetData, parent_->hstmt_, columnIndex,
SQL_C_WCHAR, (SQLPOINTER)data, size * sizeof(char16_t), NULL);
}
//------------------------------------------------------------------------------
} // namespace odbc

324
external/odbccpp/src/odbc/ResultSet.h vendored Normal file
View File

@ -0,0 +1,324 @@
#ifndef ODBC_RESULTSET_H_INCLUDED
#define ODBC_RESULTSET_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Represents the result set of a SQL query.
*/
class ODBC_EXPORT ResultSet : public RefCounted
{
friend class DatabaseMetaData;
friend class DatabaseMetaDataBase;
friend class DatabaseMetaDataUnicode;
friend class PreparedStatement;
friend class Statement;
public:
/**
* Data length constant that signals that the data is NULL.
*/
constexpr static std::size_t NULL_DATA = (std::size_t)-1;
/**
* Data length constant that signals that the database could not determine
* the length of the data.
*/
constexpr static std::size_t UNKNOWN_LENGTH = (std::size_t)-2;
private:
ResultSet(StatementBase* parent);
~ResultSet();
public:
/**
* Advances the cursor to the next row of the ResultSet object and fetches
* data for all bound columns.
*
* @return Returns true if the current row is valid, false otherwise.
*/
bool next();
/**
* Closes the cursor associated with the current Statement object and
* discards all pending results.
*/
void close();
/**
* Retrieves a ResultSetMetaData object that contains information about the
* number, types and properties of the columns in this ResultSet object.
*
* @return Returns a ResultSetMetaData object.
*/
ResultSetMetaDataRef getMetaData();
/**
* Retrieves a ResultSetMetaDataUnicode object that contains information
* about the number, types and properties of the columns in this ResultSet
* object.
*
* @return Returns a ResultSetMetaDataUnicode object.
*/
ResultSetMetaDataUnicodeRef getMetaDataUnicode();
/**
* Retrieves the value of the specified column in the current row as
* a boolean.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Boolean getBoolean(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a signed byte.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Byte getByte(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* an unsigned byte.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
UByte getUByte(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a signed short integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Short getShort(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* an unsigned short integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
UShort getUShort(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a signed integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Int getInt(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* an unsigned integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
UInt getUInt(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a signed long integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Long getLong(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* an unsigned long integer.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
ULong getULong(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a decimal object.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Decimal getDecimal(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a floating point type with single precision.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Float getFloat(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a floating point type with double precision.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Double getDouble(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a date object.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Date getDate(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a time object.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Time getTime(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a timestamp object.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Timestamp getTimestamp(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a string object.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
String getString(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* a string with 16-bit characters.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
NString getNString(unsigned short columnIndex);
/**
* Retrieves the value of the specified column in the current row as
* an object with binary data.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column value.
*/
Binary getBinary(unsigned short columnIndex);
/**
* Retrieves the length of the binary data stored in the specified column of
* the current row.
*
* If the binary data is NULL, the constant NULL_DATA is returned. If the
* database cannot determine the length, the constant UNKNOWN_LENGTH is
* returned.
*
* @param columnIndex The column index starting from 1.
* @return Returns the length of binary data, NULL_DATA or
* UNKNOWN_LENGTH.
*/
std::size_t getBinaryLength(unsigned short columnIndex);
/**
* Retrieves the binary data stored in the specified column of the current
* row.
*
* The total length of the binary data can be found by calling
* the getBinaryLength() function.
*
* @param columnIndex The column index starting from 1.
* @param data The pointer to a buffer where the read data will be
* stored.
* @param size The maximum number of bytes to be read.
*/
void getBinaryData(unsigned short columnIndex, void* data,
std::size_t size);
/**
* Retrieves the lengh of the string stored in the specified column of
* the current row.
*
* If the string is NULL, the constant NULL_DATA is returned. If the
* database cannot determine the length, the constant UNKNOWN_LENGTH is
* returned.
*
* @param columnIndex The column index starting from 1.
* @return The length of a string in characters, NULL_DATA or
* UNKNOWN_LENGTH.
*/
std::size_t getStringLength(unsigned short columnIndex);
/**
* Retrieves the string data stored in the specified column of the current
* row.
*
* The total length of the string can be found by calling the
* getStringLength() function.
*
* @param columnIndex The column index starting from 1.
* @param[out] data The pointer to a buffer where the read data will be
* stored.
* @param size The maximum number of bytes to be read.
*/
void getStringData(unsigned short columnIndex, void* data,
std::size_t size);
/**
* Retrieves the length of the string with 16-bit characters stored in the
* specified column of the current row.
*
* If the string is NULL, the constant NULL_DATA is returned. If the
* database cannot determine the length, the constant UNKNOWN_LENGTH is
* returned.
*
* @param columnIndex The column index starting from 1.
* @return The length of a string in characters, NULL_DATA or
* UNKNOWN_LENGTH.
*/
std::size_t getNStringLength(unsigned short columnIndex);
/**
* Retrieves the 16-bit character string data stored in the specified column
* of the current row.
*
* The total length of the string can be found by calling the
* getNStringLength() function.
*
* @param columnIndex The column index starting from 1.
* @param data The pointer to a buffer where read data will be
stored.
* @param size The maximum number of 16-bit characters to be read.
*/
void getNStringData(unsigned short columnIndex, void* data,
std::size_t size);
private:
StatementBaseRef parent_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,66 @@
#include <vector>
#include <odbc/Exception.h>
#include <odbc/ResultSetMetaData.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ResultSetMetaData::ResultSetMetaData(StatementBase* stmt)
: ResultSetMetaDataBase(stmt)
{
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getCatalogName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_CATALOG_NAME);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getSchemaName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_SCHEMA_NAME);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getTableName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_TABLE_NAME);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getColumnLabel(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_LABEL);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getColumnName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_NAME);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getColumnTypeName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_TYPE_NAME);
}
//------------------------------------------------------------------------------
string ResultSetMetaData::getStringColAttribute(unsigned short columnIndex,
unsigned short field)
{
vector<char> buffer;
buffer.resize(256);
while (true)
{
SQLPOINTER ptr = &buffer[0];
SQLSMALLINT bufLen = (SQLSMALLINT)buffer.size();
SQLSMALLINT dataLen;
EXEC_STMT(SQLColAttributeA, stmt_->hstmt_, columnIndex, field, ptr,
bufLen, &dataLen, NULL);
if (dataLen < bufLen)
break;
buffer.resize(dataLen + 1);
}
return string(&buffer[0]);
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,78 @@
#ifndef ODBC_RESULTSETMETADATA_H_INCLUDED
#define ODBC_RESULTSETMETADATA_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/ResultSetMetaDataBase.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Metadata on a result set.
*/
class ODBC_EXPORT ResultSetMetaData : public ResultSetMetaDataBase
{
friend class PreparedStatement;
friend class ResultSet;
private:
ResultSetMetaData(StatementBase* parent);
public:
/**
* Returns a column's catalog name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's catalog name.
*/
std::string getCatalogName(unsigned short columnIndex);
/**
* Returns a column's schema name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's schema name.
*/
std::string getSchemaName(unsigned short columnIndex);
/**
* Returns a column's table name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's table name.
*/
std::string getTableName(unsigned short columnIndex);
/**
* Returns a column's label.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's label.
*/
std::string getColumnLabel(unsigned short columnIndex);
/**
* Returns a column's name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's name.
*/
std::string getColumnName(unsigned short columnIndex);
/**
* Returns a column's type name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's type name.
*/
std::string getColumnTypeName(unsigned short columnIndex);
private:
std::string getStringColAttribute(unsigned short columnIndex,
unsigned short field);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,128 @@
#include <vector>
#include <odbc/Exception.h>
#include <odbc/ResultSetMetaDataBase.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ResultSetMetaDataBase::ResultSetMetaDataBase(StatementBase* stmt)
: stmt_(stmt, true)
{
}
//------------------------------------------------------------------------------
unsigned short ResultSetMetaDataBase::getColumnCount()
{
SQLSMALLINT ret;
EXEC_STMT(SQLNumResultCols, stmt_->hstmt_, &ret);
return (unsigned short)ret;
}
//------------------------------------------------------------------------------
short ResultSetMetaDataBase::getColumnType(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_TYPE, NULL, 0, NULL, &ret);
return (short)ret;
}
//------------------------------------------------------------------------------
size_t ResultSetMetaDataBase::getColumnLength(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_LENGTH, NULL, 0, NULL, &ret);
return (size_t)ret;
}
//------------------------------------------------------------------------------
size_t ResultSetMetaDataBase::getColumnOctetLength(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &ret);
return (size_t)ret;
}
//------------------------------------------------------------------------------
size_t ResultSetMetaDataBase::getColumnDisplaySize(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &ret);
return (size_t)ret;
}
//------------------------------------------------------------------------------
unsigned short ResultSetMetaDataBase::getPrecision(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_PRECISION, NULL, 0, NULL, &ret);
return (unsigned short)ret;
}
//------------------------------------------------------------------------------
unsigned short ResultSetMetaDataBase::getScale(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_SCALE, NULL, 0, NULL, &ret);
return (unsigned short)ret;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isAutoIncrement(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_AUTO_UNIQUE_VALUE, NULL, 0, NULL, &ret);
return ret == SQL_TRUE;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isCaseSensitive(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_CASE_SENSITIVE, NULL, 0, NULL, &ret);
return ret == SQL_TRUE;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isNamed(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_UNNAMED, NULL, 0, NULL, &ret);
return ret == SQL_NAMED;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isNullable(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_NULLABLE, NULL, 0, NULL, &ret);
return ret == SQL_NULLABLE;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isReadOnly(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_UPDATABLE, NULL, 0, NULL, &ret);
return ret == SQL_ATTR_READONLY;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isSearchable(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_SEARCHABLE, NULL, 0, NULL, &ret);
return ret == SQL_PRED_SEARCHABLE;
}
//------------------------------------------------------------------------------
bool ResultSetMetaDataBase::isSigned(unsigned short columnIndex)
{
SQLLEN ret;
EXEC_STMT(SQLColAttribute, stmt_->hstmt_, columnIndex,
SQL_DESC_UNSIGNED, NULL, 0, NULL, &ret);
return ret == SQL_FALSE;
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,157 @@
#ifndef ODBC_RESULTSETMETADATABASE_H_INCLUDED
#define ODBC_RESULTSETMETADATABASE_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Metadata on a result set.
*/
class ODBC_EXPORT ResultSetMetaDataBase : public RefCounted
{
protected:
ResultSetMetaDataBase(StatementBase* parent);
public:
/**
* Returns the number of columns in the result set.
*
* @return Returns the number of columns in the result set.
*/
unsigned short getColumnCount();
/**
* Returns a column's data type.
*
* See SQLDataTypes for a list of data type constants.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's data type.
*/
short getColumnType(unsigned short columnIndex);
/**
* Returns a column's length.
*
* This is usually the maximum length of a value measured in characters for
* strings and measured in bytes for binary data.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's length.
*/
std::size_t getColumnLength(unsigned short columnIndex);
/**
* Returns a column's octet length.
*
* This is usually the maximum length of a value measured in bytes.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's octet length.
*/
std::size_t getColumnOctetLength(unsigned short columnIndex);
/**
* Returns a column's display size.
*
* This is the maximum number of characters to display a value.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's display size.
*/
std::size_t getColumnDisplaySize(unsigned short columnIndex);
/**
* Returns a column's precision in case of decimal types.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's precision in case of decimal
* types.
*/
unsigned short getPrecision(unsigned short columnIndex);
/**
* Returns a column's scale in case of decimal types.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's scale in case of decimal
* types.
*/
unsigned short getScale(unsigned short columnIndex);
/**
* Checks whether a column is auto-incrementing.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column is auto-incrementing,
* false otherwise.
*/
bool isAutoIncrement(unsigned short columnIndex);
/**
* Checks whether a column is case sensitive.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column is case-sensitive, false
* otherwise.
*/
bool isCaseSensitive(unsigned short columnIndex);
/**
* Checks whether a column is named.
*
* If a column is named, its name can be retrieved with the getColumnName()
* function.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column is named, false otherwise.
*/
bool isNamed(unsigned short columnIndex);
/**
* Checks whether a column can contain NULL values.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column can contain NULL values,
* false otherwise.
*/
bool isNullable(unsigned short columnIndex);
/**
* Checks whether a column in a result set is read-only.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column is read-only, false
* otherwise.
*/
bool isReadOnly(unsigned short columnIndex);
/**
* Checks whether a column can be used in WHERE-clauses with any comparison
* operator.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column is searchable, false
* otherwise.
*/
bool isSearchable(unsigned short columnIndex);
/**
* Checks whether a column can contain signed numbers.
*
* @param columnIndex The column index starting from 1.
* @return Returns true if the column can contain signed
* numbers, false otherwise.
*/
bool isSigned(unsigned short columnIndex);
protected:
StatementBaseRef stmt_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,67 @@
#include <vector>
#include <odbc/Exception.h>
#include <odbc/ResultSetMetaDataUnicode.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ResultSetMetaDataUnicode::ResultSetMetaDataUnicode(StatementBase* stmt)
: ResultSetMetaDataBase(stmt)
{
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getCatalogName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_CATALOG_NAME);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getSchemaName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_SCHEMA_NAME);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getTableName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_TABLE_NAME);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getColumnLabel(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_LABEL);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getColumnName(unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_NAME);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getColumnTypeName(
unsigned short columnIndex)
{
return getStringColAttribute(columnIndex, SQL_DESC_TYPE_NAME);
}
//------------------------------------------------------------------------------
u16string ResultSetMetaDataUnicode::getStringColAttribute(
unsigned short columnIndex, unsigned short field)
{
vector<char16_t> buffer;
buffer.resize(256);
while (true)
{
SQLPOINTER ptr = &buffer[0];
SQLSMALLINT bufLen = (SQLSMALLINT)(buffer.size() * sizeof(char16_t));
SQLSMALLINT dataLen;
EXEC_STMT(SQLColAttributeW, stmt_->hstmt_, columnIndex, field, ptr,
bufLen, &dataLen, NULL);
if (dataLen < bufLen)
break;
buffer.resize(dataLen / sizeof(char16_t) + 1);
}
return u16string(&buffer[0]);
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,78 @@
#ifndef ODBC_RESULTSETMETADATAUNICODE_H_INCLUDED
#define ODBC_RESULTSETMETADATAUNICODE_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/ResultSetMetaDataBase.h>
#include <odbc/Types.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Metadata on a result set.
*/
class ODBC_EXPORT ResultSetMetaDataUnicode : public ResultSetMetaDataBase
{
friend class PreparedStatement;
friend class ResultSet;
private:
ResultSetMetaDataUnicode(StatementBase* parent);
public:
/**
* Returns a column's catalog name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's catalog name.
*/
std::u16string getCatalogName(unsigned short columnIndex);
/**
* Returns a column's schema name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's schema name.
*/
std::u16string getSchemaName(unsigned short columnIndex);
/**
* Returns a column's table name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's table name.
*/
std::u16string getTableName(unsigned short columnIndex);
/**
* Returns a column's label.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's label.
*/
std::u16string getColumnLabel(unsigned short columnIndex);
/**
* Returns a column's name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's name.
*/
std::u16string getColumnName(unsigned short columnIndex);
/**
* Returns a column's type name.
*
* @param columnIndex The column index starting from 1.
* @return Returns the column's type name.
*/
std::u16string getColumnTypeName(unsigned short columnIndex);
private:
std::u16string getStringColAttribute(unsigned short columnIndex,
unsigned short field);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

46
external/odbccpp/src/odbc/Statement.cpp vendored Normal file
View File

@ -0,0 +1,46 @@
#include <odbc/Exception.h>
#include <odbc/ResultSet.h>
#include <odbc/Statement.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
Statement::Statement(Connection* parent) : StatementBase(parent)
{
}
//------------------------------------------------------------------------------
void Statement::setHandle(void* hstmt)
{
hstmt_ = hstmt;
}
//------------------------------------------------------------------------------
void Statement::execute(const char* sql)
{
EXEC_STMT(SQLExecDirectA, hstmt_, (SQLCHAR*)sql, SQL_NTS);
}
//------------------------------------------------------------------------------
void Statement::execute(const char16_t* sql)
{
EXEC_STMT(SQLExecDirectW, hstmt_, (SQLWCHAR*)sql, SQL_NTS);
}
//------------------------------------------------------------------------------
ResultSetRef Statement::executeQuery(const char* sql)
{
ResultSetRef ret(new ResultSet(this));
EXEC_STMT(SQLFreeStmt, hstmt_, SQL_CLOSE);
EXEC_STMT(SQLExecDirectA, hstmt_, (SQLCHAR*)sql, SQL_NTS);
return ret;
}
//------------------------------------------------------------------------------
ResultSetRef Statement::executeQuery(const char16_t* sql)
{
ResultSetRef ret(new ResultSet(this));
EXEC_STMT(SQLFreeStmt, hstmt_, SQL_CLOSE);
EXEC_STMT(SQLExecDirectW, hstmt_, (SQLWCHAR*)sql, SQL_NTS);
return ret;
}
//------------------------------------------------------------------------------
} // namespace odbc

62
external/odbccpp/src/odbc/Statement.h vendored Normal file
View File

@ -0,0 +1,62 @@
#ifndef ODBC_STATEMENT_H_INCLUDED
#define ODBC_STATEMENT_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
#include <odbc/StatementBase.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* SQL Statement wrapper.
*
* Represents a SQL statement that can be executed.
*/
class ODBC_EXPORT Statement : public StatementBase
{
friend class Connection;
friend class DatabaseMetaData;
private:
Statement(Connection* parent);
private:
void setHandle(void* hstmt);
public:
/**
* Executes the given SQL statement.
*
* @param sql The SQL statement.
*/
void execute(const char* sql);
/**
* Executes the given SQL statement.
*
* @param sql The SQL statement.
*/
void execute(const char16_t* sql);
/**
* Executes the given SQL statement and returns a ResultSet object.
*
* @param sql The SQL statement.
* @return Returns a ResultSet object that contains the data produced
* by the given SQL statement.
*/
ResultSetRef executeQuery(const char* sql);
/**
* Executes the given SQL statement and returns a ResultSet object.
*
* @param sql The SQL statement.
* @return Returns a ResultSet object that contains the data produced
* by the given SQL statement.
*/
ResultSetRef executeQuery(const char16_t* sql);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,51 @@
#include <odbc/Connection.h>
#include <odbc/Exception.h>
#include <odbc/StatementBase.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
StatementBase::StatementBase(Connection* parent)
: parent_(parent, true)
, hstmt_(SQL_NULL_HANDLE)
{
}
//------------------------------------------------------------------------------
StatementBase::~StatementBase()
{
if (hstmt_)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt_);
}
//------------------------------------------------------------------------------
unsigned long StatementBase::getMaxRows()
{
SQLULEN ret;
EXEC_STMT(SQLGetStmtAttr, hstmt_, SQL_ATTR_MAX_ROWS,
(SQLPOINTER)&ret, 0, NULL);
return (unsigned long)ret;
}
//------------------------------------------------------------------------------
void StatementBase::setMaxRows(unsigned long maxRows)
{
EXEC_STMT(SQLSetStmtAttr, hstmt_, SQL_ATTR_MAX_ROWS,
(SQLPOINTER)(ptrdiff_t)maxRows, SQL_IS_UINTEGER);
}
//------------------------------------------------------------------------------
unsigned long StatementBase::getQueryTimeout()
{
SQLULEN ret;
EXEC_STMT(SQLGetStmtAttr, hstmt_, SQL_ATTR_QUERY_TIMEOUT,
(SQLPOINTER)&ret, 0, NULL);
return (unsigned long)ret;
}
//------------------------------------------------------------------------------
void StatementBase::setQueryTimeout(unsigned long seconds)
{
EXEC_STMT(SQLSetStmtAttr, hstmt_, SQL_ATTR_QUERY_TIMEOUT,
(SQLPOINTER)(ptrdiff_t)seconds, SQL_IS_UINTEGER);
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,78 @@
#ifndef ODBC_STATEMENTBASE_H_INCLUDED
#define ODBC_STATEMENTBASE_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Config.h>
#include <odbc/Forwards.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Base class for Statement and PreparedStatement.
*/
class ODBC_EXPORT StatementBase : public RefCounted
{
friend class DatabaseMetaData;
friend class DatabaseMetaDataBase;
friend class DatabaseMetaDataUnicode;
friend class ParameterMetaData;
friend class PreparedStatement;
friend class ResultSet;
friend class ResultSetMetaData;
friend class ResultSetMetaDataBase;
friend class ResultSetMetaDataUnicode;
friend class Statement;
private:
StatementBase(Connection* parent);
~StatementBase();
private:
ConnectionRef parent_;
void* hstmt_;
public:
/**
* Gets the maximum number of rows that any ResultSet object created by the
* current instance can contain.
*
* If the returned value equals 0, the driver returns all rows.
*
* @return Returns the maximum number of rows in a ResultSet object.
*/
unsigned long getMaxRows();
/**
* Sets the maximum number of rows that any ResultSet object created by the
* current instance can contain.
*
* If the value is set to 0, the driver returns all rows.
*
* @param maxRows The the maximum number of rows in a ResultSet object.
*/
void setMaxRows(unsigned long maxRows);
/**
* Retrieves the number of seconds that the Statement object has at its
* disposal to finish query execution.
*
* If the returned value equals 0, there is no limit.
*
* @return Returns the query timeout.
*/
unsigned long getQueryTimeout();
/**
* Sets the numer of seconds that the Statement object has at its
* disposal to finish query execution.
*
* If the value is set to 0, there is no limit.
*
* @param seconds The query timeout in seconds.
*/
void setQueryTimeout(unsigned long seconds);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

467
external/odbccpp/src/odbc/Types.cpp vendored Normal file
View File

@ -0,0 +1,467 @@
#include <odbc/Exception.h>
#include <odbc/Types.h>
#include <odbc/internal/Macros.h>
#include <algorithm>
#include <cstdio>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
decimal::decimal() : value_("0"), precision_(1), scale_(1)
{
}
//------------------------------------------------------------------------------
decimal::decimal(std::int64_t value, std::uint8_t precision, std::uint8_t scale)
: decimal(to_string(value), precision, scale)
{
}
//------------------------------------------------------------------------------
decimal::decimal(
std::uint64_t value, std::uint8_t precision, std::uint8_t scale)
: decimal(to_string(value), precision, scale)
{
}
//------------------------------------------------------------------------------
decimal::decimal(
std::string const& value, std::uint8_t precision, std::uint8_t scale)
: decimal(value.c_str(), precision, scale)
{
}
//------------------------------------------------------------------------------
decimal::decimal(const char* value, std::uint8_t precision, std::uint8_t scale)
: precision_(precision), scale_(scale)
{
ODBC_CHECK(precision >= 1 && precision <= 38,
"precision value must lie within [1,38]");
ODBC_CHECK(scale <= precision, "scale value must lie within [0,precision]");
// decimal = [sign] digit+
// sign = "+" | "-"
// digit = "0" - "9"
const char* pos = value;
// Process the sign
bool isNegative = false;
if (*pos == '+') {
++pos;
} else if (*pos == '-') {
isNegative = true;
++pos;
}
// Process the digits
const char* digitsStart = pos;
// Skip leading zeros
while (*pos == '0') {
++pos;
}
// Process remaining digits
const char* nonZeroDigitsStart = pos;
while (*pos) {
ODBC_CHECK(('0' <= *pos) && (*pos <= '9'),
"Decimal contains an invalid digit at position " << pos - value);
++pos;
}
const char* digitsEnd = pos;
ODBC_CHECK(digitsStart != digitsEnd, "Decimal does not contain any digits");
// Special case: Only zeros
if (nonZeroDigitsStart == digitsEnd) {
value_ = "0";
return;
}
// Usual case: some non-zero digits
ptrdiff_t numDigits = digitsEnd - nonZeroDigitsStart;
ODBC_CHECK(numDigits <= precision, "Decimal cannot have more than "
<< precision << " digits, but has " << numDigits);
if (isNegative)
value_.push_back('-');
value_.append(nonZeroDigitsStart, digitsEnd);
}
//------------------------------------------------------------------------------
int8_t decimal::signum() const
{
switch (value_[0]) {
case '-':
return -1;
case '0':
return 0;
default:
return 1;
}
}
//------------------------------------------------------------------------------
string decimal::toString() const
{
if (scale_ == 0)
return value_;
bool isSigned = (value_[0] == '-');
size_t numDigits = value_.length() - (isSigned ? 1 : 0);
string ret;
if (scale_ < numDigits)
{
ret.reserve(value_.length() + 1);
size_t numCharsBeforeDecimalPoint = value_.length() - scale_;
ret.append(value_.begin(), value_.begin() + numCharsBeforeDecimalPoint);
ret.push_back('.');
ret.append(value_.begin() + numCharsBeforeDecimalPoint, value_.end());
}
else
{
size_t len = (isSigned ? 1 : 0) + 2 + scale_;
ret.reserve(len);
if (isSigned)
ret.push_back('-');
ret.append("0.");
size_t numZeros = scale_ - numDigits;
ret.append(numZeros, '0');
ret.append(value_.begin() + (isSigned ? 1 : 0), value_.end());
}
return ret;
}
//------------------------------------------------------------------------------
bool decimal::operator==(const decimal& other) const
{
return cmp(other) == 0;
}
//------------------------------------------------------------------------------
bool decimal::operator!=(const decimal& other) const
{
return cmp(other) != 0;
}
//------------------------------------------------------------------------------
bool decimal::operator<(const decimal& other) const
{
return cmp(other) < 0;
}
//------------------------------------------------------------------------------
bool decimal::operator>(const decimal& other) const
{
return cmp(other) > 0;
}
//------------------------------------------------------------------------------
bool decimal::operator<=(const decimal& other) const
{
return cmp(other) <= 0;
}
//------------------------------------------------------------------------------
bool decimal::operator>=(const decimal& other) const
{
return cmp(other) >= 0;
}
//------------------------------------------------------------------------------
int decimal::cmp(const decimal& other) const
{
// If signums are different, we can decide the relation of the numbers to
// each other.
int signumThis = signum();
int signumOther = other.signum();
if (signumThis != signumOther)
return signumThis - signumOther;
// Signums are equals. Let's take a shortcut for the special case that both
// numbers are 0.
if (signumThis == 0)
return 0;
// When comparing the numbers, we have to consider the scale properly.
// We assign positions to each digit. The digit immediately before the the
// decimal point is assigned position 0, the digit before that position 1
// and so on. The first digit after the decimal point is assigned position
// -1 and so on. After that we can compare digit by digit starting with
// the digit having the highest position.
bool isSigned = (value_[0] == '-');
int numDigitsThis = (int)value_.length() - (isSigned ? 1 : 0);
int numDigitsOther = (int)other.value_.length() - (isSigned ? 1 : 0);
int maxDigitThis = numDigitsThis - scale_ - 1;
int minDigitThis = -(int)scale_;
int maxDigitOther = numDigitsOther - other.scale_ - 1;
int minDigitOther = -(int)other.scale_;
// We iterate over the whole position range of both numbers.
int maxDigit = max(maxDigitThis, maxDigitOther);
int minDigit = min(minDigitThis, minDigitOther);
for (int i = maxDigit; i > minDigit; --i)
{
// Get the digit at position i of this number. Substitute '0' if
// position i is not available.
char digitThis;
if ((i > maxDigitThis) || (i < minDigitThis))
{
digitThis = '0';
}
else
{
int rpos = -minDigitThis + i;
digitThis = value_[value_.length() - 1 - rpos];
}
// Get the digit at position i of the other number. Substitute '0' if
// position i is not available.
char digitOther;
if ((i > maxDigitOther) || (i < minDigitOther))
{
digitOther = '0';
}
else
{
int rpos = -minDigitOther + i;
digitOther = other.value_[other.value_.length() - 1 - rpos];
}
// If the digits are different, we are done.
if (digitThis != digitOther)
return (int)digitThis - (int)digitOther;
}
return 0;
}
//------------------------------------------------------------------------------
ostream& operator<<(ostream& out, const decimal& d)
{
out << d.toString();
return out;
}
//------------------------------------------------------------------------------
date::date() : date(0, 1, 1) {}
//------------------------------------------------------------------------------
date::date(int year, int month, int day)
{
ODBC_CHECK((year >= 0) && (year <= 9999), "Invalid year (" << year << ")");
ODBC_CHECK((month >= 1) && (month <= 12),
"Invalid month (" << month << ")");
ODBC_CHECK((day >= 1) && (day <= daysInMonth(year, month)),
"Invalid day (" << day << ")");
year_ = year;
month_ = month;
day_ = day;
}
//------------------------------------------------------------------------------
string date::toString() const
{
char buffer[32];
snprintf(buffer, 32, "%04d-%02d-%02d", year_, month_, day_);
return string(buffer);
}
//------------------------------------------------------------------------------
bool date::operator==(const date& other) const
{
return (year_ == other.year_)
&& (month_ == other.month_)
&& (day_ == other.day_);
}
//------------------------------------------------------------------------------
bool date::operator!=(const date& other) const
{
return !(*this == other);
}
//------------------------------------------------------------------------------
bool date::operator<(const date& other) const
{
if (year_ != other.year_)
return year_ < other.year_;
if (month_ != other.month_)
return month_ < other.month_;
return day_ < other.day_;
}
//------------------------------------------------------------------------------
bool date::operator>(const date& other) const
{
if (year_ != other.year_)
return year_ > other.year_;
if (month_ != other.month_)
return month_ > other.month_;
return day_ > other.day_;
}
//------------------------------------------------------------------------------
bool date::operator<=(const date& other) const
{
return !(*this > other);
}
//------------------------------------------------------------------------------
bool date::operator>=(const date& other) const
{
return !(*this < other);
}
//------------------------------------------------------------------------------
int date::daysInMonth(int year, int month)
{
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return 31;
case 4:
case 6:
case 9:
case 11:
return 30;
case 2:
return daysInFebruary(year);
default:
ODBC_FAIL("Invalid month (" << month << ")");
}
}
//------------------------------------------------------------------------------
int date::daysInFebruary(int year)
{
if ((year % 400) == 0)
return 29;
if ((year % 100) == 0)
return 28;
if ((year % 4) == 0)
return 29;
return 28;
}
//------------------------------------------------------------------------------
ostream& operator<<(ostream& out, const date& d)
{
out << d.toString();
return out;
}
//------------------------------------------------------------------------------
time::time() : time(0, 0, 0) {}
//------------------------------------------------------------------------------
time::time(int hour, int minute, int second)
{
ODBC_CHECK((hour >= 0) && (hour <= 23), "Invalid hour (" << hour << ")");
ODBC_CHECK((minute >= 0) && (minute <= 59),
"Invalid minute (" << minute << ")");
ODBC_CHECK((second >= 0) && (second <= 59),
"Invalid second (" << second << ")");
hour_ = hour;
minute_ = minute;
second_ = second;
}
//------------------------------------------------------------------------------
string time::toString() const
{
char buffer[32];
snprintf(buffer, 32, "%02d:%02d:%02d", hour_, minute_, second_);
return string(buffer);
}
//------------------------------------------------------------------------------
bool time::operator==(const time& other) const
{
return (hour_ == other.hour_)
&& (minute_ == other.minute_)
&& (second_ == other.second_);
}
//------------------------------------------------------------------------------
bool time::operator!=(const time& other) const
{
return !(*this == other);
}
//------------------------------------------------------------------------------
bool time::operator<(const time& other) const
{
if (hour_ != other.hour_)
return hour_ < other.hour_;
if (minute_ != other.minute_)
return minute_ < other.minute_;
return second_ < other.second_;
}
//------------------------------------------------------------------------------
bool time::operator>(const time& other) const
{
if (hour_ != other.hour_)
return hour_ > other.hour_;
if (minute_ != other.minute_)
return minute_ > other.minute_;
return second_ > other.second_;
}
//------------------------------------------------------------------------------
bool time::operator<=(const time& other) const
{
return !(*this > other);
}
//------------------------------------------------------------------------------
bool time::operator>=(const time& other) const
{
return !(*this < other);
}
//------------------------------------------------------------------------------
ostream& operator<<(ostream& out, const time& t)
{
out << t.toString();
return out;
}
//------------------------------------------------------------------------------
timestamp::timestamp() : timestamp(0, 1, 1, 0, 0, 0, 0) {}
//------------------------------------------------------------------------------
timestamp::timestamp(int year, int month, int day, int hour, int minute,
int second, int milliseconds) : date(year, month, day),
time(hour, minute, second)
{
ODBC_CHECK((milliseconds >= 0) && (milliseconds <= 999),
"Invalid milliseconds (" << milliseconds << ")");
milliseconds_ = milliseconds;
}
//------------------------------------------------------------------------------
string timestamp::toString() const
{
char buffer[40];
snprintf(buffer, 40, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
year_, month_, day_, hour_, minute_, second_, milliseconds_);
return string(buffer);
}
//------------------------------------------------------------------------------
bool timestamp::operator==(const timestamp& other) const
{
return date::operator==(other)
&& time::operator==(other)
&& (milliseconds_ == other.milliseconds_);
}
//------------------------------------------------------------------------------
bool timestamp::operator!=(const timestamp& other) const
{
return !(*this == other);
}
//------------------------------------------------------------------------------
bool timestamp::operator<(const timestamp& other) const
{
if (date::operator!=(other))
return date::operator<(other);
if (time::operator!=(other))
return time::operator<(other);
return milliseconds_ < other.milliseconds_;
}
//------------------------------------------------------------------------------
bool timestamp::operator>(const timestamp& other) const
{
if (date::operator!=(other))
return date::operator>(other);
if (time::operator!=(other))
return time::operator>(other);
return milliseconds_ > other.milliseconds_;
}
//------------------------------------------------------------------------------
bool timestamp::operator<=(const timestamp& other) const
{
return !(*this > other);
}
//------------------------------------------------------------------------------
bool timestamp::operator>=(const timestamp& other) const
{
return !(*this < other);
}
//------------------------------------------------------------------------------
ostream& operator<<(ostream& out, const timestamp& ts)
{
out << ts.toString();
return out;
}
//------------------------------------------------------------------------------
} // namespace odbc

960
external/odbccpp/src/odbc/Types.h vendored Normal file
View File

@ -0,0 +1,960 @@
#ifndef ODBC_TYPES_H_INCLUDED
#define ODBC_TYPES_H_INCLUDED
//------------------------------------------------------------------------------
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>
#include <odbc/Config.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Specifies the type of an ODBC DSN.
*/
enum class DSNType
{
/**
* Indicates that both user and system DSNs will be returned.
*/
ALL,
/**
* Indicates that only system DSNs will be returned.
*/
SYSTEM,
/**
* Indicates that only user DSNs will be returned.
*/
USER
};
//------------------------------------------------------------------------------
/**
* Specifies the constants that identify ODBC SQL data types.
*/
class SQLDataTypes
{
SQLDataTypes() = delete;
public:
/// 64-bit integer value.
static constexpr int BigInt = -5;
/// Binary data of fixed length.
static constexpr int Binary = -2;
/// Single bit binary data.
static constexpr int Bit = -7;
/// Boolean value.
static constexpr int Boolean = 16;
/// Character string of fixed string length.
static constexpr int Char = 1;
/// Year, month, and day fields.
static constexpr int Date = 9;
/// Year, month, and day fields.
static constexpr int DateTime = 9;
/// Signed, exact, numeric value.
static constexpr int Decimal = 3;
/// Double-precision floating point number.
static constexpr int Double = 8;
/// Floating point number with driver-specific precision.
static constexpr int Float = 6;
/// Fixed length GUID.
static constexpr int Guid = -11;
/// 32-bit integer value.
static constexpr int Integer = 4;
/// Interval data type.
static constexpr int Interval = 10;
/// Variable length binary data.
static constexpr int LongVarBinary = -4;
/// Variable length character data.
static constexpr int LongVarChar = -1;
/// Signed, exact, numeric value.
static constexpr int Numeric = 2;
/// Single-precision floating point number.
static constexpr int Real = 7;
/// 16-bit integer value.
static constexpr int SmallInt = 5;
/// Hour, minute, and second fields.
static constexpr int Time = 10;
/// Year, month, day, hour, minute, and second fields.
static constexpr int Timestamp = 11;
/// 8-bit integer value.
static constexpr int TinyInt = -6;
/// Year, month, and day fields.
static constexpr int TypeDate = 91;
/// Hour, minute, and second fields.
static constexpr int TypeTime = 92;
/// Year, month, day, hour, minute, and second fields.
static constexpr int TypeTimestamp = 93;
/// Variable length binary data.
static constexpr int VarBinary = -3;
/// Variable-length character string.
static constexpr int VarChar = 12;
/// Unicode character string of fixed string length.
static constexpr int WChar = -8;
/// Unicode variable-length character data.
static constexpr int WLongVarChar = -10;
/// Unicode variable-length character string.
static constexpr int WVarChar = -9;
};
//------------------------------------------------------------------------------
/**
* Specifies isolation levels for transactions.
*/
enum class TransactionIsolationLevel
{
/**
* Indicates that any phenomena such as dirty reads, non-repeatable reads
* and phantoms can occur.
*/
READ_UNCOMMITTED,
/**
* Prevents from dirty reads, but non-repeatable reads and phantoms can
* occur.
*/
READ_COMMITTED,
/**
* Prevents from dirty and non-repeatable reads, but phantoms can occur.
*/
REPEATABLE_READ,
/**
* Prevents from any phenomena such as dirty reads, non-repeatable reads and
* phantoms.
*/
SERIALIZABLE,
/**
* Indicates that transactions are not supported by a database.
*/
NONE
};
//------------------------------------------------------------------------------
/**
* Represents a decimal number.
*
* A decimal number has a precision and a scale. The precision is the total
* number of digits used to represent a number. The scale is the number of
* digits after the decimal point.
*
* The maximum supported precision is 38. The scale must not be greater than the
* precision.
*/
class ODBC_EXPORT decimal
{
public:
/**
* Constructs a decimal with value 0, precision 1 and scale 1.
*/
decimal();
/**
* Constructs a decimal from a given integer value applying the given scale.
*
* Applying the scale means that the value is divided by 10^scale, e.g. 12
* is converted to 1.2 if the given scale is 1.
*
* @param value The value.
* @param precision The precision.
* @param scale The scale.
*/
decimal(std::int64_t value, std::uint8_t precision, std::uint8_t scale = 0);
/**
* Constructs a decimal from a given integer value applying the given scale.
*
* Applying the scale means that the value is divided by 10^scale, e.g. 12
* is converted to 1.2 if the given scale is 1.
*
* @param value The value.
* @param precision The precision.
* @param scale The scale.
*/
decimal(std::uint64_t value, std::uint8_t precision,
std::uint8_t scale = 0);
/**
* Constructs a decimal from a given value applying the given scale.
*
* Applying the scale means that the value is divided by 10^scale, e.g. 12
* is converted to 1.2 if the given scale is 1.
*
* @param value The value.
* @param precision The precision.
* @param scale The scale.
*/
decimal(const std::string& value, std::uint8_t precision,
std::uint8_t scale = 0);
/**
* Constructs a decimal from a given value applying the given scale.
*
* Applying the scale means that the value is divided by 10^scale, e.g. 12
* is converted to 1.2 if the given scale is 1.
*
* @param value The value.
* @param precision The precision.
* @param scale The scale.
*/
decimal(const char* value, std::uint8_t precision, std::uint8_t scale = 0);
/**
* Returns the precision of this decimal number.
*
* @return Returns the precision of this decimal number.
*/
std::uint8_t precision() const { return precision_; }
/**
* Returns the scale of this decimal number.
*
* @return Returns the scale of this decimal number.
*/
std::uint8_t scale() const { return scale_; }
/**
* Returns the signum of this decimal number.
*
* The signum is 1 if the number is positive, -1 if the number is negative,
* 0 if the number is 0.
*
* @return Returns the signum of this decimal number.
*/
std::int8_t signum() const;
/**
* Returns the unscaled value of this decimal.
*
* In the unscaled value, the decimal point is ommited.
*
* @return Returns the unscaled value of this decimal number.
*/
const char* unscaledValue() const { return value_.c_str(); };
/**
* Returns a string representation of this number.
*
* The string representation has the usual human-readable format with sign
* and decimal point.
*
* @return Returns the human-readable string representation of this value.
*/
std::string toString() const;
/**
* Checks whether this number is equal to another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is equal to the other number,
* false otherwise.
*/
bool operator==(const decimal& other) const;
/**
* Checks whether this number is not equal to another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is not equal to the other
* number, false otherwise.
*/
bool operator!=(const decimal& other) const;
/**
* Checks whether this number is less than another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is less than the other number,
* false otherwise.
*/
bool operator< (const decimal& other) const;
/**
* Checks whether this number is greater than another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is greater than the other
* number, false otherwise.
*/
bool operator> (const decimal& other) const;
/**
* Checks whether this number is not greater than another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is not greater than the other
* number, false otherwise.
*/
bool operator<=(const decimal& other) const;
/**
* Checks whether this number is not les than another number.
*
* Only the scaled values are considered by the comparison. Hence, numbers
* that have different scale and precisions can still be considered equal.
*
* @param other Another number.
* @return Returns true if this number is not less than the other
* number, false otherwise.
*/
bool operator>=(const decimal& other) const;
private:
int cmp(const decimal& other) const;
private:
std::string value_;
std::uint8_t precision_;
std::uint8_t scale_;
};
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& out, const decimal& d);
//------------------------------------------------------------------------------
/**
* Represents a date consisting of year, month and day in month.
*/
class ODBC_EXPORT date
{
friend class timestamp;
public:
/**
* Constructs a date with value 0000-01-01.
*/
date();
/**
* Constructs a date with the given year, month and day in month.
*
* @param year The year.
* @param month The month.
* @param day The day in month.
*/
date(int year, int month, int day);
public:
/**
* Returns the year of this date object.
*
* @return Returns the year of this date object.
*/
int year() const { return year_; }
/**
* Returns the month of this date object.
*
* @return Returns the month of this date object.
*/
int month() const { return month_; }
/**
* Returns the day of month of this date object.
*
* @return Returns the day of month of this date object.
*/
int day() const { return day_; }
/**
* Returns this date as a string in the format yyyy-mm-dd.
*
* @return Returns this date as a string in the format yyyy-mm-dd.
*/
std::string toString() const;
/**
* Checks whether this date is equal to another date.
*
* @param other Another date.
* @return Returns true if this date is equal to the other date, false
* otherwise.
*/
bool operator==(const date& other) const;
/**
* Checks whether this date is not equal to another date.
*
* @param other Another date.
* @return Returns true if this date is not equal to the other date,
* false otherwise.
*/
bool operator!=(const date& other) const;
/**
* Checks whether this date is before another date.
*
* @param other Another date.
* @return Returns true if this date is before the other date, false
* otherwise.
*/
bool operator< (const date& other) const;
/**
* Checks whether this date is after another date.
*
* @param other Another date.
* @return Returns true if this date is after the other date, false
* otherwise.
*/
bool operator> (const date& other) const;
/**
* Checks whether this date is not after another date.
*
* @param other Another date.
* @return Returns true if this date is not after the other date,
* false otherwise.
*/
bool operator<=(const date& other) const;
/**
* Checks whether this date is not before another date.
*
* @param other Another date.
* @return Returns true if this date is not before the other date,
* false otherwise.
*/
bool operator>=(const date& other) const;
private:
static int daysInMonth(int year, int month);
static int daysInFebruary(int year);
private:
std::int16_t year_;
std::uint8_t month_;
std::uint8_t day_;
};
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& out, const date& d);
//------------------------------------------------------------------------------
/**
* Represents a time consisting of hour, minute and second.
*/
class ODBC_EXPORT time
{
friend class timestamp;
public:
/**
* Constructs a time with value 00:00:00.
*/
time();
/**
* Constructs a time with the given hour, minute and second.
*
* @param hour The hour (0-23).
* @param minute The minute (0-59).
* @param second The second (0-59).
*/
time(int hour, int minute, int second);
public:
/**
* Returns the hour.
*
* @return Returns the hour.
*/
int hour() const { return hour_; }
/**
* Returns the minute.
*
* @return Returns the minute.
*/
int minute() const { return minute_; }
/**
* Returns the second.
*
* @return Returns the second.
*/
int second() const { return second_; }
/**
* Returns the time in the format hh:mm::ss.
*
* @return Returns the time in the format hh:mm::ss.
*/
std::string toString() const;
/**
* Checks whether this time is equal to another time.
*
* @param other Another time.
* @return Returns true if this time is equal to the other time, false
* otherwise.
*/
bool operator==(const time& other) const;
/**
* Checks whether this time is not equal to another time.
*
* @param other Another time.
* @return Returns true if this time is not equal to the other time,
* false otherwise.
*/
bool operator!=(const time& other) const;
/**
* Checks whether this time is before another time.
*
* @param other Another time.
* @return Returns true if this time is before the other time, false
* otherwise.
*/
bool operator< (const time& other) const;
/**
* Checks whether this time is after another time.
*
* @param other Another time.
* @return Returns true if this time is after the other time, false
* otherwise.
*/
bool operator> (const time& other) const;
/**
* Checks whether this time is not after another time.
*
* @param other Another time.
* @return Returns true if this time is not after the other time,
* false otherwise.
*/
bool operator<=(const time& other) const;
/**
* Checks whether this time is not before another time.
*
* @param other Another time.
* @return Returns true if this time is not before the other time,
* false otherwise.
*/
bool operator>=(const time& other) const;
private:
std::uint8_t hour_;
std::uint8_t minute_;
std::uint8_t second_;
};
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& out, const time& t);
//------------------------------------------------------------------------------
/**
* Represents a timestamp consisting of year, month, day in month, hour, minute,
* second and milliseconds.
*/
class ODBC_EXPORT timestamp : public date, public time
{
public:
/**
* Creates a timestamp with value 0000-01-01 00:00:00.000.
*/
timestamp();
/**
* Creates a timestamp with the given values.
*
* @param year The year.
* @param month The month.
* @param day The day.
* @param hour The hour.
* @param minute The minute.
* @param second The second.
* @param milliseconds The milliseconds.
*/
timestamp(int year, int month, int day, int hour, int minute, int second,
int milliseconds);
public:
/**
* Returns the milliseconds.
*
* @return Returns the milliseconds.
*/
int milliseconds() const { return milliseconds_; }
/**
* Returns the timestamp as string in the format yyyy-MM-dd HH:mm:ss.SSS.
*
* @return Returns the timestamp as string in the format
* yyyy-MM-dd HH:mm:ss.SSS.
*/
std::string toString() const;
/**
* Checks whether this timestamp is equal to another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is equal to the other
* timestamp, false otherwise.
*/
bool operator==(const timestamp& other) const;
/**
* Checks whether this timestamp is not equal to another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is not equal to the other
* timestamp, false otherwise.
*/
bool operator!=(const timestamp& other) const;
/**
* Checks whether this timestamp is before another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is before the other
* timestamp, false otherwise.
*/
bool operator< (const timestamp& other) const;
/**
* Checks whether this timestamp is after another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is after the other
* timestamp, false otherwise.
*/
bool operator> (const timestamp& other) const;
/**
* Checks whether this timestamp is not after another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is not after the other
* timestamp, false otherwise.
*/
bool operator<=(const timestamp& other) const;
/**
* Checks whether this timestamp is not before another timestamp.
*
* @param other Another timestamp.
* @return Returns true if this timestamp is not before the other
* timestamp, false otherwise.
*/
bool operator>=(const timestamp& other) const;
private:
std::uint16_t milliseconds_;
};
//------------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& out, const timestamp& ts);
//------------------------------------------------------------------------------
/**
* Wrapper class for types that don't have a dedicated NULL value.
*
* @tparam T The type to wrap.
*/
template<typename T>
class Nullable
{
public:
/**
* Constructs a NULL value.
*/
Nullable() : val_(), isNull_(true) { }
/**
* Constructs a non-NULL value.
*
* @param val The value to copy.
*/
Nullable(const T& val) : val_(val), isNull_(false) { }
/**
* Constructs a non-NULL value.
*
* @param val The value to move.
*/
Nullable(T&& val) : val_(std::move(val)), isNull_(false) { }
/**
* Copy constructor.
*
* @param other The instance to copy.
*/
Nullable(const Nullable<T>& other);
/**
* Move constructor.
*
* @param other The instance to move.
*/
Nullable(Nullable<T>&& other);
/**
* Copy assignment operator.
*
* @param other The instance to copy.
* @return Returns a reference to this instance.
*/
Nullable<T>& operator=(const Nullable<T>& other);
/**
* Move assignment operator.
*
* @param other The instance to move.
* @return Returns a reference to this instance.
*/
Nullable<T>& operator=(Nullable<T>&& other);
public:
/**
* Checks whether this value is NULL.
*
* @return Returns true if this value is NULL, false otherwise.
*/
bool isNull() const { return isNull_; }
/**
* Returns a constant reference to the wrapped value.
*
* @return Returns a constant reference to the wrapped value.
*/
const T& operator*() const { return val_; }
/**
* Returns a reference to the wrapped value.
*
* @return Returns a reference to the wrapped value.
*/
T& operator*() { return val_; }
/**
* Returns a constant pointer to the wrapped value.
*
* @return Returns a constant pointer to the wrapped value.
*/
const T* operator->() const { return &val_; }
/**
* Returns a pointer to the wrapped value.
*
* @return Returns a pointer to the wrapped value.
*/
T* operator->() { return &val_; }
/**
* Checks whether this value is equal to another value.
*
* @return Returns true if this value is equal to the other value, false
* otherwise.
*/
bool operator==(const Nullable<T>& other) const;
/**
* Checks whether this value is not equal to another value.
*
* @return Returns true if this value is not equal to the other value,
* false otherwise.
*/
bool operator!=(const Nullable<T>& other) const;
/**
* Checks whether this value is less than another value.
*
* A NULL value is never considered less than a non-NULL value.
*
* @return Returns true if this value is less than the other value, false
* otherwise.
*/
bool operator< (const Nullable<T>& other) const;
/**
* Checks whether this value is greater than another value.
*
* A NULL value is always considered greater than a non-NULL value.
*
* @return Returns true if this value is greater than the other value,
* false otherwise.
*/
bool operator> (const Nullable<T>& other) const;
/**
* Checks whether this value is not greater than another value.
*
* A NULL value is always considered greater than a non-NULL value.
*
* @return Returns true if this value is not greater than the other value,
* false otherwise.
*/
bool operator<=(const Nullable<T>& other) const;
/**
* Checks whether this value is not less than another value.
*
* A NULL value is never considered less than a non-NULL value.
*
* @return Returns true if this value is not less than the other value,
* false otherwise.
*/
bool operator>=(const Nullable<T>& other) const;
private:
T val_;
bool isNull_;
};
//------------------------------------------------------------------------------
template<typename T, typename... Args>
Nullable<T> makeNullable(Args&&... args)
{
return Nullable<T>(T(args...));
}
//------------------------------------------------------------------------------
template<typename T>
Nullable<T>::Nullable(const Nullable<T>& other)
: val_(other.val_)
, isNull_(other.isNull_)
{
}
//------------------------------------------------------------------------------
template<typename T>
Nullable<T>::Nullable(Nullable<T>&& other)
: val_(std::move(other.val_))
, isNull_(other.isNull_)
{
}
//------------------------------------------------------------------------------
template<typename T>
Nullable<T>& Nullable<T>::operator=(const Nullable<T>& other)
{
val_ = other.val_;
isNull_ = other.isNull_;
return *this;
}
//------------------------------------------------------------------------------
template<typename T>
Nullable<T>& Nullable<T>::operator=(Nullable<T>&& other)
{
val_ = std::move(other.val_);
isNull_ = other.isNull_;
return *this;
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator==(const Nullable<T>& other) const
{
if (isNull())
return other.isNull();
if (other.isNull())
return false;
return **this == *other;
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator!=(const Nullable<T>& other) const
{
return !(*this == other);
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator<(const Nullable<T>& other) const
{
if (isNull())
return false;
if (other.isNull())
return true;
return **this < *other;
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator>(const Nullable<T>& other) const
{
if (isNull())
return !other.isNull();
if (other.isNull())
return false;
return **this > *other;
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator<=(const Nullable<T>& other) const
{
return !(*this > other);
}
//------------------------------------------------------------------------------
template<typename T>
bool Nullable<T>::operator>=(const Nullable<T>& other) const
{
return !(*this < other);
}
//------------------------------------------------------------------------------
template<typename T>
std::ostream& operator<<(std::ostream& out, const Nullable<T>& val)
{
if (val.isNull())
out << "<NULL>";
else
out << *val;
return out;
}
//------------------------------------------------------------------------------
typedef Nullable<bool> Boolean;
typedef Nullable<std::int8_t> Byte;
typedef Nullable<std::uint8_t> UByte;
typedef Nullable<std::int16_t> Short;
typedef Nullable<std::uint16_t> UShort;
typedef Nullable<std::int32_t> Int;
typedef Nullable<std::uint32_t> UInt;
typedef Nullable<std::int64_t> Long;
typedef Nullable<std::uint64_t> ULong;
typedef Nullable<decimal> Decimal;
typedef Nullable<float> Float;
typedef Nullable<double> Double;
typedef Nullable<std::string> String;
typedef Nullable<std::u16string> NString;
typedef Nullable<std::vector<char>> Binary;
typedef Nullable<date> Date;
typedef Nullable<time> Time;
typedef Nullable<timestamp> Timestamp;
//------------------------------------------------------------------------------
template<>
inline std::ostream& operator<<(std::ostream& out, const NString& val)
{
if (val.isNull()) {
out << "<NULL>";
return out;
}
for (char16_t c : *val)
out << ((c <= u'~') ? (char)c : '?');
return out;
}
//------------------------------------------------------------------------------
template<>
inline std::ostream& operator<<(std::ostream& out, const Binary& val)
{
if (val.isNull()) {
out << "<NULL>";
return out;
}
const char* hexdigits = "0123456789ABCDEF";
for (std::size_t i = 0; i < val->size(); ++i)
{
if ((i % 16) == 0)
out << std::endl;
else if (((i + 8) % 16) == 0)
out << " ";
else
out << ' ';
unsigned char uc = (unsigned char)(*val)[i];
out << hexdigits[uc >> 4];
out << hexdigits[uc & 0xF];
}
return out;
}
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

60
external/odbccpp/src/odbc/Util.cpp vendored Normal file
View File

@ -0,0 +1,60 @@
#include <sstream>
#include <odbc/Util.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------------
void escape(const char* s, ostream& out)
{
while (*s)
{
switch (*s)
{
case '"':
out << "\"\"";
break;
default:
out << *s;
break;
}
++s;
}
}
//------------------------------------------------------------------------------
}
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
string Util::quote(const std::string& s)
{
return quote(s.c_str());
}
//------------------------------------------------------------------------------
string Util::quote(const char* s)
{
ostringstream os;
os << "\"";
escape(s, os);
os << "\"";
return os.str();
}
//------------------------------------------------------------------------------
string Util::quote(const std::string& schema, const std::string& table)
{
return quote(schema.c_str(), table.c_str());
}
//------------------------------------------------------------------------------
string Util::quote(const char* schema, const char* table)
{
ostringstream os;
os << "\"";
escape(schema, os);
os << "\".\"";
escape(table, os);
os << "\"";
return os.str();
}
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------

68
external/odbccpp/src/odbc/Util.h vendored Normal file
View File

@ -0,0 +1,68 @@
#ifndef ODBC_UTIL_H_INCLUDED
#define ODBC_UTIL_H_INCLUDED
//------------------------------------------------------------------------------
#include <string>
#include <odbc/Config.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Hosts various utility functions.
*/
class ODBC_EXPORT Util
{
public:
/**
* Quotes an identifier.
*
* The function adds a "-character at the start and end of the string. If
* the string contains a "-character, it is escaped by duplicating it, e.g.
* a"b becomes "a""b".
*
* @param s The identifier to quote.
* @return Returns the quoted identifier.
*/
static std::string quote(const std::string& s);
/**
* Quotes an identifier.
*
* The function adds a "-character at the start and end of the string. If
* the string contains a "-character, it is escaped by duplicating it, e.g.
* a"b becomes "a""b".
*
* @param s The identifier to quote.
* @return Returns the quoted identifier.
*/
static std::string quote(const char* s);
/**
* Quotes an schema/table name-pair.
*
* This function quotes the schema and table and separates then by a dot.
*
* @param schema The schema.
* @param table The table.
* @return Returns the quoted schema/table name-pair.
*/
static std::string quote(
const std::string& schema,
const std::string& table);
/**
* Quotes an schema/table name-pair.
*
* This function quotes the schema and table and separates then by a dot.
*
* @param schema The schema.
* @param table The table.
* @return Returns the quoted schema/table name-pair.
*/
static std::string quote(
const char* schema,
const char* table);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,380 @@
#include <odbc/Exception.h>
#include <odbc/internal/Batch.h>
#include <odbc/internal/Macros.h>
#include <odbc/internal/Odbc.h>
#include <odbc/internal/ParameterData.h>
#include <odbc/internal/TypeInfo.h>
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
// Batch class
//------------------------------------------------------------------------------
void Batch::addRow()
{
if (valueTypeInfos_.empty())
initialize();
else
checkAndCompleteValueTypes();
if (batchBlocks_.empty() || (blockRow_ == rowsPerBlock_))
{
// We might fetch the blocks from a Batch-object specific pool in a more
// sophisticated version.
batchBlocks_.emplace_back(rowsPerBlock_ * rowLength_);
blockRow_ = 0;
}
dataSize_ += rowLength_;
Block& block = batchBlocks_.back();
char* dest = block.getData() + rowLength_ * blockRow_;
for (size_t i = 0; i < parameters_.size(); ++i)
writeParameter(dest + paramDataOffsets_[i], parameters_[i]);
++blockRow_;
}
//------------------------------------------------------------------------------
void Batch::clear()
{
if (batchBlocks_.empty())
return;
// Before we can clear the vector with batch blocks, we have to deal with
// the buffers on the heap. For the last row, we have to return ownership
// to the parameter data. For previous rows, we have to delete the heap
// buffer.
for (size_t i = 0; i < parameters_.size(); ++i)
clearBatchParameter(i);
// We might want to put the blocks into a Batch-object specific block pool
// instead in a more sophisticated version.
batchBlocks_.clear();
blockRow_ = 0;
dataSize_ = 0;
}
//------------------------------------------------------------------------------
void Batch::execute(void* hstmt)
{
if (batchBlocks_.empty())
return;
// Ensure that the batch is cleared, even if an exception occurs.
Clearer clearer(*this);
NextRowInfo nextRowInfo(rowLength_);
for (size_t i = 0; i < (batchBlocks_.size() - 1); ++i)
{
bindBlockParameters(batchBlocks_[i].getData(), rowsPerBlock_, hstmt);
executeBlockBatch(batchBlocks_[i].getData(), rowsPerBlock_,
nextRowInfo, hstmt);
}
bindBlockParameters(batchBlocks_.back().getData(), blockRow_, hstmt);
executeBlockBatch(batchBlocks_.back().getData(), blockRow_,
nextRowInfo, hstmt);
}
//------------------------------------------------------------------------------
size_t Batch::getDataSize() const
{
return dataSize_;
}
//------------------------------------------------------------------------------
void Batch::writeParameter(char* dest, ParameterData& pd)
{
int16_t valueType = pd.getValueType();
size_t valueSize = TypeInfo::getSizeOfValueFromValueType(valueType);
if (valueSize == 0)
writeVariableSizeParameter(dest, pd);
else
writeFixedSizeParameter(dest, pd);
}
//------------------------------------------------------------------------------
void Batch::writeVariableSizeParameter(char* dest, ParameterData& pd)
{
if (pd.isNull())
{
memcpy(dest, pd.getLenIndPtr(), sizeof(SQLLEN));
}
else if (pd.getSize() <= ParameterData::INPLACE_BYTES)
{
memcpy(dest, pd.getLenIndPtr(), sizeof(SQLLEN));
dest += sizeof(SQLLEN);
memcpy(dest, pd.getData(), pd.getSize());
}
else
{
// We take over the ownership of the heap buffer and store a pointer
// to the data on the heap in the data field of the row
SQLLEN ind = SQL_LEN_DATA_AT_EXEC((SQLLEN)pd.getSize());
memcpy(dest, &ind, sizeof(SQLLEN));
dest += sizeof(SQLLEN);
const void* data = pd.getData();
memcpy(dest, &data, sizeof(data));
if (pd.ownsHeapBuffer())
pd.releaseHeapBufferOwnership();
dataSize_ += pd.getSize();
}
}
//------------------------------------------------------------------------------
void Batch::writeFixedSizeParameter(char* dest, ParameterData& pd)
{
memcpy(dest, pd.getLenIndPtr(), sizeof(SQLLEN));
if (!pd.isNull())
{
dest += sizeof(SQLLEN);
memcpy(dest, pd.getData(), pd.getSize());
}
}
//------------------------------------------------------------------------------
void Batch::clearBatchParameter(size_t index)
{
// We don't need to do anything for fixed size parameters
if (TypeInfo::getSizeOfValueFromValueType(valueTypeInfos_[index].type) != 0)
return;
ParameterData& pd = parameters_[index];
// If the current parameter uses a heap buffer and has transferred
// ownership to this batch, we must transfer back the ownership. Afterwards,
// we have to delete all other buffers on the heap. The same buffer might
// be used in several rows, so we have to take care to delete it only once.
// We use the fact that the same buffer can only be used in consecutive
// rows, so we can just remember the last one.
const void* preserve = nullptr;
const void* last = nullptr;
if (pd.usesHeapBuffer() && !pd.ownsHeapBuffer())
{
pd.restoreHeapBufferOwnership();
preserve = pd.getData();
}
size_t offset = paramDataOffsets_[index];
for (size_t i = 0; i < (batchBlocks_.size() - 1); ++i)
{
char* data = batchBlocks_[i].getData() + offset;
last = clearBatchParameterBlock(data, rowsPerBlock_, last, preserve);
}
char* data = batchBlocks_.back().getData() + offset;
clearBatchParameterBlock(data, blockRow_, last, preserve);
}
//------------------------------------------------------------------------------
const void* Batch::clearBatchParameterBlock(char* data, size_t numRows,
const void* last, const void* preserve)
{
for (size_t i = 0; i < numRows; ++i, data += rowLength_)
{
SQLLEN len;
memcpy(&len, data, sizeof(SQLLEN));
if ((len != SQL_NULL_DATA) && (len < 0))
{
void* buffer;
memcpy(&buffer, data + sizeof(SQLLEN), sizeof(void*));
if ((buffer != last) && (buffer != preserve))
{
free(buffer);
last = buffer;
}
}
}
return last;
}
//------------------------------------------------------------------------------
void Batch::bindBlockParameters(const char* blockData, size_t numRows,
void* hstmt)
{
EXEC_STMT(SQLFreeStmt, hstmt, SQL_UNBIND);
EXEC_STMT(SQLFreeStmt, hstmt, SQL_RESET_PARAMS);
EXEC_STMT(SQLSetStmtAttr, hstmt, SQL_ATTR_PARAM_BIND_TYPE,
(SQLPOINTER)rowLength_, SQL_IS_UINTEGER);
EXEC_STMT(SQLSetStmtAttr, hstmt, SQL_ATTR_PARAMSET_SIZE,
(SQLPOINTER)numRows, SQL_IS_UINTEGER);
for (size_t i = 0; i < valueTypeInfos_.size(); ++i)
{
const ValueTypeInfo& vti = valueTypeInfos_[i];
SQLLEN* lenInd = (SQLLEN*)(blockData + paramDataOffsets_[i]);
EXEC_STMT(SQLBindParameter, hstmt, (SQLUSMALLINT)(i + 1),
SQL_PARAM_INPUT, vti.type,
TypeInfo::getParamTypeForValueType(vti.type),
vti.columnSize, vti.decimalDigits,
(SQLPOINTER)(lenInd + 1), 0, lenInd);
}
}
//------------------------------------------------------------------------------
void Batch::executeBlockBatch(const char* blockData, size_t numRows,
NextRowInfo& nextRowInfo, void* hstmt)
{
SQLRETURN rc = SQLExecute(hstmt);
char* paramData = nullptr;
if (rc == SQL_NEED_DATA)
{
// For variable row size parameters, determine the first variable size
// value of the column.
for (size_t i = 0; i < valueTypeInfos_.size(); ++i)
{
size_t valueSize = TypeInfo::getSizeOfValueFromValueType(
valueTypeInfos_[i].type);
if (valueSize != 0)
continue;
size_t nextRow = findNextVarSizeRow(
blockData + paramDataOffsets_[i], 0, numRows);
nextRowInfo.setNextRow(paramDataOffsets_[i], nextRow);
}
rc = SQLParamData(hstmt, (SQLPOINTER*)&paramData);
}
while (rc == SQL_NEED_DATA)
{
// Compute the offset of the length/indicator field in the row
size_t offset = (paramData - sizeof(SQLLEN)) - blockData;
// Get the row to send
size_t nextRow = nextRowInfo.getNextRow(offset);
// Compute the pointer to the field value. This field contains the
// pointer to the buffer on the heap containing the actual data.
char* dataPtr = paramData + nextRow * rowLength_;
char* data;
memcpy(&data, dataPtr, sizeof(char*));
// Use the length/indicator field to compute the size of the data
SQLLEN len;
memcpy(&len, dataPtr - sizeof(SQLLEN), sizeof(SQLLEN));
len = SQL_LEN_DATA_AT_EXEC(len);
EXEC_STMT(SQLPutData, hstmt, data, len);
// Advance to the next row with a value on the heap in the current
// column
nextRow = findNextVarSizeRow(
blockData + offset, nextRow + 1, numRows);
nextRowInfo.setNextRow(offset, nextRow);
rc = SQLParamData(hstmt, (SQLPOINTER*)&paramData);
}
Exception::checkForError(rc, SQL_HANDLE_STMT, hstmt);
}
//------------------------------------------------------------------------------
size_t Batch::findNextVarSizeRow(const char* paramDataFirstRow, size_t startRow,
size_t numRows)
{
const char* data = paramDataFirstRow + startRow * rowLength_;
for (size_t row = startRow; row < numRows; ++row, data += rowLength_)
{
SQLLEN ind;
memcpy(&ind, data, sizeof(SQLLEN));
if ((ind != SQL_NULL_DATA) && (ind < 0))
return row;
}
return numRows;
}
//------------------------------------------------------------------------------
void Batch::initialize()
{
assert(!parameters_.empty());
valueTypeInfos_.resize(parameters_.size());
paramDataOffsets_.resize(parameters_.size());
dataSize_ = 0;
rowLength_ = 0;
for (size_t i = 0; i < parameters_.size(); ++i)
{
const ParameterData& param = parameters_[i];
assert(param.isInitialized());
valueTypeInfos_[i] = { param.getValueType(),
param.getColumnSize(), param.getDecimalDigits() };
paramDataOffsets_[i] = rowLength_;
rowLength_ += sizeof(SQLLEN);
size_t valueSize =
TypeInfo::getSizeOfValueFromValueType(param.getValueType());
rowLength_ +=
(valueSize == 0) ? ParameterData::INPLACE_BYTES : valueSize;
}
rowsPerBlock_ = BLOCK_SIZE / rowLength_;
if (rowsPerBlock_ < MIN_NUM_ROWS)
rowsPerBlock_ = MIN_NUM_ROWS;
}
//------------------------------------------------------------------------------
void Batch::checkAndCompleteValueTypes()
{
assert(parameters_.size() == valueTypeInfos_.size());
for (size_t i = 0; i < parameters_.size(); ++i)
{
const ParameterData& param = parameters_[i];
assert(param.isInitialized());
ValueTypeInfo& valTypeInfo = valueTypeInfos_[i];
ODBC_CHECK(
param.getValueType() == valTypeInfo.type,
"Value type of parameter " << (i + 1) << " does not match the "
"previous value type used in the batch. Before it was "
<< TypeInfo::getValueTypeName(valTypeInfo.type) << ", now it is "
<< TypeInfo::getValueTypeName(
param.getValueType()) << ".");
if (param.getValueType() == SQL_C_NUMERIC)
{
// columnSize and decimalDigits might not be set during the first
// call of addRow method. Therefore, we set their values here.
if (valTypeInfo.columnSize == 0)
{
valTypeInfo.columnSize = param.getColumnSize();
valTypeInfo.decimalDigits = param.getDecimalDigits();
}
ODBC_CHECK(
param.getColumnSize() == valTypeInfo.columnSize &&
param.getDecimalDigits() == valTypeInfo.decimalDigits,
"Precision and scale values of parameter " << (i + 1) << " do "
"not match the previous values used in the batch. Before it "
"was numeric(" << valTypeInfo.columnSize << "," <<
valTypeInfo.decimalDigits << "), now it is numeric(" <<
param.getColumnSize() << ", " << param.getDecimalDigits() <<
").");
}
// Update column size for types with variable size
if (TypeInfo::getSizeOfValueFromValueType(param.getValueType()) == 0)
{
valTypeInfo.columnSize =
max(valTypeInfo.columnSize, param.getColumnSize());
}
}
}
//------------------------------------------------------------------------------
// Batch::Block class
//------------------------------------------------------------------------------
Batch::Block::Block(size_t size)
{
data_ = (char*)malloc(size);
if (data_ == nullptr)
throw bad_alloc();
}
//------------------------------------------------------------------------------
Batch::Block::Block(Batch::Block&& other) : data_(other.data_)
{
other.data_ = nullptr;
}
//------------------------------------------------------------------------------
Batch::Block::~Block()
{
free(data_);
}
//------------------------------------------------------------------------------
// Batch::NextRowInfo class
//------------------------------------------------------------------------------
void Batch::NextRowInfo::setNextRow(size_t offset, size_t nextRow)
{
char* data = row_.getData() + offset;
memcpy(data, &nextRow, sizeof(size_t));
}
//------------------------------------------------------------------------------
size_t Batch::NextRowInfo::getNextRow(size_t offset) const
{
size_t ret;
const char* data = row_.getData() + offset;
memcpy(&ret, data, sizeof(size_t));
return ret;
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,225 @@
#ifndef ODBC_INTERNAL_BATCH_H_INCLUDED
#define ODBC_INTERNAL_BATCH_H_INCLUDED
//------------------------------------------------------------------------------
#include <cstddef>
#include <cstdint>
#include <vector>
#include <odbc/Forwards.h>
#include <odbc/RefCounted.h>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
class ParameterData;
//------------------------------------------------------------------------------
/**
* A batch of rows consisting of parameter values.
*
* A batch consists of blocks that are made up by rows.
*
* A row contains a length/indicator field and a value field for each parameter.
* The size of the value field depends on the value type used for the parameter.
* The value type must be the same for all parameter values in the same column.
*
* In case of fixed size value types, the fixed size is used as size for the
* value field. In case of variable size value types, the value of
* ParameterData::INPLACE_BYTES is used as size.
*
* When a row is added to the batch, fixed size values and variable size values
* with a size of at most ParameterData::INPLACE_BYTES are copied over to the
* row. The batch will take over the heap buffer ownership of parameter values
* that are larger than ParameterData::INPLACE_BYTES.
*/
class Batch : public RefCounted
{
/// The maximum size of a batch block in bytes.
constexpr static std::size_t BLOCK_SIZE = 256 * 1024;
/// Minimum number of rows per batch block, even if that results in blocks
/// larger than BLOCK_SIZE.
constexpr static std::size_t MIN_NUM_ROWS = 128;
class NextRowInfo;
public:
/**
* Constructor.
*
* @param parameters Reference to the PreparedStatement's parameters
* vector.
*/
Batch(std::vector<ParameterData>& parameters) : parameters_(parameters) { }
/**
* Destructor.
*/
~Batch() { clear(); }
public:
/**
* Adds a row using the current PreparedStatement's parameter values.
*
* For all but the first row this method will verify that the value types
* of the parameters matches the value types in the batch. If there is a
* mismatch, an exception will be thrown.
*/
void addRow();
/**
* Clears the batch.
*/
void clear();
/**
* Executes the batch.
*
* The batch will be cleared after execution, even if an error occurred.
*
* @param hstmt The ODBC statement andle.
*/
void execute(void* hstmt);
/**
* Retrieves the number of bytes required by the batch.
*
* @return Returns the number of bytes required by the batch.
*/
std::size_t getDataSize() const;
private:
void writeParameter(char* dest, ParameterData& pd);
void writeVariableSizeParameter(char* dest, ParameterData& pd);
void writeFixedSizeParameter(char* dest, ParameterData& pd);
void clearBatchParameter(std::size_t index);
const void* clearBatchParameterBlock(
char* data,
std::size_t numRows,
const void* last,
const void* preserve);
void bindBlockParameters(
const char* blockData,
std::size_t numRows,
void* hstmt);
void executeBlockBatch(
const char* blockData,
std::size_t numRows,
NextRowInfo& nextRowInfo,
void* hstmt);
size_t findNextVarSizeRow(
const char* paramDataFirstRow,
size_t startRow,
size_t numRows);
void initialize();
void checkAndCompleteValueTypes();
private:
/**
* A block of memory.
*
* Could be replaced by vector<char> with the desired size. However,
* vector<char> has the disadvantage that it unncecessarily initializes
* the allocated memory.
*/
class Block
{
public:
Block(std::size_t size);
Block(Block&& other);
~Block();
Block(const Block& other) = delete;
Block& operator=(const Block& other) = delete;
public:
char* getData() const { return data_; }
private:
char* data_;
};
/**
* Helper class that ensures that the batch is cleared.
*/
class Clearer
{
public:
Clearer(Batch& parent) : parent_(parent) { }
~Clearer() { parent_.clear(); }
private:
Batch& parent_;
};
/**
* Helper class storing the next row that contains a value in a heap buffer
* and requires SQLPutData() (for each column).
*
* Implementing this is a bit tricky. When binding parameters we have to
* give the pointer to the data in the first row because this pointer will
* be used by the ODBC driver to consume the inplace values.
*
* This pointer will also be returned by SQLParamData() when we need to send
* the data of a value in a heap buffer. We can easily compute the row
* offset, but finding the index of the parameter would require a lookup
* in some offset-to-index map. Instead we just allocate memory for a
* whole row and store the information on the next row at the offset we can
* easily compute.
*/
class NextRowInfo
{
public:
NextRowInfo(std::size_t rowLength) : row_(rowLength) { }
public:
void setNextRow(std::size_t offset, std::size_t nextRow);
std::size_t getNextRow(std::size_t offset) const;
private:
Block row_;
};
/**
* Helper class for storing parameter value information.
*/
struct ValueTypeInfo
{
std::int16_t type;
std::size_t columnSize;
std::int16_t decimalDigits;
};
private:
/// Reference to the PreparedStatement's parameters
std::vector<ParameterData>& parameters_;
/// Parameter value information of the columns. Initialized when the first
/// row is added to the batch.
std::vector<ValueTypeInfo> valueTypeInfos_;
/// Offsets of the length/indicator fields in a row, which is immediately
/// followed by the value field. Initialized when the first row is added to
/// the batch.
std::vector<std::size_t> paramDataOffsets_;
/// The length of a row. Initialized when the first row is added to the
/// batch.
std::size_t rowLength_;
/// Number of rows per block. Calculated when the first row is added to the
/// batch.
std::size_t rowsPerBlock_;
/// Vector with batch blocks. Will grow on demand.
std::vector<Block> batchBlocks_;
/// The number of rows in the last block (in batchBlocks_.back()).
std::size_t blockRow_;
/// The number of bytes required for the batch.
std::size_t dataSize_;
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,43 @@
#ifndef ODBC_INTERNAL_MACROS_H_INCLUDED
#define ODBC_INTERNAL_MACROS_H_INCLUDED
//------------------------------------------------------------------------------
#include <sstream>
//------------------------------------------------------------------------------
#define EXEC_DBC(function, handle, ...) \
do { \
SQLRETURN rc = function(handle, ##__VA_ARGS__); \
::odbc::Exception::checkForError(rc, SQL_HANDLE_DBC, handle); \
} while (false)
//------------------------------------------------------------------------------
#define EXEC_ENV(function, handle, ...) \
do { \
SQLRETURN rc = function(handle, ##__VA_ARGS__); \
::odbc::Exception::checkForError(rc, SQL_HANDLE_ENV, handle); \
} while (false)
//------------------------------------------------------------------------------
#define EXEC_STMT(function, handle, ...) \
do { \
SQLRETURN rc = function(handle, ##__VA_ARGS__); \
::odbc::Exception::checkForError(rc, SQL_HANDLE_STMT, handle); \
} while (false)
//------------------------------------------------------------------------------
#define ODBC_FAIL(msg) \
do { \
::std::ostringstream out; \
out << msg; \
throw ::odbc::Exception(out.str()); \
} while (false)
//------------------------------------------------------------------------------
#define ODBC_CHECK(condition, msg) \
do { \
if (!(condition)) { \
::std::ostringstream out; \
out << msg; \
throw ::odbc::Exception(out.str()); \
} \
} while (false)
//------------------------------------------------------------------------------
#define IS_FLAG_SET(value, flag) (((value & flag) == flag))
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,12 @@
#ifndef ODBC_INTERNAL_ODBC_H_INCLUDED
#define ODBC_INTERNAL_ODBC_H_INCLUDED
//------------------------------------------------------------------------------
#ifdef _WIN32
# define NOMINMAX
# define DNOMINMAX
# include <Windows.h>
#endif
#include <sql.h>
#include <sqlext.h>
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,190 @@
#include <new>
#include <odbc/internal/Odbc.h>
#include <odbc/internal/ParameterData.h>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <utility>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
ParameterData::ParameterData()
: state_(UNINITIALIZED)
, valueType_(0)
, columnSize_(0)
, decimalDigits_(0)
{
}
//------------------------------------------------------------------------------
ParameterData::ParameterData(ParameterData&& other)
: state_(other.state_)
, valueType_(other.valueType_)
, columnSize_(other.columnSize_)
, decimalDigits_(other.decimalDigits_)
, size_(other.size_)
{
switch (state_)
{
case UNINITIALIZED:
case IS_NULL:
break;
case NORMAL_INPLACE:
memcpy(inplaceData_, other.inplaceData_, size_);
break;
case NORMAL_HEAP_OWNING:
case NORMAL_HEAP_NOT_OWNING:
capacity_ = other.capacity_;
heapData_ = other.heapData_;
break;
}
other.state_ = UNINITIALIZED;
}
//------------------------------------------------------------------------------
ParameterData::~ParameterData()
{
if (state_ == NORMAL_HEAP_OWNING)
free(heapData_);
}
//------------------------------------------------------------------------------
ParameterData& ParameterData::operator=(ParameterData&& other)
{
if (this == &other)
return *this;
if (state_ == NORMAL_HEAP_OWNING)
free(heapData_);
state_ = other.state_;
valueType_ = other.valueType_;
columnSize_ = other.columnSize_;
decimalDigits_ = other.decimalDigits_;
size_ = other.size_;
switch (state_)
{
case UNINITIALIZED:
break;
case IS_NULL:
break;
case NORMAL_INPLACE:
memcpy(inplaceData_, other.inplaceData_, size_);
break;
case NORMAL_HEAP_OWNING:
case NORMAL_HEAP_NOT_OWNING:
capacity_ = other.capacity_;
heapData_ = other.heapData_;
break;
}
other.state_ = UNINITIALIZED;
return *this;
}
//------------------------------------------------------------------------------
void ParameterData::setValue(int16_t type, const void* value, size_t size)
{
// TODO: Maybe we'd like to set the value on the heap even if it fitted into
// the inplace buffer. This would avoid re-allocations if a value slightly
// larger then the inplace buffer is set.
if (size <= INPLACE_BYTES)
setValueInplace(value, size);
else
setValueOnHeap(value, size);
valueType_ = type;
columnSize_ = 0;
decimalDigits_ = 0;
}
//------------------------------------------------------------------------------
void ParameterData::setNull(int16_t type)
{
if (state_ == NORMAL_HEAP_OWNING)
free(heapData_);
valueType_ = type;
state_ = IS_NULL;
size_ = SQL_NULL_DATA;
}
//------------------------------------------------------------------------------
void ParameterData::clear()
{
if (state_ == NORMAL_HEAP_OWNING)
free(heapData_);
state_ = UNINITIALIZED;
}
//------------------------------------------------------------------------------
const void* ParameterData::getData() const
{
switch (state_)
{
case UNINITIALIZED:
case IS_NULL:
assert(false);
return nullptr;
case NORMAL_INPLACE:
return inplaceData_;
case NORMAL_HEAP_OWNING:
case NORMAL_HEAP_NOT_OWNING:
return heapData_;
}
return nullptr;
}
//------------------------------------------------------------------------------
bool ParameterData::usesHeapBuffer() const
{
return (state_ == NORMAL_HEAP_OWNING) || (state_ == NORMAL_HEAP_NOT_OWNING);
}
//------------------------------------------------------------------------------
void ParameterData::releaseHeapBufferOwnership()
{
assert(state_ == NORMAL_HEAP_OWNING);
state_ = NORMAL_HEAP_NOT_OWNING;
}
//------------------------------------------------------------------------------
void ParameterData::restoreHeapBufferOwnership()
{
assert(state_ == NORMAL_HEAP_NOT_OWNING);
state_ = NORMAL_HEAP_OWNING;
}
//------------------------------------------------------------------------------
void ParameterData::setValueInplace(const void* value, size_t size)
{
if (state_ == NORMAL_HEAP_OWNING)
free(heapData_);
state_ = NORMAL_INPLACE;
size_ = size;
memcpy(inplaceData_, value, size);
}
//------------------------------------------------------------------------------
void ParameterData::setValueOnHeap(const void* value, size_t size)
{
if (state_ != NORMAL_HEAP_OWNING)
{
void* data = malloc(size);
if (data == nullptr)
throw bad_alloc();
memcpy(data, value, size);
capacity_ = size;
heapData_ = data;
state_ = NORMAL_HEAP_OWNING;
size_ = size;
return;
}
size_t reallocIfLess = (size_t)(LOAD_FACTOR * (double)capacity_);
if ((size > capacity_) || (size < reallocIfLess))
{
void* data = malloc(size);
if (data == nullptr)
throw bad_alloc();
memcpy(data, value, size);
free(heapData_);
capacity_ = size;
heapData_ = data;
size_ = size;
return;
}
memcpy(heapData_, value, size);
size_ = size;
}
//------------------------------------------------------------------------------
} // namespace odbc

View File

@ -0,0 +1,284 @@
#ifndef ODBC_INTERNAL_PARAMETERDATA_H_INCLUDED
#define ODBC_INTERNAL_PARAMETERDATA_H_INCLUDED
//------------------------------------------------------------------------------
#include <cstddef>
#include <cinttypes>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Class that holds the data of a parameter.
*
* The data of a parameter consists of its type and its value, which can be
* NULL.
*
* This class has a small inplace buffer that can hold numeric, date/time-
* related types and short strings without the need for additional memory
* allocation from the heap. Larger values are allocated from the heap.
*
* The data of this class is intended to be transferrable to a block with batch
* data. Values in the inplace buffer are just copied over. If a value is
* located in a buffer allocated from the heap, the ownership of the heap buffer
* is transferred to the batch block.
*
* As the data in the buffer can be re-added (i.e. without the user setting a
* new value for the next row), we have to keep the pointer to the data, but
* have to mark that we don't own the heap buffer anymore.
*
* Ownership of the buffer can also be transferred back to a ParameterData
* object. This happens if a batch is executed/cleared and a ParameterData
* object holds a pointer to the memory buffer in the batch.
*/
class ParameterData
{
public:
// Size of the inplace buffer
static constexpr std::size_t INPLACE_BYTES = 32;
// Required load factor so that no re-allocation occurs if a buffer
// allocated from the heap receives new content.
static constexpr double LOAD_FACTOR = 0.75;
public:
/**
* Creates an uninitialized instance.
*/
ParameterData();
ParameterData(const ParameterData& other) = delete;
/**
* Creates an instance and moves the data of another instance over to this
* instance.
*
* @param other Another instance.
*/
ParameterData(ParameterData&& other);
/**
* Destructor.
*/
~ParameterData();
ParameterData& operator=(const ParameterData& other) = delete;
/**
* Moves the parameter data of another instance to this instance.
*
* @param other Another instance.
* @return Returns a reference to this instance.
*/
ParameterData& operator=(ParameterData&& other);
public:
/**
* Sets the type and value of the parameter.
*
* @param type The ODBC-C-type of the value.
* @param value Pointer to the value.
* @param size Size of the value in bytes.
*/
void setValue(std::int16_t type, const void* value, std::size_t size);
/**
* Sets the type of this parameter and sets the value to NULL.
*
* @param type The ODBC-C-type of the value.
*/
void setNull(std::int16_t type);
/**
* Clears the parameter.
*
* The instance will be uninitialized after this method has been called.
*/
void clear();
/**
* Checks whether the value has been initialized.
*
* @return True if the value has been initialized, false otherwise.
*/
bool isInitialized() const { return state_ != UNINITIALIZED; }
/**
* Returns the ODBC-C-type of the value.
*
* @return The ODBC-C-type of the value.
*/
std::int16_t getValueType() const { return valueType_; }
/**
* Returns the size of the parameter. If the parameter type is SQL_DECIMAL,
* SQL_NUMERIC, SQL_FLOAT, SQL_REAL or SQL_DOUBLE the returned value
* represents the maximum precision of the corrresponding parameter. If the
* parameter type is SQL_CHAR, SQL_VARCHAR, SQL_LONGVARCHAR, SQL_BINARY,
* SQL_VARBINARY, SQL_LONGVARBINARY, SQL_TYPE_DATE, SQL_TYPE_TIME or
* SQL_TYPE_TIMESTAMP the returned value represents the maximum length of
* the corresponding parameter in bytes.
*
* @return The column size.
*/
std::size_t getColumnSize() const { return columnSize_; }
/**
* Sets the column size of this parameter. If the parameter type is
* SQL_DECIMAL, SQL_NUMERIC, SQL_FLOAT, SQL_REAL or SQL_DOUBLE the value
* represents the maximum precision of the corrresponding parameter. If the
* parameter type is SQL_CHAR, SQL_VARCHAR, SQL_LONGVARCHAR, SQL_BINARY,
* SQL_VARBINARY, SQL_LONGVARBINARY, SQL_TYPE_DATE, SQL_TYPE_TIME or
* SQL_TYPE_TIMESTAMP the value represents the maximum length of the
* corresponding parameter in bytes.
*
* @param value The column size.
*/
void setColumnSize(std::size_t value) { columnSize_ = value; }
/**
* Returns the number of decimal digits of this parameter. If the parameter
* type is SQL_DECIMAL or SQL_NUMERIC the value represents the scale of the
* corresponding parameter. If the parameter type is SQL_TYPE_TIME or
* SQL_TYPE_TIMESTAMP the value represents the precision of the
* corresponding parameter.
*
* @return The number of decimal digits.
*/
std::int16_t getDecimalDigits() const { return decimalDigits_; }
/**
* Sets the number of decimal digits of this parameter. If the parameter
* type is SQL_DECIMAL or SQL_NUMERIC the value represents the scale of the
* corresponding parameter. If the parameter type is SQL_TYPE_TIME or
* SQL_TYPE_TIMESTAMP the value represents the precision of the
* corresponding parameter.
*
* @param value The number of decimal digits.
*/
void setDecimalDigits(std::int16_t value) { decimalDigits_ = value; }
/**
* Checks whether the value is NULL.
*
* @return True if the value is NULL, false otherwise.
*/
bool isNull() const { return state_ == IS_NULL; }
/**
* Returns a pointer to the data.
*
* The result is undefined if the value is either uninitialized or NULL.
*
* @return Returns a pointer to the data.
*/
const void* getData() const;
/**
* Returns the size of the data in bytes.
*
* The result is undefined if the value is either uninitialized or NULL.
*
* @return The size of the data in bytes.
*/
std::size_t getSize() const { return size_; }
/**
* Returns a pointer to the value's length or NULL indicator.
*
* The result is undefined if the value is uninitialized.
*
* @return A pointer to the value's length or NULL indicator.
*/
const void* getLenIndPtr() const { return &size_; }
/**
* Checks whether the data is located in a buffer on the heap.
*
* @return True if the data is located in a buffer on the heap, false
* if the data is in the inplace buffer or if the value is NULL.
*/
bool usesHeapBuffer() const;
/**
* Checks whether this instance owns a buffer on the heap.
*
* @return True if this instance owns a buffer on the heap, false
* otherwise.
*/
bool ownsHeapBuffer() const { return state_ == NORMAL_HEAP_OWNING; }
/**
* Returns the capacity of the buffer on the heap.
*
* This method must only be called if usesHeapBuffer() returns true.
*
* @return Returns the capacity of the buffer on the heap.
*/
std::size_t getHeapBufferCapacity() const { return capacity_; }
/**
* Releases the heap buffer ownership.
*
* This method must only be called if ownsHeapBuffer() returns true.
* Afterwards the caller is responsible for the buffer on the heap. This
* means the caller has either to free that memory or he has to return
* ownership to this instance via restoreHeapBufferOwnership().
*
* The pointer to the buffer can be retrieved via getData().
*/
void releaseHeapBufferOwnership();
/**
* Restores the heap buffer ownership.
*
* This method must only be called if usesHeapBuffer() return true and
* ownsHeapBuffer() returns false.
*
* After calling this method, this instance will manage the lifetime of the
* heap buffer again.
*/
void restoreHeapBufferOwnership();
private:
void setValueInplace(const void* value, std::size_t size);
void setValueOnHeap(const void* value, std::size_t size);
private:
enum State : std::uint8_t
{
/// Uninitialized
UNINITIALIZED,
/// Value is NULL
IS_NULL,
/// Normal value stored in-place
NORMAL_INPLACE,
/// Normal value stored in buffer on heap that is ownerd by this object
NORMAL_HEAP_OWNING,
/// Normal value stored in buffer on heap whose ownership has been
/// transferred
NORMAL_HEAP_NOT_OWNING,
};
private:
// We have the following invariants:
// - inplaceData_ is the active union member iff state_ is NORMAL_INPLACE
// - the structure is the active union member iff state_ is
// NORMAL_HEAP_OWNING or NORMAL_HEAP_NOT_OWNING
State state_;
std::int16_t valueType_;
std::size_t columnSize_;
std::int16_t decimalDigits_;
std::size_t size_;
union
{
char inplaceData_[INPLACE_BYTES];
struct
{
std::size_t capacity_;
void* heapData_;
};
};
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,272 @@
#ifndef ODBC_INTERNAL_TYPEINFO_H_INCLUDED
#define ODBC_INTERNAL_TYPEINFO_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Types.h>
#include <odbc/internal/Odbc.h>
#include <cassert>
#include <cstdint>
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
/**
* Structure that contains the ODBC value type and parameter type for a C++
* type.
*
* @tparam T The C++ type.
*/
template<typename T>
struct TypeToOdbc
{
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<bool>
{
constexpr static std::int16_t VALUETYPE = SQL_C_BIT;
constexpr static std::int16_t PARAMTYPE = SQL_BIT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::uint8_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_UTINYINT;
constexpr static std::int16_t PARAMTYPE = SQL_TINYINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::int8_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_STINYINT;
constexpr static std::int16_t PARAMTYPE = SQL_TINYINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::uint16_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_USHORT;
constexpr static std::int16_t PARAMTYPE = SQL_SMALLINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::int16_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_SSHORT;
constexpr static std::int16_t PARAMTYPE = SQL_SMALLINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::uint32_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_ULONG;
constexpr static std::int16_t PARAMTYPE = SQL_INTEGER;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::int32_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_SLONG;
constexpr static std::int16_t PARAMTYPE = SQL_INTEGER;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::uint64_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_UBIGINT;
constexpr static std::int16_t PARAMTYPE = SQL_BIGINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<std::int64_t>
{
constexpr static std::int16_t VALUETYPE = SQL_C_SBIGINT;
constexpr static std::int16_t PARAMTYPE = SQL_BIGINT;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<float>
{
constexpr static std::int16_t VALUETYPE = SQL_C_FLOAT;
constexpr static std::int16_t PARAMTYPE = SQL_REAL;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<double>
{
constexpr static std::int16_t VALUETYPE = SQL_C_DOUBLE;
constexpr static std::int16_t PARAMTYPE = SQL_DOUBLE;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<date>
{
constexpr static std::int16_t VALUETYPE = SQL_C_TYPE_DATE;
constexpr static std::int16_t PARAMTYPE = SQL_TYPE_DATE;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<time>
{
constexpr static std::int16_t VALUETYPE = SQL_C_TYPE_TIME;
constexpr static std::int16_t PARAMTYPE = SQL_TYPE_TIME;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<timestamp>
{
constexpr static std::int16_t VALUETYPE = SQL_C_TYPE_TIMESTAMP;
constexpr static std::int16_t PARAMTYPE = SQL_TYPE_TIMESTAMP;
};
//------------------------------------------------------------------------------
template<>
struct TypeToOdbc<decimal>
{
constexpr static std::int16_t VALUETYPE = SQL_C_NUMERIC;
constexpr static std::int16_t PARAMTYPE = SQL_DECIMAL;
};
//------------------------------------------------------------------------------
struct TypeInfo
{
/**
* Returns the parameter type to use for a given value type.
*
* @param valueType The ODBC value type.
* @return The parameter type to use for a given value type.
*/
static std::int16_t getParamTypeForValueType(std::int16_t valueType)
{
switch (valueType)
{
case SQL_C_CHAR:
return SQL_LONGVARCHAR;
case SQL_C_WCHAR:
return SQL_WLONGVARCHAR;
case SQL_C_SSHORT:
case SQL_C_USHORT:
return SQL_SMALLINT;
case SQL_C_SLONG:
case SQL_C_ULONG:
return SQL_INTEGER;
case SQL_C_FLOAT:
return SQL_REAL;
case SQL_C_DOUBLE:
return SQL_DOUBLE;
case SQL_C_BIT:
return SQL_BIT;
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
return SQL_TINYINT;
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
return SQL_BIGINT;
case SQL_C_BINARY:
return SQL_LONGVARBINARY;
case SQL_C_TYPE_DATE:
return SQL_TYPE_DATE;
case SQL_C_TYPE_TIME:
return SQL_TYPE_TIME;
case SQL_C_TYPE_TIMESTAMP:
return SQL_TYPE_TIMESTAMP;
case SQL_C_NUMERIC:
return SQL_DECIMAL;
}
assert(false);
return SQL_UNKNOWN_TYPE;
}
/**
* Returns a human-readable name for a value type.
*
* @param valueType The ODBC value type.
* @return A human-readable name for the value type.
*/
static const char* getValueTypeName(std::int16_t valueType)
{
switch (valueType)
{
case SQL_C_CHAR:
return "CLOB";
case SQL_C_WCHAR:
return "NCLOB";
case SQL_C_SSHORT:
case SQL_C_USHORT:
return "SHORT";
case SQL_C_SLONG:
case SQL_C_ULONG:
return "INTEGER";
case SQL_C_FLOAT:
return "REAL";
case SQL_C_DOUBLE:
return "DOUBLE";
case SQL_C_BIT:
return "BOOLEAN";
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
return "TINYINT";
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
return "BIGINT";
case SQL_C_BINARY:
return "BLOB";
case SQL_C_TYPE_DATE:
return "DATE";
case SQL_C_TYPE_TIME:
return "TIME";
case SQL_C_TYPE_TIMESTAMP:
return "TIMESTAMP";
case SQL_C_NUMERIC:
return "DECIMAL";
}
assert(false);
return "<unknown>";
}
/**
* Returns a value's size in bytes of a given value type.
*
* This method will return 0 if values of the given type are not of fixed
* size.
*
* @param valueType The ODBC value type.
* @return A value's size in bytes of a given value type or 0 if a
* value of this type does not have a fixed size.
*/
static std::size_t getSizeOfValueFromValueType(std::int16_t valueType)
{
switch (valueType)
{
case SQL_C_CHAR:
case SQL_C_WCHAR:
case SQL_C_BINARY:
return 0;
case SQL_C_BIT:
case SQL_C_STINYINT:
case SQL_C_UTINYINT:
return 1;
case SQL_C_SSHORT:
case SQL_C_USHORT:
return 2;
case SQL_C_SLONG:
case SQL_C_ULONG:
case SQL_C_FLOAT:
return 4;
case SQL_C_SBIGINT:
case SQL_C_UBIGINT:
case SQL_C_DOUBLE:
return 8;
case SQL_C_TYPE_DATE:
return sizeof(SQL_DATE_STRUCT);
case SQL_C_TYPE_TIME:
return sizeof(SQL_TIME_STRUCT);
case SQL_C_TYPE_TIMESTAMP:
return sizeof(SQL_TIMESTAMP_STRUCT);
case SQL_C_NUMERIC:
return sizeof(SQL_NUMERIC_STRUCT);
}
assert(false);
return 0;
}
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -0,0 +1,114 @@
#include <algorithm>
#include <cstring>
#include <odbc/internal/Odbc.h>
#include <odbc/internal/UtilInternal.h>
//------------------------------------------------------------------------------
using namespace std;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
static uint32_t fromLittleEndianArray(const SQLCHAR* number)
{
uint32_t ret =
(static_cast<unsigned char>(number[0]) << 0)
| (static_cast<unsigned char>(number[1]) << 8)
| (static_cast<unsigned char>(number[2]) << 16)
| (static_cast<unsigned char>(number[3]) << 24);
return ret;
}
//------------------------------------------------------------------------------
static void toLittleEndianArray(uint32_t val, SQLCHAR* target)
{
target[0] = val & 0xFF;
target[1] = (val >> 8) & 0xFF;
target[2] = (val >> 16) & 0xFF;
target[3] = (val >> 24) & 0xFF;
}
//------------------------------------------------------------------------------
template<int N>
static bool allZero(const uint32_t(&number)[N])
{
for (int i = 0; i < N; ++i) {
if (number[i] != 0)
return false;
}
return true;
}
//------------------------------------------------------------------------------
void UtilInternal::numericToString(const SQL_NUMERIC_STRUCT& num, char* str)
{
static_assert((SQL_MAX_NUMERIC_LEN % 4) == 0,
"SQL_MAX_NUMERIC_LEN is not a multiple of 4");
// Translate to base 2^32
constexpr int NUM_DIGITS = SQL_MAX_NUMERIC_LEN / 4;
uint32_t digits[NUM_DIGITS];
for (int i = 0; i < NUM_DIGITS; ++i) {
digits[i] = fromLittleEndianArray(num.val + 4 * i);
}
// Perform an Euclidean division by 10
char* pos = str;
while (!allZero(digits))
{
uint64_t carry = 0;
for (int i = NUM_DIGITS - 1; i >= 0; --i)
{
uint64_t with_carry = digits[i] + (carry << 32);
digits[i] = static_cast<uint32_t>(with_carry / 10);
carry = with_carry % 10;
}
*pos = '0' + static_cast<char>(carry);
++pos;
}
// Treat the special case in which the number is 0
if (pos == str) {
*pos = '0';
++pos;
} else if (num.sign == 0) {
// Append the sign (only if not 0)
*pos = '-';
++pos;
}
// Reverse and terminate the string
reverse(str, pos);
*pos = '\0';
}
//------------------------------------------------------------------------------
void UtilInternal::decimalToNumeric(const decimal& dec, SQL_NUMERIC_STRUCT& num)
{
static_assert((SQL_MAX_NUMERIC_LEN % 4) == 0,
"SQL_MAX_NUMERIC_LEN is not a multiple of 4");
num.scale = dec.scale();
num.precision = dec.precision();
num.sign = (dec.signum() == -1) ? 0 : 1;
constexpr int NUM_DIGITS = SQL_MAX_NUMERIC_LEN / 4;
uint32_t digits[NUM_DIGITS];
memset(digits, 0, NUM_DIGITS * sizeof(uint32_t));
const char* pos = dec.unscaledValue();
// Skip the sign
if (dec.signum() == -1)
++pos;
while (*pos)
{
uint64_t value = *pos - '0';
for (int i = 0; i < NUM_DIGITS; i++)
{
value = value + static_cast<uint64_t>(digits[i]) * 10;
digits[i] = static_cast<uint32_t>(value);
value = value >> 32;
}
++pos;
}
for (int i = 0; i < NUM_DIGITS; ++i) {
toLittleEndianArray(digits[i], num.val + 4 * i);
}
}
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------

View File

@ -0,0 +1,20 @@
#ifndef ODBC_INTERNAL_UTILINTERNAL_H_INCLUDED
#define ODBC_INTERNAL_UTILINTERNAL_H_INCLUDED
//------------------------------------------------------------------------------
#include <odbc/Types.h>
#include <cinttypes>
//------------------------------------------------------------------------------
typedef struct tagSQL_NUMERIC_STRUCT SQL_NUMERIC_STRUCT;
//------------------------------------------------------------------------------
namespace odbc {
//------------------------------------------------------------------------------
class UtilInternal
{
public:
static void numericToString(const SQL_NUMERIC_STRUCT& num, char* str);
static void decimalToNumeric(const decimal& dec, SQL_NUMERIC_STRUCT& num);
};
//------------------------------------------------------------------------------
} // namespace odbc
//------------------------------------------------------------------------------
#endif

View File

@ -1,5 +1,3 @@
FIND_PACKAGE(ODBCCPP REQUIRED)
########################################################
# Files
@ -76,6 +74,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/gui/codeeditors
${CMAKE_SOURCE_DIR}/external
${CMAKE_SOURCE_DIR}/external/nlohmann
${CMAKE_SOURCE_DIR}/external/odbccpp/src
${CMAKE_BINARY_DIR}/src/core
${CMAKE_BINARY_DIR}/src/gui
@ -86,7 +85,6 @@ INCLUDE_DIRECTORIES (SYSTEM
${PROJ_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${ODBC_INCLUDE_DIRS}
${ODBCCPP_INCLUDE_DIR}
${QCA_INCLUDE_DIR}
${QTKEYCHAIN_INCLUDE_DIR}
)
@ -99,13 +97,13 @@ TARGET_COMPILE_DEFINITIONS(hanaprovider_a PUBLIC ODBC_STATIC)
TARGET_LINK_LIBRARIES(hanaprovider
qgis_core
${ODBCCPP_LIBRARY}
odbccpp
${ODBC_LIBRARIES}
)
TARGET_LINK_LIBRARIES(hanaprovider_a
qgis_core
${ODBCCPP_LIBRARY}
odbccpp
${ODBC_LIBRARIES}
)