mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Squashed 'src/soci/' content from commit 6e9312c
git-subtree-dir: src/soci git-subtree-split: 6e9312c4bb3748907bd28d62c40feca42878cfef
This commit is contained in:
20
src/.gitignore
vendored
Normal file
20
src/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
*~
|
||||
*.kdev[0-9]
|
||||
*.swp
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
confdefs.h
|
||||
config.guess
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4
|
||||
missing
|
||||
Makefile
|
||||
Makefile.in
|
||||
tmp
|
||||
73
src/AUTHORS
Normal file
73
src/AUTHORS
Normal file
@@ -0,0 +1,73 @@
|
||||
=== SOCI Team ===
|
||||
|
||||
Alex Ott
|
||||
David Courtney
|
||||
Denis Arnaud
|
||||
Denis Chapligin
|
||||
Julian Taylor
|
||||
Maciej Sobczak
|
||||
Mateusz Łoskot
|
||||
Paweł Aleksander Fedoryński
|
||||
Rafał Bobrowski
|
||||
Sergei Nikulov
|
||||
Steve Hutton
|
||||
Vadim Zeitlin
|
||||
Viacheslav Naydenov
|
||||
|
||||
=== SOCI Contributors ===
|
||||
|
||||
We would like to thank you for your contributions
|
||||
- they allowed us to improve the quality of the SOCI library:
|
||||
|
||||
Andrey Belobrov
|
||||
Andrey Utkin
|
||||
Andriy Gapon
|
||||
Artyom Beilis
|
||||
Artyom Tonkikh
|
||||
avg-I
|
||||
b-s-a
|
||||
Brian R. Toonen
|
||||
Cengizhan Paşaoğlu
|
||||
Chris Weed
|
||||
Cory Bennett
|
||||
creste
|
||||
Daniel Lidström
|
||||
Eli Green
|
||||
Faik Uygur
|
||||
Fedor Trushkin
|
||||
Frank Bielig
|
||||
Frederic Chateau
|
||||
Gevorg Voskanyan
|
||||
Henning Basold
|
||||
Howard Butler
|
||||
Ilia Barahovski
|
||||
Jakub Stachowski
|
||||
jamercee
|
||||
Joerg Sonnenberger
|
||||
Krzysztof Bieleń
|
||||
Mario Valesco
|
||||
Martin Muenstermann
|
||||
Mathias Hasselmann
|
||||
Matt Arsenault
|
||||
Matthieu Kermagoret
|
||||
Michael Davidsaver
|
||||
Mika Fischer
|
||||
Paul Bondo
|
||||
Petr Vanek
|
||||
Philip Pemberton
|
||||
Rainer Bauer
|
||||
Ricardo Andrade
|
||||
ryandesign
|
||||
Robert Massaioli
|
||||
Roger Orr
|
||||
Sergey Belyashov
|
||||
Shridhar Daithankar
|
||||
Sören Meyer-Eppler
|
||||
soyersoyer
|
||||
Stefan Chrobot
|
||||
Tomasz Olszewski
|
||||
Vaclav Slavik
|
||||
xol
|
||||
|
||||
There are a lot of people that help to drive SOCI project forward,
|
||||
if we have forgot to mention someone in here, send us an email!
|
||||
338
src/CHANGES
Normal file
338
src/CHANGES
Normal file
@@ -0,0 +1,338 @@
|
||||
This file contains the history of changes in the SOCI library.
|
||||
|
||||
---
|
||||
Version 3.2.2 differs from 3.2.1 in the following ways:
|
||||
|
||||
- Fixed once_temp_type destructor with noexcept(false) specifier for C++11
|
||||
- Fix uninitialized indicators in conversion_into_type and conversion_use_type specialisations
|
||||
- Fixed placeholder matching for PostgreSQL-style casts with ORM
|
||||
- Fixed memory leaking in use binding in case of bind/unbind sequence
|
||||
- Fixed sscanf formatter for MinGW/MSVC in backends
|
||||
- Fixed partial placeholder name matching for ORM cases
|
||||
- Added test for use of indicators with rowset<row>
|
||||
- Added test for get_affected_rows after bulk operations
|
||||
|
||||
- DB2
|
||||
-- Enable build and testing on Travis CI
|
||||
-- Fixed use type size calculation during bind/pre_use
|
||||
|
||||
- Firebird
|
||||
-- Enable memory cleanup to avoid leaks after binding
|
||||
-- Added missing C++ library headers
|
||||
-- Added get_affected_rows() support for bulk operations
|
||||
-- Fixed compilation with VS2008
|
||||
-- Fixed building tests with the backend built as DLL.
|
||||
|
||||
- MySQL
|
||||
-- Added get_affected_rows() support for bulk operations
|
||||
|
||||
- ODBC
|
||||
-- Always call ASCII version of ODBC function SQLGetDiagRec
|
||||
-- Fixed invalid condition test in assertions
|
||||
-- Added get_affected_rows() support for bulk operations
|
||||
-- Added support for (unsigned) long long vectors
|
||||
-- Changed mapping of SQL_BIGINT to dt_long_long in ODBC backend
|
||||
-- Added some trivial optimizations in statement preparation code
|
||||
|
||||
- Oracle
|
||||
-- Fixed missing noData_ reported in particular case when OCIStmtExecute returns success
|
||||
-- Improved Oracle testing setup on Travis CI
|
||||
|
||||
- PostgreSQL
|
||||
-- Added session::get_next_sequence_value() for PostgreSQL backend
|
||||
-- Added get_affected_rows() support for bulk operations
|
||||
-- Applied RAII and simplified error handling logic
|
||||
-- Fixed issue with exceptions escaping postgresql_statement_backend destructor
|
||||
|
||||
- SQLite3
|
||||
-- Replaced sqlite3_prepare with sqlite3_prepare_v2
|
||||
-- Added missing <cstdarg> header
|
||||
-- Fixed wrong size used in memcpy() during bulk processing
|
||||
-- Added get_affected_rows() support for bulk operations
|
||||
-- Added shared_cache=true propery to control SQLITE_OPEN_SHAREDCACHE flag
|
||||
|
||||
- Documentation
|
||||
-- Corrected usage examples
|
||||
|
||||
- Building
|
||||
-- Updated CMake for Oracle 12g
|
||||
-- Restructured and improved Travis CI setup to do single build per backend
|
||||
-- Fixed wrong GCC_VERSION used in CMake config for commandline-overriden GCC
|
||||
-- Allows CMake to find 32-bit DB2 on Windows x64
|
||||
-- Removed redundant Oracle libclntsh library lookup
|
||||
-- Fixed SQLITE3_LIBRARIES handling by find_package_handle_standard_args
|
||||
|
||||
---
|
||||
Version 3.2.1 differs from 3.2.0 in the following ways:
|
||||
|
||||
- Fixed binding of Oracle type NUMBER(9,0) to C++ int, by Tomasz Olszewski
|
||||
- Fixed Oracle client library detection on Windows, by Tomasz Olszewski
|
||||
- Fixed PostgreSQL issue with deallocate_prepared_statement called for non-existing statement
|
||||
- Fixed prepared insert statements with custom data types support, by Tomasz Olszewski
|
||||
- Fixed recent improvements in x:=y name binding, by Poul Bondo
|
||||
- Fixed query transformation assignment in pooled sessions, by Stefan Chrobot
|
||||
- Cleaned up SOCI_POSTGRESQL_NOPARAMS and related options in code and documentation
|
||||
- Dropped patch (micro) version number from documentation URLs on the website
|
||||
|
||||
---
|
||||
Version 3.2.0 differs from 3.1.0 in the following ways:
|
||||
|
||||
- SOCI is now organization at GitHub
|
||||
-- Git repository moved to https://github.com/SOCI/soci
|
||||
-- Opened new bug tracker (SF.net tracker is read-only)
|
||||
-- Opened Wiki for FAQ and development articles
|
||||
-- Website, mailing lists and downloads remain on SourceForge.net
|
||||
-- Applied GitFlow branching model
|
||||
-- Added package maintenance repository https://github.com/SOCI/soci-pkg
|
||||
|
||||
- Core
|
||||
-- Added connection_parameters to enable support for session options used by core, backends or low-level API.
|
||||
-- Added user-defined query transformation support based on function, functor and lambda
|
||||
-- Fixed missing connection check and backend setup ensured for session
|
||||
-- Fixed backend initialization when used with connection pool (Core)
|
||||
-- Fixed memory leaks on exception thrown during statement preparation stage
|
||||
|
||||
- DB2
|
||||
-- Added new backend for IBM DB2
|
||||
|
||||
- Firebird
|
||||
-- Fixed bug in repeated statement execution
|
||||
-- Fixed issues with input parameter binding
|
||||
-- Fixed connection string parsing to handle possible white-spaces
|
||||
-- Fixed issues with C++ type unsigned long
|
||||
-- Fixed reading negative values into C++ type double
|
||||
-- Added option to fetch numeric/decimal data into string of characters
|
||||
-- Added CMake configuration
|
||||
-- Updated tests
|
||||
-- Continued regular testing on Windows and Unix platforms
|
||||
|
||||
- MySQL
|
||||
-- Replaced use of type=InnoDB with engine=InnoDB
|
||||
-- Improved CMake configuration
|
||||
|
||||
- ODBC
|
||||
-- Added connection_parameters option ODBC_OPTION_DRIVER_COMPLETE
|
||||
-- Fixed issues in handling C++ types int and long on 64-bit architectures
|
||||
-- Added missing CMake configuration for tests
|
||||
-- Continued regular testing on Windows and Unix platforms
|
||||
|
||||
- Oracle
|
||||
-- Implemented statement::get_affected_rows() operation
|
||||
-- Use OCI_THREADED and OCI_ENV_NO_MUTEX with OCIEnvCreate
|
||||
-- Added numerous fixes and improvements in tests
|
||||
-- Added option to fetch numeric/decimal data into string of characters
|
||||
-- Fixed issues in binding procedure IN/OUT parameters
|
||||
|
||||
- PostgreSQL
|
||||
-- Add reading of BYTEA data into std::string (not fully-featured binary data support yet)
|
||||
-- Add JSON data type support available in PostgreSQL 9.2+
|
||||
-- Fixed incorrect assertion in postgresql::get_error_details
|
||||
-- Fixed premature deallocation of prepared statements
|
||||
-- Fixed servers-side memory leak in prepared statements
|
||||
-- Fixed memory leak of PGresult on exception thrown
|
||||
-- Fixed isues in parsing complex PL/PSQL functions
|
||||
|
||||
- SQLite3
|
||||
-- Implemented statement::get_affected_rows() operation
|
||||
-- Fixed handling of database file path with spaces
|
||||
|
||||
- Building, testing and installation
|
||||
-- Marked Makefile.basic as deprecated (maintainer wanted)
|
||||
-- Cleaned numerous compilation warnings reported by various compilers
|
||||
-- Improved compilation using latest version of clang
|
||||
-- Added numerous improvements in CMake configuration
|
||||
-- Added SOCI_STATIC option to enable/disable static libraries build
|
||||
-- Fixed issues with ignored -DWITH_<dependency> options
|
||||
-- Fixed FindMySQL.cmake to allow use of mysqlclient from custom location
|
||||
-- Added support of SQLITE_ROOT_DIR variable to FindSQLite3.cmake module
|
||||
-- Fixed and tested CMake configuration on OSX
|
||||
-- Fixed and tested CMake configuration on FreeBSD
|
||||
-- Fixed and tested CMake configuration to use with Ninja
|
||||
-- Fixed building using Visual Studio 2010
|
||||
-- Added support for building using Visual Studio 2012
|
||||
-- Set up SOCI continuous integration at travis-ci.org
|
||||
-- Added Debian packaging support to soci-pkg repository
|
||||
-- Added Fedora/CentOS/RedHat packaging support to soci-pkg repository
|
||||
|
||||
- Documentation
|
||||
-- Review and update to catch up with current status quo
|
||||
-- Updated code examples
|
||||
-- Start maintenance and hosting of SOCI documentation per released version
|
||||
|
||||
---
|
||||
Version 3.1.0 differs from 3.0.0 in the following ways:
|
||||
|
||||
- Added Ada language binding
|
||||
|
||||
- Migraded build system from GNU Autotools and Visual Studio projects to CMake
|
||||
|
||||
- CMake build tested with Visual Studio, GCC and clang
|
||||
|
||||
- Incorporated a compromise for naming versioned shared libraries
|
||||
|
||||
- Enhanced and improved integration with Boost libraries:
|
||||
-- Boost.DateTime
|
||||
-- Boost.Fusion
|
||||
-- Boost.Optional
|
||||
-- Boost.Tuple
|
||||
|
||||
- Bug fixes and improvements in core and backends:
|
||||
-- Added soci::values::get_properties accessor useful for composing soci::type_conversion
|
||||
-- Export advanced API of backend loader from DLL.
|
||||
-- Added static factory registration functions for backends
|
||||
-- Added get_affected_rows operation
|
||||
-- Fixed thread-safety of connection pool under Windows
|
||||
-- Fixed bug with droping const qualifiers when binding to std::vector<soci::indicator>
|
||||
-- Fixed bug in default initialization of an object of const backend_factory wihch requires user-provided default constructor (see C++03/C++0x)
|
||||
-- Fixes for 64-bit builds
|
||||
-- Removed redundant exchange_traits breaking ODR on LP64
|
||||
-- Better ODBC support
|
||||
-- Type conversion support for unsigned integer types
|
||||
-- Bug ID:1971436 - incorrect rowset copy operations
|
||||
-- Bug ID:2010367 - memory leak (ODBC)
|
||||
-- Bug ID:2010409 - invalid memory allocaton in define by position (ODBC)
|
||||
-- Bug ID:2021243 - long long type support in Visual C++
|
||||
-- Patch ID:2483066 - 64bit Linux and 64bit integer submitted
|
||||
-- Patch ID:2809809 - Fix build with GCC 4.4
|
||||
-- Patch ID:2809810 - Fix SQLite3 build with GCC 4.3
|
||||
-- Patch ID:2581206 - Windows unicode application
|
||||
-- Patch ID:3058275 - install target for cmake build submitted
|
||||
-- Patch ID:3069375 - use $(prefix)/lib64 on AMD64 platforms.
|
||||
-- Improved performance while accessing query results (MySQL)
|
||||
-- Bug fixes for PROCEDURE support (MySQL)
|
||||
-- Removed throw statements from mysql_rowid_backend and mysql_blob_backend destructors (MySQL)
|
||||
-- Verify that prepared statements survive session::reconnect() operatoin (MySQL)
|
||||
-- Improved support for time and date (MySQL, PostgreSQL)
|
||||
-- Fixed bug with strings of length exceeding 255 characters (ODBC)
|
||||
-- Improved interpretation of the connect string (Oracle)
|
||||
-- Added handling of unsigned long long (Oracle, SQLite3, PostgreSQL)
|
||||
-- Fixes in integral types support (PostgreSQL)
|
||||
-- Support for colon-casts (PostgreSQL)
|
||||
-- Added possibility for use BLOB (PostgreSQL)
|
||||
-- Added support for connection property "synchronous=on|off" (SQLite3)
|
||||
-- Improved BLOB data handling (SQLite3)
|
||||
-- Improved boolean type suppport (SQLite3)
|
||||
-- Session timeout support (SQLite3)
|
||||
-- Improved tests clean-up (SQLite3)
|
||||
-- Added missing typedef of sqlite3_destructor_type which has been defined in sqlite3.h but since 3.3.10 (see comment for reference to SQLite ticket)
|
||||
|
||||
- Updated tests for various backends and SQL data types
|
||||
|
||||
- Migrated from CVS to Git repository
|
||||
|
||||
- Changed naming convensions and style across all the source code
|
||||
|
||||
- Firebird backend removed from official release as not actively maintained. Available in the Git repository.
|
||||
|
||||
---
|
||||
Version 3.0.0 differs from 2.2.0 in the following ways:
|
||||
|
||||
- Exposed the session's locale object.
|
||||
|
||||
- Moved the "no data" flag from indicators to statement.
|
||||
|
||||
- Allowed const objects as "use" elements.
|
||||
|
||||
- Added connection mode for Oracle.
|
||||
|
||||
- Added RAII support for transactions.
|
||||
|
||||
- Added the open/close/reconnect functionality.
|
||||
|
||||
- Added support for long long as a fundamental data type.
|
||||
|
||||
- Unified column names for dynamic rowset description, to overcome
|
||||
differences between database servers.
|
||||
|
||||
- Added the "simple" interface for interfacing from other languages.
|
||||
|
||||
- Added thread-safe connection pool.
|
||||
|
||||
- Added integrated support for Boost data types: gregorian_date,
|
||||
fusion and tuple.
|
||||
|
||||
- Added dynamic backend loading.
|
||||
|
||||
- Changed the naming convention to comply with Boost recommendations.
|
||||
|
||||
---
|
||||
Version 2.2.0 differs from 2.1.0 in the following ways:
|
||||
|
||||
- Added true support for statement preparation with PostgreSQL.
|
||||
|
||||
- Added support for the stream-like extraction from Row.
|
||||
|
||||
- Added STL-compatible iterator interface for select statements.
|
||||
|
||||
- Refactored the set of common tests to validate core library functionality
|
||||
independently on the target database server.
|
||||
|
||||
- Introduced new backends for MS SQL Server (via ODBC) and Firebird.
|
||||
|
||||
- Provided complete build system for both Unix (autotools) and
|
||||
Windows (solution and project files for MSVC++).
|
||||
|
||||
---
|
||||
Version 2.1.0 differs from 2.0.1 in the following ways:
|
||||
|
||||
- Added two additional backends: MySQL and SQLite3
|
||||
|
||||
- Restructured the source code layout so that the whole library was broken
|
||||
into the "core" part and independent "backends", each in its own
|
||||
directory together with appropriate tests.
|
||||
|
||||
- Provided basic Makefiles for static and shared libraries on
|
||||
Linux-compatible systems.
|
||||
|
||||
- Added the general class and function reference to the documentation.
|
||||
|
||||
---
|
||||
Version 2.0.1 differs from 2.0.0 in the following ways:
|
||||
|
||||
- Corrected some typos in documentation.
|
||||
|
||||
- Corrected handling of dynamic rowset description for those backends
|
||||
which do not have dedicated description functionality.
|
||||
|
||||
- A bug fix to correctly handle std::tm in the Oracle backend.
|
||||
|
||||
- A bug fix to correctly handle object relational mapping when
|
||||
Values::set<T>() and Values::get<T>() are called where T is a
|
||||
TypeConversion-based type.
|
||||
|
||||
---
|
||||
Version 2.0.0 differs from 1.2.1 in the following ways:
|
||||
|
||||
- The whole library was internally re-architectured to allow operation
|
||||
with many different backends. The top-level part of the library
|
||||
(the syntax layer) provides essentially the same interface as in previous
|
||||
versions of the library, but it can work with independent (and dynamically
|
||||
selected) backends, possibly targeting different database engines.
|
||||
As a prove of concept (and to encourage developments of new backends),
|
||||
the PostgreSQL backend was provided in addition to the Oracle one.
|
||||
During this re-architecturing, some minor bugs were fixed as well.
|
||||
|
||||
- The old Boost-style license was changed to the new (v. 1.0) Boost license.
|
||||
|
||||
---
|
||||
The version 1.2.1 differs from 1.2.0 in the following ways:
|
||||
|
||||
- A bug was fixed that caused compile errors on MS VC++ compiler.
|
||||
|
||||
---
|
||||
The version 1.2.0 differs from 1.1.0 in the following ways:
|
||||
|
||||
- A memory leak when reading into Row objects was fixed.
|
||||
|
||||
- Bulk (array) operations were introduced for high-performance
|
||||
applications, where the number of network round-trips can be
|
||||
significantly reduced by operating on whole arrays (vectors).
|
||||
|
||||
---
|
||||
The version 1.1.0 differs from 1.0.1 in the following ways:
|
||||
|
||||
- Explicit support for calling stored procedures was added.
|
||||
|
||||
- Dynamic row recognition (type discovery) was added.
|
||||
|
||||
- Support for user-defined data types was added.
|
||||
105
src/CMakeLists.txt
Normal file
105
src/CMakeLists.txt
Normal file
@@ -0,0 +1,105 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2009-2013 Mateusz Loskot <mateusz@loskot.net>
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
# General settings
|
||||
###############################################################################
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
|
||||
project(SOCI)
|
||||
|
||||
###############################################################################
|
||||
# SOCI CMake modules
|
||||
###############################################################################
|
||||
|
||||
# Path to additional CMake modules
|
||||
set(CMAKE_MODULE_PATH ${SOCI_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
|
||||
set(CMAKE_MODULE_PATH ${SOCI_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||
|
||||
include(SociUtilities)
|
||||
|
||||
colormsg(_HIBLUE_ "Configuring SOCI:")
|
||||
|
||||
###############################################################################
|
||||
# SOCI version information
|
||||
###############################################################################
|
||||
include(SociVersion)
|
||||
|
||||
soci_version(MAJOR 3 MINOR 2 PATCH 2)
|
||||
|
||||
###############################################################################
|
||||
# Build features and variants
|
||||
###############################################################################
|
||||
include(SociSystemInfo)
|
||||
include(SociConfig)
|
||||
|
||||
boost_report_value(SOCI_PLATFORM_NAME)
|
||||
boost_report_value(SOCI_COMPILER_NAME)
|
||||
|
||||
option(SOCI_SHARED "Enable build of shared libraries" ON)
|
||||
boost_report_value(SOCI_SHARED)
|
||||
|
||||
option(SOCI_STATIC "Enable build of static libraries" ON)
|
||||
boost_report_value(SOCI_STATIC)
|
||||
|
||||
option(SOCI_TESTS "Enable build of collection of SOCI tests" ON)
|
||||
boost_report_value(SOCI_TESTS)
|
||||
|
||||
# Put the libaries and binaries that get built into directories at the
|
||||
# top of the build tree rather than in hard-to-find leaf
|
||||
# directories. This simplifies manual testing and the use of the build
|
||||
# tree rather than installed Boost libraries.
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
###############################################################################
|
||||
# Find SOCI dependencies
|
||||
###############################################################################
|
||||
|
||||
set(SOCI_CORE_DEPENDENCIES)
|
||||
|
||||
include(SociDependencies)
|
||||
|
||||
#message("${SOCI_CORE_DEPENDENCIES}")
|
||||
|
||||
###############################################################################
|
||||
# Installation
|
||||
###############################################################################
|
||||
|
||||
if(APPLE OR CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(SOCI_LIBDIR "lib")
|
||||
else()
|
||||
set(SOCI_LIBDIR "lib64")
|
||||
endif()
|
||||
|
||||
set(BINDIR "bin" CACHE PATH "The directory to install binaries into.")
|
||||
set(LIBDIR ${SOCI_LIBDIR} CACHE PATH "The directory to install libraries into.")
|
||||
set(DATADIR "share" CACHE PATH "The directory to install data files into.")
|
||||
set(INCLUDEDIR "include" CACHE PATH "The directory to install includes into.")
|
||||
|
||||
###############################################################################
|
||||
# Enable tests
|
||||
###############################################################################
|
||||
enable_testing()
|
||||
# Configure for testing with dashboard submissions to CDash
|
||||
#include(CTest) # disabled as unused
|
||||
|
||||
# Define "make check" as alias for "make test"
|
||||
add_custom_target(check COMMAND ctest)
|
||||
|
||||
###############################################################################
|
||||
# Build configured components
|
||||
###############################################################################
|
||||
include(SociBackend)
|
||||
|
||||
include_directories(${SOCI_SOURCE_DIR}/core)
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(backends)
|
||||
13
src/CTestConfig.cmake
Normal file
13
src/CTestConfig.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
## This file should be placed in the root directory of your project.
|
||||
## Then modify the CMakeLists.txt file in the root directory of your
|
||||
## project to incorporate the testing dashboard.
|
||||
## # The following are required to uses Dart and the Cdash dashboard
|
||||
## ENABLE_TESTING()
|
||||
## INCLUDE(CTest)
|
||||
set(CTEST_PROJECT_NAME "SOCI")
|
||||
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
|
||||
|
||||
set(CTEST_DROP_METHOD "http")
|
||||
set(CTEST_DROP_SITE "my.cdash.org")
|
||||
set(CTEST_DROP_LOCATION "/submit.php?project=SOCI")
|
||||
set(CTEST_DROP_SITE_CDASH TRUE)
|
||||
23
src/LICENSE_1_0.txt
Normal file
23
src/LICENSE_1_0.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
35
src/README
Normal file
35
src/README
Normal file
@@ -0,0 +1,35 @@
|
||||
SOCI - The C++ Database Access Library.
|
||||
|
||||
Copyright (C) 2004-2008 Maciej Sobczak, Stephen Hutton,
|
||||
David Courtney, Pawel Aleksander Fedorynski,
|
||||
Rafal Bobrowski, Mateusz Loskot
|
||||
|
||||
The homepage of the SOCI library is:
|
||||
http://soci.sourceforge.net/
|
||||
|
||||
---
|
||||
|
||||
You should see the following files and directories here:
|
||||
|
||||
- README - This file.
|
||||
- LICENSE_1_0.txt - Full text of the Boost license.
|
||||
- CHANGES - The description of changes.
|
||||
|
||||
- contrib - Acknowledgements for contributions.
|
||||
|
||||
- doc/ - Documentation.
|
||||
|
||||
- build/ - Additional build scripts and configuration files
|
||||
for various compilation systems.
|
||||
|
||||
- src/ - The source code of the library.
|
||||
|
||||
- src/core/ - The main part of the SOCI library.
|
||||
|
||||
- src/backends/ - Directory containing implementations and tests for
|
||||
all available backends.
|
||||
|
||||
|
||||
Please see the doc/structure.html file for compilation instructions.
|
||||
|
||||
---
|
||||
5
src/backends/.gitignore
vendored
Normal file
5
src/backends/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.o
|
||||
*.la
|
||||
*.lo
|
||||
.deps
|
||||
.libs
|
||||
48
src/backends/CMakeLists.txt
Normal file
48
src/backends/CMakeLists.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
colormsg(_HIBLUE_ "Configuring SOCI database backends:")
|
||||
|
||||
# First, we'll investigate what can be found from database engines
|
||||
foreach(dep ${SOCI_BACKENDS_DB_DEPENDENCIES})
|
||||
string(TOUPPER ${dep} depUP)
|
||||
if (WITH_${depUP})
|
||||
find_package(${dep})
|
||||
endif()
|
||||
if(${dep}_FOUND OR ${depUP}_FOUND)
|
||||
set(${depUP}_FOUND ON)
|
||||
else()
|
||||
set(${depUP}_FOUND OFF)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# get all files in backends
|
||||
file(GLOB backend_dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
|
||||
|
||||
# empty backend always on by default
|
||||
option(SOCI_EMPTY "Build empty backend" ON)
|
||||
if(SOCI_EMPTY)
|
||||
set(WITH_EMPTY ON)
|
||||
set(EMPTY_FOUND ON)
|
||||
endif()
|
||||
|
||||
# enable only found backends
|
||||
foreach(dir ${backend_dirs})
|
||||
if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${dir})
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt)
|
||||
string(TOUPPER ${dir} dirUP)
|
||||
if(${dirUP}_FOUND AND WITH_${dirUP})
|
||||
add_subdirectory(${dir})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "")
|
||||
18
src/backends/db2/CMakeLists.txt
Normal file
18
src/backends/db2/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2011 Denis Chapligin
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend(DB2
|
||||
DEPENDS DB2
|
||||
HEADERS soci-db2.h
|
||||
DESCRIPTION "SOCI backend for DB2 Universal Database"
|
||||
AUTHORS "Denis Chapligin"
|
||||
MAINTAINERS "Denis Chapligin")
|
||||
|
||||
add_subdirectory(test)
|
||||
62
src/backends/db2/blob.cpp
Normal file
62
src/backends/db2/blob.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
db2_blob_backend::db2_blob_backend(db2_session_backend &session)
|
||||
: session_(session)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
db2_blob_backend::~db2_blob_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
std::size_t db2_blob_backend::get_len()
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t db2_blob_backend::read(
|
||||
std::size_t /* offset */, char * /* buf */, std::size_t /* toRead */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t db2_blob_backend::write(
|
||||
std::size_t /* offset */, char const * /* buf */,
|
||||
std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t db2_blob_backend::append(
|
||||
char const * /* buf */, std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void db2_blob_backend::trim(std::size_t /* newLen */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
21
src/backends/db2/common.h
Normal file
21
src/backends/db2/common.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright (C) 2013 Mateusz Loskot <mateusz@loskot.net>
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_DB2_COMMON_H_INCLUDED
|
||||
#define SOCI_DB2_COMMON_H_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace soci { namespace details { namespace db2 {
|
||||
|
||||
const std::size_t cli_max_buffer = 1024 * 1024 * 1024; //CLI limit is about 3 GB, but 1GB should be enough
|
||||
|
||||
}}} // namespace soci::details::db2
|
||||
|
||||
#endif // SOCI_DB2_COMMON_H_INCLUDED
|
||||
40
src/backends/db2/factory.cpp
Normal file
40
src/backends/db2/factory.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <backend-loader.h>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
// concrete factory for ODBC concrete strategies
|
||||
db2_session_backend * db2_backend_factory::make_session(
|
||||
connection_parameters const & parameters) const
|
||||
{
|
||||
return new db2_session_backend(parameters);
|
||||
}
|
||||
|
||||
db2_backend_factory const soci::db2;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_DB2_DECL backend_factory const * factory_db2()
|
||||
{
|
||||
return &soci::db2;
|
||||
}
|
||||
|
||||
SOCI_DB2_DECL void register_factory_db2()
|
||||
{
|
||||
soci::dynamic_backends::register_backend("db2", soci::db2);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
28
src/backends/db2/row-id.cpp
Normal file
28
src/backends/db2/row-id.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
db2_rowid_backend::db2_rowid_backend(db2_session_backend & /* session */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
db2_rowid_backend::~db2_rowid_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
217
src/backends/db2/session.cpp
Normal file
217
src/backends/db2/session.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <connection-parameters.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
const std::string db2_soci_error::sqlState(std::string const & msg,const SQLSMALLINT htype,const SQLHANDLE hndl) {
|
||||
std::ostringstream ss(msg, std::ostringstream::app);
|
||||
|
||||
|
||||
SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1];
|
||||
SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1];
|
||||
SQLINTEGER sqlcode;
|
||||
SQLSMALLINT length;
|
||||
|
||||
if ( SQLGetDiagRec(htype,
|
||||
hndl,
|
||||
1,
|
||||
sqlstate,
|
||||
&sqlcode,
|
||||
message,
|
||||
SQL_MAX_MESSAGE_LENGTH + 1,
|
||||
&length) == SQL_SUCCESS ) {
|
||||
ss<<" SQLMESSAGE: ";
|
||||
ss<<message;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void db2_session_backend::parseKeyVal(std::string const & keyVal) {
|
||||
size_t delimiter=keyVal.find_first_of("=");
|
||||
std::string key=keyVal.substr(0,delimiter);
|
||||
std::string value=keyVal.substr(delimiter+1,keyVal.length());
|
||||
|
||||
if (!key.compare("DSN")) {
|
||||
this->dsn=value;
|
||||
}
|
||||
if (!key.compare("Uid")) {
|
||||
this->username=value;
|
||||
}
|
||||
if (!key.compare("Pwd")) {
|
||||
this->password=value;
|
||||
}
|
||||
this->autocommit=true; //Default value
|
||||
if (!key.compare("autocommit")) {
|
||||
if (!value.compare("off")) {
|
||||
this->autocommit=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;AutoCommit=off */
|
||||
void db2_session_backend::parseConnectString(std::string const & connectString) {
|
||||
std::string processingString(connectString);
|
||||
size_t delimiter=processingString.find_first_of(";");
|
||||
while(delimiter!=std::string::npos) {
|
||||
std::string keyVal=processingString.substr(0,delimiter);
|
||||
parseKeyVal(keyVal);
|
||||
processingString=processingString.erase(0,delimiter+1);
|
||||
delimiter=processingString.find_first_of(";");
|
||||
}
|
||||
if (!processingString.empty()) {
|
||||
parseKeyVal(processingString);
|
||||
}
|
||||
}
|
||||
|
||||
db2_session_backend::db2_session_backend(
|
||||
connection_parameters const & parameters) :
|
||||
in_transaction(false)
|
||||
{
|
||||
parseConnectString(parameters.get_connect_string());
|
||||
SQLRETURN cliRC = SQL_SUCCESS;
|
||||
|
||||
/* Prepare handles */
|
||||
cliRC = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&hEnv);
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
throw db2_soci_error("Error while allocating the enironment handle",cliRC);
|
||||
}
|
||||
|
||||
cliRC = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
std::string msg=db2_soci_error::sqlState("Error while allocating the connection handle",SQL_HANDLE_ENV,hEnv);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
|
||||
/* Set autocommit */
|
||||
if(this->autocommit) {
|
||||
cliRC = SQLSetConnectAttr(hDbc,SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, SQL_NTS);
|
||||
} else {
|
||||
cliRC = SQLSetConnectAttr(hDbc,SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_NTS);
|
||||
}
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
std::string msg=db2_soci_error::sqlState("Error while setting autocommit attribute",SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
|
||||
/* Connect to database */
|
||||
cliRC = SQLConnect(hDbc, const_cast<SQLCHAR *>((const SQLCHAR *) dsn.c_str()), SQL_NTS,
|
||||
const_cast<SQLCHAR *>((const SQLCHAR *) username.c_str()), SQL_NTS,
|
||||
const_cast<SQLCHAR *>((const SQLCHAR *) password.c_str()), SQL_NTS);
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
std::string msg=db2_soci_error::sqlState("Error connecting to database",SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
db2_session_backend::~db2_session_backend()
|
||||
{
|
||||
clean_up();
|
||||
}
|
||||
|
||||
void db2_session_backend::begin()
|
||||
{
|
||||
// In DB2, transations begin implicitly; however, autocommit must be disabled for the duration of the transaction
|
||||
if(autocommit)
|
||||
{
|
||||
SQLRETURN cliRC = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, SQL_NTS);
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
std::string msg=db2_soci_error::sqlState("Clearing the autocommit attribute failed", SQL_HANDLE_DBC, hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
in_transaction = true;
|
||||
}
|
||||
|
||||
void db2_session_backend::commit()
|
||||
{
|
||||
if (!autocommit || in_transaction) {
|
||||
in_transaction = false;
|
||||
SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_COMMIT);
|
||||
if(autocommit)
|
||||
{
|
||||
SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS);
|
||||
if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) &&
|
||||
cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
}
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) {
|
||||
throw db2_soci_error("Commit failed",cliRC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void db2_session_backend::rollback()
|
||||
{
|
||||
if (!autocommit || in_transaction) {
|
||||
in_transaction = false;
|
||||
SQLRETURN cliRC = SQLEndTran(SQL_HANDLE_DBC,hDbc,SQL_ROLLBACK);
|
||||
if(autocommit)
|
||||
{
|
||||
SQLRETURN cliRC2 = SQLSetConnectAttr(hDbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, SQL_NTS);
|
||||
if ((cliRC == SQL_SUCCESS || cliRC == SQL_SUCCESS_WITH_INFO) &&
|
||||
cliRC2 != SQL_SUCCESS && cliRC2 != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
std::string msg=db2_soci_error::sqlState("Setting the autocommit attribute failed", SQL_HANDLE_DBC, hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
throw db2_soci_error(msg,cliRC);
|
||||
}
|
||||
}
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO) {
|
||||
throw db2_soci_error("Rollback failed",cliRC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void db2_session_backend::clean_up()
|
||||
{
|
||||
// if a transaction is in progress, it will automatically be rolled back upon when the connection is disconnected/freed
|
||||
in_transaction = false;
|
||||
|
||||
SQLDisconnect(hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_DBC,hDbc);
|
||||
SQLFreeHandle(SQL_HANDLE_ENV,hEnv);
|
||||
}
|
||||
|
||||
db2_statement_backend * db2_session_backend::make_statement_backend()
|
||||
{
|
||||
return new db2_statement_backend(*this);
|
||||
}
|
||||
|
||||
db2_rowid_backend * db2_session_backend::make_rowid_backend()
|
||||
{
|
||||
return new db2_rowid_backend(*this);
|
||||
}
|
||||
|
||||
db2_blob_backend * db2_session_backend::make_blob_backend()
|
||||
{
|
||||
return new db2_blob_backend(*this);
|
||||
}
|
||||
282
src/backends/db2/soci-db2.h
Normal file
282
src/backends/db2/soci-db2.h
Normal file
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_DB2_H_INCLUDED
|
||||
#define SOCI_DB2_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef SOCI_DLL
|
||||
# ifdef SOCI_DB2_SOURCE
|
||||
# define SOCI_DB2_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define SOCI_DB2_DECL __declspec(dllimport)
|
||||
# endif // SOCI_DB2_SOURCE
|
||||
# endif // SOCI_DLL
|
||||
#endif // _WIN32
|
||||
//
|
||||
// If SOCI_DB2_DECL isn't defined yet define it now
|
||||
#ifndef SOCI_DB2_DECL
|
||||
# define SOCI_DB2_DECL
|
||||
#endif
|
||||
|
||||
#include "soci-backend.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#include <sqlcli1.h>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
namespace details { namespace db2
|
||||
{
|
||||
enum binding_method
|
||||
{
|
||||
BOUND_BY_NONE,
|
||||
BOUND_BY_NAME,
|
||||
BOUND_BY_POSITION
|
||||
};
|
||||
}}
|
||||
|
||||
static const std::size_t maxBuffer = 1024 * 1024 * 1024; //CLI limit is about 3 GB, but 1GB should be enough
|
||||
|
||||
class db2_soci_error : public soci_error {
|
||||
public:
|
||||
db2_soci_error(std::string const & msg, SQLRETURN rc) : soci_error(msg),errorCode(rc) {};
|
||||
~db2_soci_error() throw() { };
|
||||
|
||||
//We have to extract error information before exception throwing, cause CLI handles could be broken at the construction time
|
||||
static const std::string sqlState(std::string const & msg,const SQLSMALLINT htype,const SQLHANDLE hndl);
|
||||
|
||||
SQLRETURN errorCode;
|
||||
};
|
||||
|
||||
struct db2_statement_backend;
|
||||
|
||||
struct SOCI_DB2_DECL db2_standard_into_type_backend : details::standard_into_type_backend
|
||||
{
|
||||
db2_standard_into_type_backend(db2_statement_backend &st)
|
||||
: statement_(st),buf(NULL)
|
||||
{}
|
||||
|
||||
void define_by_pos(int& position, void* data, details::exchange_type type);
|
||||
|
||||
void pre_fetch();
|
||||
void post_fetch(bool gotData, bool calledFromFetch, indicator* ind);
|
||||
|
||||
void clean_up();
|
||||
|
||||
db2_statement_backend& statement_;
|
||||
|
||||
char* buf;
|
||||
void *data;
|
||||
details::exchange_type type;
|
||||
int position;
|
||||
SQLSMALLINT cType;
|
||||
SQLLEN valueLen;
|
||||
};
|
||||
|
||||
struct SOCI_DB2_DECL db2_vector_into_type_backend : details::vector_into_type_backend
|
||||
{
|
||||
db2_vector_into_type_backend(db2_statement_backend &st)
|
||||
: statement_(st),buf(NULL)
|
||||
{}
|
||||
|
||||
void define_by_pos(int& position, void* data, details::exchange_type type);
|
||||
|
||||
void pre_fetch();
|
||||
void post_fetch(bool gotData, indicator* ind);
|
||||
|
||||
void resize(std::size_t sz);
|
||||
std::size_t size();
|
||||
|
||||
void clean_up();
|
||||
|
||||
db2_statement_backend& statement_;
|
||||
|
||||
void prepare_indicators(std::size_t size);
|
||||
|
||||
SQLLEN *indptr;
|
||||
std::vector<SQLLEN> indVec;
|
||||
void *data;
|
||||
char *buf;
|
||||
int position_;
|
||||
details::exchange_type type;
|
||||
SQLSMALLINT cType;
|
||||
std::size_t colSize;
|
||||
};
|
||||
|
||||
struct SOCI_DB2_DECL db2_standard_use_type_backend : details::standard_use_type_backend
|
||||
{
|
||||
db2_standard_use_type_backend(db2_statement_backend &st)
|
||||
: statement_(st),buf(NULL),ind(0)
|
||||
{}
|
||||
|
||||
void bind_by_pos(int& position, void* data, details::exchange_type type, bool readOnly);
|
||||
void bind_by_name(std::string const& name, void* data, details::exchange_type type, bool readOnly);
|
||||
|
||||
void pre_use(indicator const* ind);
|
||||
void post_use(bool gotData, indicator* ind);
|
||||
|
||||
void clean_up();
|
||||
|
||||
db2_statement_backend& statement_;
|
||||
|
||||
void *prepare_for_bind(void *data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType);
|
||||
|
||||
void *data;
|
||||
details::exchange_type type;
|
||||
int position;
|
||||
std::string name;
|
||||
char* buf;
|
||||
SQLLEN ind;
|
||||
};
|
||||
|
||||
struct SOCI_DB2_DECL db2_vector_use_type_backend : details::vector_use_type_backend
|
||||
{
|
||||
db2_vector_use_type_backend(db2_statement_backend &st)
|
||||
: statement_(st),buf(NULL) {}
|
||||
|
||||
void bind_by_pos(int& position, void* data, details::exchange_type type);
|
||||
void bind_by_name(std::string const& name, void* data, details::exchange_type type);
|
||||
|
||||
void pre_use(indicator const* ind);
|
||||
|
||||
std::size_t size();
|
||||
|
||||
void clean_up();
|
||||
|
||||
db2_statement_backend& statement_;
|
||||
|
||||
void prepare_indicators(std::size_t size);
|
||||
void prepare_for_bind(void *&data, SQLUINTEGER &size,SQLSMALLINT &sqlType, SQLSMALLINT &cType);
|
||||
void bind_helper(int &position, void *data, details::exchange_type type);
|
||||
|
||||
SQLLEN *indptr;
|
||||
std::vector<SQLLEN> indVec;
|
||||
void *data;
|
||||
char *buf;
|
||||
details::exchange_type type;
|
||||
std::size_t colSize;
|
||||
};
|
||||
|
||||
struct db2_session_backend;
|
||||
struct SOCI_DB2_DECL db2_statement_backend : details::statement_backend
|
||||
{
|
||||
db2_statement_backend(db2_session_backend &session);
|
||||
|
||||
void alloc();
|
||||
void clean_up();
|
||||
void prepare(std::string const& query, details::statement_type eType);
|
||||
|
||||
exec_fetch_result execute(int number);
|
||||
exec_fetch_result fetch(int number);
|
||||
|
||||
long long get_affected_rows();
|
||||
int get_number_of_rows();
|
||||
|
||||
std::string rewrite_for_procedure_call(std::string const& query);
|
||||
|
||||
int prepare_for_describe();
|
||||
void describe_column(int colNum, data_type& dtype, std::string& columnName);
|
||||
std::size_t column_size(int col);
|
||||
|
||||
db2_standard_into_type_backend* make_into_type_backend();
|
||||
db2_standard_use_type_backend* make_use_type_backend();
|
||||
db2_vector_into_type_backend* make_vector_into_type_backend();
|
||||
db2_vector_use_type_backend* make_vector_use_type_backend();
|
||||
|
||||
db2_session_backend& session_;
|
||||
|
||||
SQLHANDLE hStmt;
|
||||
std::string query_;
|
||||
std::vector<std::string> names;
|
||||
bool hasVectorUseElements;
|
||||
SQLUINTEGER numRowsFetched;
|
||||
details::db2::binding_method use_binding_method_;
|
||||
};
|
||||
|
||||
struct db2_rowid_backend : details::rowid_backend
|
||||
{
|
||||
db2_rowid_backend(db2_session_backend &session);
|
||||
|
||||
~db2_rowid_backend();
|
||||
};
|
||||
|
||||
struct db2_blob_backend : details::blob_backend
|
||||
{
|
||||
db2_blob_backend(db2_session_backend& session);
|
||||
|
||||
~db2_blob_backend();
|
||||
|
||||
std::size_t get_len();
|
||||
std::size_t read(std::size_t offset, char* buf, std::size_t toRead);
|
||||
std::size_t write(std::size_t offset, char const* buf, std::size_t toWrite);
|
||||
std::size_t append(char const* buf, std::size_t toWrite);
|
||||
void trim(std::size_t newLen);
|
||||
|
||||
db2_session_backend& session_;
|
||||
};
|
||||
|
||||
struct db2_session_backend : details::session_backend
|
||||
{
|
||||
db2_session_backend(connection_parameters const& parameters);
|
||||
|
||||
~db2_session_backend();
|
||||
|
||||
void begin();
|
||||
void commit();
|
||||
void rollback();
|
||||
|
||||
std::string get_backend_name() const { return "DB2"; }
|
||||
|
||||
void clean_up();
|
||||
|
||||
db2_statement_backend* make_statement_backend();
|
||||
db2_rowid_backend* make_rowid_backend();
|
||||
db2_blob_backend* make_blob_backend();
|
||||
|
||||
void parseConnectString(std::string const &);
|
||||
void parseKeyVal(std::string const &);
|
||||
|
||||
std::string dsn;
|
||||
std::string username;
|
||||
std::string password;
|
||||
bool autocommit;
|
||||
bool in_transaction;
|
||||
|
||||
SQLHANDLE hEnv; /* Environment handle */
|
||||
SQLHANDLE hDbc; /* Connection handle */
|
||||
};
|
||||
|
||||
struct SOCI_DB2_DECL db2_backend_factory : backend_factory
|
||||
{
|
||||
db2_backend_factory() {}
|
||||
db2_session_backend* make_session(
|
||||
connection_parameters const & parameters) const;
|
||||
};
|
||||
|
||||
extern SOCI_DB2_DECL db2_backend_factory const db2;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_DB2_DECL backend_factory const* factory_db2();
|
||||
SOCI_DB2_DECL void register_factory_db2();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_DB2_H_INCLUDED
|
||||
168
src/backends/db2/standard-into-type.cpp
Normal file
168
src/backends/db2/standard-into-type.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include "common.h"
|
||||
#include <ctime>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
void db2_standard_into_type_backend::define_by_pos(
|
||||
int & position, void * data, exchange_type type)
|
||||
{
|
||||
this->data = data;
|
||||
this->type = type;
|
||||
this->position = position;
|
||||
position++;
|
||||
|
||||
SQLUINTEGER size = 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case x_char:
|
||||
cType = SQL_C_CHAR;
|
||||
size = sizeof(char) + 1;
|
||||
buf = new char[size];
|
||||
data = buf;
|
||||
break;
|
||||
case x_stdstring:
|
||||
cType = SQL_C_CHAR;
|
||||
// Patch: set to min between column size and 100MB (used ot be 32769)
|
||||
// Column size for text data type can be too large for buffer allocation
|
||||
size = statement_.column_size(this->position);
|
||||
size = size > details::db2::cli_max_buffer ? details::db2::cli_max_buffer : size;
|
||||
size++;
|
||||
buf = new char[size];
|
||||
data = buf;
|
||||
break;
|
||||
case x_short:
|
||||
cType = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
break;
|
||||
case x_integer:
|
||||
cType = SQL_C_SLONG;
|
||||
size = sizeof(SQLINTEGER);
|
||||
break;
|
||||
case x_long_long:
|
||||
cType = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
break;
|
||||
case x_double:
|
||||
cType = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
break;
|
||||
case x_stdtm:
|
||||
cType = SQL_C_TYPE_TIMESTAMP;
|
||||
size = sizeof(TIMESTAMP_STRUCT);
|
||||
buf = new char[size];
|
||||
data = buf;
|
||||
break;
|
||||
case x_rowid:
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Into element used with non-supported type.");
|
||||
}
|
||||
|
||||
valueLen = 0;
|
||||
|
||||
SQLRETURN cliRC = SQLBindCol(statement_.hStmt, static_cast<SQLUSMALLINT>(this->position),
|
||||
static_cast<SQLUSMALLINT>(cType), data, size, &valueLen);
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error("Error while pre-fething into type",cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
void db2_standard_into_type_backend::pre_fetch()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
void db2_standard_into_type_backend::post_fetch(
|
||||
bool gotData, bool calledFromFetch, indicator * ind)
|
||||
{
|
||||
if (calledFromFetch == true && gotData == false)
|
||||
{
|
||||
// this is a normal end-of-rowset condition,
|
||||
// no need to do anything (fetch() will return false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gotData)
|
||||
{
|
||||
// first, deal with indicators
|
||||
if (SQL_NULL_DATA == valueLen)
|
||||
{
|
||||
if (ind == NULL)
|
||||
{
|
||||
throw soci_error(
|
||||
"Null value fetched and no indicator defined.");
|
||||
}
|
||||
|
||||
*ind = i_null;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ind != NULL)
|
||||
{
|
||||
*ind = i_ok;
|
||||
}
|
||||
}
|
||||
|
||||
// only std::string and std::tm need special handling
|
||||
if (type == x_char)
|
||||
{
|
||||
char *c = static_cast<char*>(data);
|
||||
*c = buf[0];
|
||||
}
|
||||
if (type == x_stdstring)
|
||||
{
|
||||
std::string *s = static_cast<std::string *>(data);
|
||||
*s = buf;
|
||||
if (s->size() >= (details::db2::cli_max_buffer - 1))
|
||||
{
|
||||
throw soci_error("Buffer size overflow; maybe got too large string");
|
||||
}
|
||||
}
|
||||
else if (type == x_stdtm)
|
||||
{
|
||||
std::tm *t = static_cast<std::tm *>(data);
|
||||
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf);
|
||||
t->tm_isdst = -1;
|
||||
t->tm_year = ts->year - 1900;
|
||||
t->tm_mon = ts->month - 1;
|
||||
t->tm_mday = ts->day;
|
||||
t->tm_hour = ts->hour;
|
||||
t->tm_min = ts->minute;
|
||||
t->tm_sec = ts->second;
|
||||
|
||||
// normalize and compute the remaining fields
|
||||
std::mktime(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void db2_standard_into_type_backend::clean_up()
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
delete [] buf;
|
||||
buf = 0;
|
||||
}
|
||||
}
|
||||
201
src/backends/db2/standard-use-type.cpp
Normal file
201
src/backends/db2/standard-use-type.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
void *db2_standard_use_type_backend::prepare_for_bind(
|
||||
void *data, SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
// simple cases
|
||||
case x_short:
|
||||
sqlType = SQL_SMALLINT;
|
||||
cType = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
break;
|
||||
case x_integer:
|
||||
sqlType = SQL_INTEGER;
|
||||
cType = SQL_C_SLONG;
|
||||
size = sizeof(int);
|
||||
break;
|
||||
case x_long_long:
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
break;
|
||||
case x_double:
|
||||
sqlType = SQL_DOUBLE;
|
||||
cType = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
case x_char:
|
||||
{
|
||||
sqlType = SQL_CHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
size = sizeof(char) + 1;
|
||||
buf = new char[size];
|
||||
char *c = static_cast<char*>(data);
|
||||
buf[0] = *c;
|
||||
buf[1] = '\0';
|
||||
ind = SQL_NTS;
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
// TODO: No textual value is assigned here!
|
||||
|
||||
std::string* s = static_cast<std::string*>(data);
|
||||
sqlType = SQL_LONGVARCHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
size = s->size() + 1;
|
||||
buf = new char[size];
|
||||
strncpy(buf, s->c_str(), size);
|
||||
ind = SQL_NTS;
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
sqlType = SQL_TIMESTAMP;
|
||||
cType = SQL_C_TIMESTAMP;
|
||||
buf = new char[sizeof(TIMESTAMP_STRUCT)];
|
||||
std::tm *t = static_cast<std::tm *>(data);
|
||||
data = buf;
|
||||
size = 19; // This number is not the size in bytes, but the number
|
||||
// of characters in the date if it was written out
|
||||
// yyyy-mm-dd hh:mm:ss
|
||||
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf);
|
||||
|
||||
ts->year = static_cast<SQLSMALLINT>(t->tm_year + 1900);
|
||||
ts->month = static_cast<SQLUSMALLINT>(t->tm_mon + 1);
|
||||
ts->day = static_cast<SQLUSMALLINT>(t->tm_mday);
|
||||
ts->hour = static_cast<SQLUSMALLINT>(t->tm_hour);
|
||||
ts->minute = static_cast<SQLUSMALLINT>(t->tm_min);
|
||||
ts->second = static_cast<SQLUSMALLINT>(t->tm_sec);
|
||||
ts->fraction = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case x_blob:
|
||||
break;
|
||||
case x_statement:
|
||||
case x_rowid:
|
||||
break;
|
||||
}
|
||||
|
||||
// Return either the pointer to C++ data itself or the buffer that we
|
||||
// allocated, if any.
|
||||
return buf ? buf : data;
|
||||
}
|
||||
|
||||
void db2_standard_use_type_backend::bind_by_pos(
|
||||
int &position, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.use_binding_method_ == details::db2::BOUND_BY_NAME)
|
||||
{
|
||||
throw soci_error("Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
statement_.use_binding_method_ = details::db2::BOUND_BY_POSITION;
|
||||
|
||||
this->data = data; // for future reference
|
||||
this->type = type; // for future reference
|
||||
this->position = position++;
|
||||
}
|
||||
|
||||
void db2_standard_use_type_backend::bind_by_name(
|
||||
std::string const &name, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.use_binding_method_ == details::db2::BOUND_BY_POSITION)
|
||||
{
|
||||
throw soci_error("Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
statement_.use_binding_method_ = details::db2::BOUND_BY_NAME;
|
||||
|
||||
int position = -1;
|
||||
int count = 1;
|
||||
|
||||
for (std::vector<std::string>::iterator it = statement_.names.begin();
|
||||
it != statement_.names.end(); ++it)
|
||||
{
|
||||
if (*it == name)
|
||||
{
|
||||
position = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (position != -1)
|
||||
{
|
||||
this->data = data; // for future reference
|
||||
this->type = type; // for future reference
|
||||
this->position = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Unable to find name '" << name << "' to bind to";
|
||||
throw soci_error(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void db2_standard_use_type_backend::pre_use(indicator const *ind_ptr)
|
||||
{
|
||||
// first deal with data
|
||||
SQLSMALLINT sqlType;
|
||||
SQLSMALLINT cType;
|
||||
SQLLEN size;
|
||||
|
||||
void *sqlData = prepare_for_bind(data, size, sqlType, cType);
|
||||
|
||||
SQLRETURN cliRC = SQLBindParameter(statement_.hStmt,
|
||||
static_cast<SQLUSMALLINT>(position),
|
||||
SQL_PARAM_INPUT,
|
||||
cType, sqlType, size, 0, sqlData, size, &ind);
|
||||
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error("Error while binding value",cliRC);
|
||||
}
|
||||
|
||||
// then handle indicators
|
||||
if (ind_ptr != NULL && *ind_ptr == i_null)
|
||||
{
|
||||
ind = SQL_NULL_DATA; // null
|
||||
}
|
||||
}
|
||||
|
||||
void db2_standard_use_type_backend::post_use(bool /*gotData*/, indicator* /*ind*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void db2_standard_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf != NULL)
|
||||
{
|
||||
delete [] buf;
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
322
src/backends/db2/statement.cpp
Normal file
322
src/backends/db2/statement.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <cctype>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
db2_statement_backend::db2_statement_backend(db2_session_backend &session)
|
||||
: session_(session),hasVectorUseElements(false),use_binding_method_(details::db2::BOUND_BY_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
void db2_statement_backend::alloc()
|
||||
{
|
||||
SQLRETURN cliRC = SQL_SUCCESS;
|
||||
|
||||
cliRC = SQLAllocHandle(SQL_HANDLE_STMT,session_.hDbc,&hStmt);
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
throw db2_soci_error("Error while allocation statement handle",cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
void db2_statement_backend::clean_up()
|
||||
{
|
||||
SQLRETURN cliRC = SQL_SUCCESS;
|
||||
|
||||
cliRC=SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
|
||||
if (cliRC != SQL_SUCCESS) {
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Statement handle clean-up error",SQL_HANDLE_STMT,hStmt),cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
void db2_statement_backend::prepare(std::string const & query ,
|
||||
statement_type /* eType */)
|
||||
{
|
||||
// rewrite the query by transforming all named parameters into
|
||||
// the markers (:abc -> ?, etc.)
|
||||
|
||||
enum { normal, in_quotes, in_name } state = normal;
|
||||
|
||||
std::string name;
|
||||
|
||||
for (std::string::const_iterator it = query.begin(), end = query.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case normal:
|
||||
if (*it == '\'')
|
||||
{
|
||||
query_ += *it;
|
||||
state = in_quotes;
|
||||
}
|
||||
else if (*it == ':')
|
||||
{
|
||||
// Check whether this is a cast operator (e.g. 23::float)
|
||||
// and treat it as a special case, not as a named binding
|
||||
const std::string::const_iterator next_it = it + 1;
|
||||
if ((next_it != end) && (*next_it == ':'))
|
||||
{
|
||||
query_ += "::";
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = in_name;
|
||||
}
|
||||
}
|
||||
else // regular character, stay in the same state
|
||||
{
|
||||
query_ += *it;
|
||||
}
|
||||
break;
|
||||
case in_quotes:
|
||||
if (*it == '\'')
|
||||
{
|
||||
query_ += *it;
|
||||
state = normal;
|
||||
}
|
||||
else // regular quoted character
|
||||
{
|
||||
query_ += *it;
|
||||
}
|
||||
break;
|
||||
case in_name:
|
||||
if (std::isalnum(*it) || *it == '_')
|
||||
{
|
||||
name += *it;
|
||||
}
|
||||
else // end of name
|
||||
{
|
||||
names.push_back(name);
|
||||
name.clear();
|
||||
std::ostringstream ss;
|
||||
ss << '?';
|
||||
query_ += ss.str();
|
||||
query_ += *it;
|
||||
state = normal;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == in_name)
|
||||
{
|
||||
names.push_back(name);
|
||||
std::ostringstream ss;
|
||||
ss << '?';
|
||||
query_ += ss.str();
|
||||
}
|
||||
|
||||
SQLRETURN cliRC = SQLPrepare(hStmt, const_cast<SQLCHAR *>((const SQLCHAR *) query_.c_str()), SQL_NTS);
|
||||
if (cliRC!=SQL_SUCCESS) {
|
||||
throw db2_soci_error("Error while preparing query",cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
db2_statement_backend::execute(int number )
|
||||
{
|
||||
SQLUINTEGER rows_processed = 0;
|
||||
SQLRETURN cliRC;
|
||||
|
||||
if (hasVectorUseElements)
|
||||
{
|
||||
SQLSetStmtAttr(hStmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0);
|
||||
}
|
||||
|
||||
// if we are called twice for the same statement we need to close the open
|
||||
// cursor or an "invalid cursor state" error will occur on execute
|
||||
cliRC = SQLFreeStmt(hStmt,SQL_CLOSE);
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC);
|
||||
}
|
||||
|
||||
cliRC = SQLExecute(hStmt);
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Statement execution error",SQL_HANDLE_STMT,hStmt),cliRC);
|
||||
}
|
||||
|
||||
SQLSMALLINT colCount;
|
||||
SQLNumResultCols(hStmt, &colCount);
|
||||
|
||||
if (number > 0 && colCount > 0)
|
||||
{
|
||||
return fetch(number);
|
||||
}
|
||||
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
db2_statement_backend::fetch(int number )
|
||||
{
|
||||
numRowsFetched = 0;
|
||||
|
||||
SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
|
||||
SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0);
|
||||
SQLSetStmtAttr(hStmt, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched, 0);
|
||||
|
||||
SQLRETURN cliRC = SQLFetch(hStmt);
|
||||
|
||||
if (SQL_NO_DATA == cliRC)
|
||||
{
|
||||
return ef_no_data;
|
||||
}
|
||||
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Error while fetching data", SQL_HANDLE_STMT, hStmt), cliRC);
|
||||
}
|
||||
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
long long db2_statement_backend::get_affected_rows()
|
||||
{
|
||||
SQLLEN rows;
|
||||
|
||||
SQLRETURN cliRC = SQLRowCount(hStmt, &rows);
|
||||
if (cliRC != SQL_SUCCESS && cliRC != SQL_SUCCESS_WITH_INFO)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Error while getting affected row count", SQL_HANDLE_STMT, hStmt), cliRC);
|
||||
}
|
||||
else if (rows == -1)
|
||||
{
|
||||
throw soci_error("Error getting affected row count: statement did not perform an update, insert, delete, or merge");
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
int db2_statement_backend::get_number_of_rows()
|
||||
{
|
||||
return numRowsFetched;
|
||||
}
|
||||
|
||||
std::string db2_statement_backend::rewrite_for_procedure_call(
|
||||
std::string const &query)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
int db2_statement_backend::prepare_for_describe()
|
||||
{
|
||||
SQLSMALLINT numCols;
|
||||
SQLNumResultCols(hStmt, &numCols);
|
||||
return numCols;
|
||||
}
|
||||
|
||||
void db2_statement_backend::describe_column(int colNum,
|
||||
data_type & type, std::string & columnName )
|
||||
{
|
||||
SQLCHAR colNameBuffer[2048];
|
||||
SQLSMALLINT colNameBufferOverflow;
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN colSize;
|
||||
SQLSMALLINT decDigits;
|
||||
SQLSMALLINT isNullable;
|
||||
|
||||
SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast<SQLUSMALLINT>(colNum),
|
||||
colNameBuffer, 2048,
|
||||
&colNameBufferOverflow, &dataType,
|
||||
&colSize, &decDigits, &isNullable);
|
||||
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Error while describing column",SQL_HANDLE_STMT,hStmt),cliRC);
|
||||
}
|
||||
|
||||
char const *name = reinterpret_cast<char const *>(colNameBuffer);
|
||||
columnName.assign(name, std::strlen(name));
|
||||
|
||||
switch (dataType)
|
||||
{
|
||||
case SQL_TYPE_DATE:
|
||||
case SQL_TYPE_TIME:
|
||||
case SQL_TYPE_TIMESTAMP:
|
||||
type = dt_date;
|
||||
break;
|
||||
case SQL_DOUBLE:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_REAL:
|
||||
case SQL_FLOAT:
|
||||
case SQL_NUMERIC:
|
||||
type = dt_double;
|
||||
break;
|
||||
case SQL_TINYINT:
|
||||
case SQL_SMALLINT:
|
||||
case SQL_INTEGER:
|
||||
type = dt_integer;
|
||||
break;
|
||||
case SQL_BIGINT:
|
||||
type = dt_long_long;
|
||||
break;
|
||||
case SQL_CHAR:
|
||||
case SQL_VARCHAR:
|
||||
case SQL_LONGVARCHAR:
|
||||
default:
|
||||
type = dt_string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t db2_statement_backend::column_size(int col) {
|
||||
SQLCHAR colNameBuffer[2048];
|
||||
SQLSMALLINT colNameBufferOverflow;
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN colSize;
|
||||
SQLSMALLINT decDigits;
|
||||
SQLSMALLINT isNullable;
|
||||
|
||||
SQLRETURN cliRC = SQLDescribeCol(hStmt, static_cast<SQLUSMALLINT>(col),
|
||||
colNameBuffer, 2048,
|
||||
&colNameBufferOverflow, &dataType,
|
||||
&colSize, &decDigits, &isNullable);
|
||||
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error(db2_soci_error::sqlState("Error while detecting column size",SQL_HANDLE_STMT,hStmt),cliRC);
|
||||
}
|
||||
|
||||
return colSize;
|
||||
}
|
||||
|
||||
db2_standard_into_type_backend * db2_statement_backend::make_into_type_backend()
|
||||
{
|
||||
return new db2_standard_into_type_backend(*this);
|
||||
}
|
||||
|
||||
db2_standard_use_type_backend * db2_statement_backend::make_use_type_backend()
|
||||
{
|
||||
return new db2_standard_use_type_backend(*this);
|
||||
}
|
||||
|
||||
db2_vector_into_type_backend *
|
||||
db2_statement_backend::make_vector_into_type_backend()
|
||||
{
|
||||
return new db2_vector_into_type_backend(*this);
|
||||
}
|
||||
|
||||
db2_vector_use_type_backend * db2_statement_backend::make_vector_use_type_backend()
|
||||
{
|
||||
hasVectorUseElements = true;
|
||||
return new db2_vector_use_type_backend(*this);
|
||||
}
|
||||
14
src/backends/db2/test/CMakeLists.txt
Normal file
14
src/backends/db2/test/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2011 Denis Chapligin
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend_test(
|
||||
BACKEND DB2
|
||||
SOURCE test-db2.cpp
|
||||
CONNSTR "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off")
|
||||
454
src/backends/db2/test/test-db2.cpp
Normal file
454
src/backends/db2/test/test-db2.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-db2.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_db2();
|
||||
|
||||
|
||||
//
|
||||
// Support for soci Common Tests
|
||||
//
|
||||
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(ID INTEGER, VAL SMALLINT, C CHAR, STR VARCHAR(20), SH SMALLINT, UL NUMERIC(20), D DOUBLE, "
|
||||
"TM TIMESTAMP, I1 INTEGER, I2 INTEGER, I3 INTEGER, NAME VARCHAR(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(NUM_FLOAT DOUBLE, NUM_INT INTEGER, NAME VARCHAR(20), SOMETIME TIMESTAMP, CHR CHAR)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(NAME VARCHAR(100) NOT NULL, PHONE VARCHAR(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(VAL INTEGER)";
|
||||
}
|
||||
};
|
||||
|
||||
class test_context :public test_context_base
|
||||
{
|
||||
public:
|
||||
test_context(backend_factory const & pi_back_end, std::string const & pi_connect_string)
|
||||
: test_context_base(pi_back_end, pi_connect_string) {}
|
||||
|
||||
table_creator_base* table_creator_1(session & pr_s) const
|
||||
{
|
||||
pr_s << "SET CURRENT SCHEMA = 'DB2INST1'";
|
||||
return new table_creator_one(pr_s);
|
||||
}
|
||||
|
||||
table_creator_base* table_creator_2(session & pr_s) const
|
||||
{
|
||||
pr_s << "SET CURRENT SCHEMA = 'DB2INST1'";
|
||||
return new table_creator_two(pr_s);
|
||||
}
|
||||
|
||||
table_creator_base* table_creator_3(session & pr_s) const
|
||||
{
|
||||
pr_s << "SET CURRENT SCHEMA = 'DB2INST1'";
|
||||
return new table_creator_three(pr_s);
|
||||
}
|
||||
|
||||
table_creator_base* table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const & pi_datdt_string) const
|
||||
{
|
||||
return "to_date('" + pi_datdt_string + "', 'YYYY-MM-DD HH24:MI:SS')";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Additional tests to exercise the DB2 backend
|
||||
//
|
||||
|
||||
void test1()
|
||||
{
|
||||
{
|
||||
session sql(backEnd, connectString);
|
||||
|
||||
sql << "SELECT CURRENT TIMESTAMP FROM SYSIBM.SYSDUMMY1";
|
||||
sql << "SELECT " << 123 << " FROM SYSIBM.SYSDUMMY1";
|
||||
|
||||
std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8))";
|
||||
sql << query;
|
||||
|
||||
{
|
||||
const int i = 7;
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,"id");
|
||||
int j = 0;
|
||||
sql << "select id from db2inst1.SOCI_TEST where id=7", into(j);
|
||||
assert(j == i);
|
||||
}
|
||||
|
||||
{
|
||||
const long int li = 9;
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(li,"id");
|
||||
long int lj = 0;;
|
||||
sql << "select id from db2inst1.SOCI_TEST where id=9", into(lj);
|
||||
assert(lj == li);
|
||||
}
|
||||
|
||||
{
|
||||
const long long ll = 11;
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(ll,"id");
|
||||
long long lj = 0;
|
||||
sql << "select id from db2inst1.SOCI_TEST where id=11", into(lj);
|
||||
assert(lj == ll);
|
||||
}
|
||||
|
||||
{
|
||||
const int i = 13;
|
||||
indicator i_ind = i_ok;
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,i_ind,"id");
|
||||
int j = 0;
|
||||
indicator j_ind = i_null;
|
||||
sql << "select id from db2inst1.SOCI_TEST where id=13", into(j,j_ind);
|
||||
assert(j == i);
|
||||
assert(j_ind == i_ok);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> numbers(100);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
numbers[i] = i + 1000;
|
||||
}
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(numbers,"id");
|
||||
sql << "select id from db2inst1.SOCI_TEST where id >= 1000 and id < 2000 order by id", into(numbers);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 1000);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> numbers(100);
|
||||
std::vector<indicator> inds(100);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
numbers[i] = i + 2000;
|
||||
inds[i] = i_ok;
|
||||
}
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(numbers,inds,"id");
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
numbers[i] = 0;
|
||||
inds[i] = i_null;
|
||||
}
|
||||
sql << "select id from db2inst1.SOCI_TEST where id >= 2000 and id < 3000 order by id", into(numbers,inds);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 2000);
|
||||
assert(inds[i] == i_ok);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int i = 0;
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id < 1000", into(i));
|
||||
st.execute();
|
||||
st.fetch();
|
||||
assert (i == 7);
|
||||
st.fetch();
|
||||
assert (i == 9);
|
||||
st.fetch();
|
||||
assert (i == 11);
|
||||
st.fetch();
|
||||
assert (i == 13);
|
||||
}
|
||||
|
||||
{
|
||||
int i = 0;
|
||||
indicator i_ind = i_null;
|
||||
std::string d;
|
||||
indicator d_ind = i_ok;
|
||||
statement st = (sql.prepare << "select id, data from db2inst1.SOCI_TEST where id = 13", into(i, i_ind), into(d, d_ind));
|
||||
st.execute();
|
||||
st.fetch();
|
||||
assert (i == 13);
|
||||
assert (i_ind == i_ok);
|
||||
assert (d_ind == i_null);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> numbers(100);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
numbers[i] = 0;
|
||||
}
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id >= 1000 order by id", into(numbers));
|
||||
st.execute();
|
||||
st.fetch();
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 1000);
|
||||
}
|
||||
st.fetch();
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 2000);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> numbers(100);
|
||||
std::vector<indicator> inds(100);
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
numbers[i] = 0;
|
||||
inds[i] = i_null;
|
||||
}
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST where id >= 1000 order by id", into(numbers, inds));
|
||||
st.execute();
|
||||
st.fetch();
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 1000);
|
||||
assert(inds[i] == i_ok);
|
||||
}
|
||||
st.fetch();
|
||||
for (int i = 0 ; i < 100 ; i++)
|
||||
{
|
||||
assert(numbers[i] == i + 2000);
|
||||
assert(inds[i] == i_ok);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// XXX: what is the purpose of this test?? what is the expected value?
|
||||
int i = 0;
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(i));
|
||||
}
|
||||
|
||||
{
|
||||
// XXX: what is the purpose of this test?? what is the expected value?
|
||||
int i = 0;
|
||||
indicator ind = i_ok;
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(i, ind));
|
||||
}
|
||||
|
||||
{
|
||||
// XXX: what is the purpose of this test?? what is the expected value?
|
||||
std::vector<int> numbers(100);
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(numbers));
|
||||
}
|
||||
|
||||
{
|
||||
// XXX: what is the purpose of this test?? what is the expected value?
|
||||
std::vector<int> numbers(100);
|
||||
std::vector<indicator> inds(100);
|
||||
statement st = (sql.prepare << "select id from db2inst1.SOCI_TEST", use(numbers, inds));
|
||||
}
|
||||
|
||||
sql<<"DROP TABLE DB2INST1.SOCI_TEST";
|
||||
|
||||
sql.commit();
|
||||
}
|
||||
|
||||
std::cout << "test 1 passed" << std::endl;
|
||||
}
|
||||
|
||||
void test2() {
|
||||
{
|
||||
session sql(backEnd, connectString);
|
||||
|
||||
std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)";
|
||||
sql << query;
|
||||
|
||||
{
|
||||
int i = 7;
|
||||
std::string n("test");
|
||||
sql << "insert into db2inst1.SOCI_TEST (id,data) values (:id,:name)", use(i,"id"),use(n,"name");
|
||||
int j;
|
||||
std::string m;
|
||||
sql << "select id,data from db2inst1.SOCI_TEST where id=7", into(j),into(m);
|
||||
assert (j == i);
|
||||
assert (m == n);
|
||||
}
|
||||
|
||||
{
|
||||
int i = 8;
|
||||
sql << "insert into db2inst1.SOCI_TEST (id) values (:id)", use(i,"id");
|
||||
int j;
|
||||
std::string m;
|
||||
indicator ind = i_ok;
|
||||
sql << "select id,data from db2inst1.SOCI_TEST where id=8", into(j),into(m,ind);
|
||||
assert(j == i);
|
||||
assert(ind==i_null);
|
||||
}
|
||||
|
||||
{
|
||||
std::tm dt;
|
||||
sql << "select current timestamp from sysibm.sysdummy1",into(dt);
|
||||
sql << "insert into db2inst1.SOCI_TEST (dt) values (:dt)",use(dt,"dt");
|
||||
std::tm dt2;
|
||||
sql << "select dt from db2inst1.SOCI_TEST where dt is not null", into(dt2);
|
||||
assert(dt2.tm_year == dt.tm_year && dt2.tm_mon == dt.tm_mon && dt2.tm_mday == dt.tm_mday &&
|
||||
dt2.tm_hour == dt.tm_hour && dt2.tm_min == dt.tm_min && dt2.tm_sec == dt.tm_sec);
|
||||
}
|
||||
|
||||
sql<<"DROP TABLE DB2INST1.SOCI_TEST";
|
||||
sql.commit();
|
||||
}
|
||||
|
||||
std::cout << "test 2 passed" << std::endl;
|
||||
}
|
||||
|
||||
void test3() {
|
||||
{
|
||||
session sql(backEnd, connectString);
|
||||
int i;
|
||||
|
||||
std::string query = "CREATE TABLE DB2INST1.SOCI_TEST (ID BIGINT,DATA VARCHAR(8),DT TIMESTAMP)";
|
||||
sql << query;
|
||||
|
||||
std::vector<long long> ids(100);
|
||||
std::vector<std::string> data(100);
|
||||
std::vector<std::tm> dts(100);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
ids[i] = 1000000000LL + i;
|
||||
data[i] = "test";
|
||||
dts[i].tm_year = 112;
|
||||
dts[i].tm_mon = 7;
|
||||
dts[i].tm_mday = 17;
|
||||
dts[i].tm_hour = 0;
|
||||
dts[i].tm_min = 0;
|
||||
dts[i].tm_sec = i % 60;
|
||||
}
|
||||
|
||||
sql << "insert into db2inst1.SOCI_TEST (id, data, dt) values (:id, :data, :dt)",
|
||||
use(ids, "id"), use(data,"data"), use(dts, "dt");
|
||||
|
||||
i = 0;
|
||||
rowset<row> rs = (sql.prepare<<"SELECT ID, DATA, DT FROM DB2INST1.SOCI_TEST");
|
||||
for (rowset<row>::const_iterator it = rs.begin(); it != rs.end(); it++)
|
||||
{
|
||||
const row & r = *it;
|
||||
const long long id = r.get<long long>(0);
|
||||
const std::string data = r.get<std::string>(1);
|
||||
const std::tm dt = r.get<std::tm>(2);
|
||||
|
||||
assert(id == 1000000000LL + i);
|
||||
assert(data == "test");
|
||||
assert(dt.tm_year == 112);
|
||||
assert(dt.tm_mon == 7);
|
||||
assert(dt.tm_mday == 17);
|
||||
assert(dt.tm_hour == 0);
|
||||
assert(dt.tm_min == 0);
|
||||
assert(dt.tm_sec == i % 60);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
sql<<"DROP TABLE DB2INST1.SOCI_TEST";
|
||||
sql.commit();
|
||||
}
|
||||
|
||||
std::cout << "test 3 passed" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "usage: " << argv[0]
|
||||
<< " connectstring\n"
|
||||
<< "example: " << argv[0]
|
||||
<< " \'DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off\'\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
try
|
||||
{
|
||||
std::cout<<"\nSOCI DB2 Tests:\n\n";
|
||||
|
||||
session sql(backEnd, connectString);
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to delete the test table from previous runs
|
||||
sql << "DROP TABLE DB2INST1.SOCI_TEST";
|
||||
}
|
||||
catch (soci_error const & e)
|
||||
{
|
||||
// if the table didn't exist, then proceed
|
||||
}
|
||||
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
// ...
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
411
src/backends/db2/vector-into-type.cpp
Normal file
411
src/backends/db2/vector-into-type.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
void db2_vector_into_type_backend::prepare_indicators(std::size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
throw soci_error("Vectors of size 0 are not allowed.");
|
||||
}
|
||||
|
||||
indVec.resize(size);
|
||||
indptr = &indVec[0];
|
||||
}
|
||||
|
||||
void db2_vector_into_type_backend::define_by_pos(
|
||||
int &position, void *data, exchange_type type)
|
||||
{
|
||||
this->data = data; // for future reference
|
||||
this->type = type; // for future reference
|
||||
|
||||
SQLINTEGER size = 0; // also dummy
|
||||
|
||||
switch (type)
|
||||
{
|
||||
// simple cases
|
||||
case x_short:
|
||||
{
|
||||
cType = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
std::vector<short> *vp = static_cast<std::vector<short> *>(data);
|
||||
std::vector<short> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
cType = SQL_C_SLONG;
|
||||
size = sizeof(SQLINTEGER);
|
||||
std::vector<SQLINTEGER> *vp = static_cast<std::vector<SQLINTEGER> *>(data);
|
||||
std::vector<SQLINTEGER> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
cType = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
std::vector<long long> *vp
|
||||
= static_cast<std::vector<long long> *>(data);
|
||||
std::vector<long long> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
std::vector<unsigned long long> *vp
|
||||
= static_cast<std::vector<unsigned long long> *>(data);
|
||||
std::vector<unsigned long long> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
cType = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
std::vector<double> *vp = static_cast<std::vector<double> *>(data);
|
||||
std::vector<double> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
|
||||
case x_char:
|
||||
{
|
||||
cType = SQL_C_CHAR;
|
||||
|
||||
std::vector<char> *v
|
||||
= static_cast<std::vector<char> *>(data);
|
||||
|
||||
prepare_indicators(v->size());
|
||||
|
||||
size = sizeof(char) * 2;
|
||||
std::size_t bufSize = size * v->size();
|
||||
|
||||
colSize = size;
|
||||
|
||||
buf = new char[bufSize];
|
||||
data = buf;
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
cType = SQL_C_CHAR;
|
||||
std::vector<std::string> *v
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
colSize = statement_.column_size(position) + 1;
|
||||
std::size_t bufSize = colSize * v->size();
|
||||
buf = new char[bufSize];
|
||||
|
||||
prepare_indicators(v->size());
|
||||
|
||||
size = static_cast<SQLINTEGER>(colSize);
|
||||
data = buf;
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
cType = SQL_C_TYPE_TIMESTAMP;
|
||||
std::vector<std::tm> *v
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
|
||||
prepare_indicators(v->size());
|
||||
|
||||
size = sizeof(TIMESTAMP_STRUCT);
|
||||
colSize = size;
|
||||
|
||||
std::size_t bufSize = size * v->size();
|
||||
|
||||
buf = new char[bufSize];
|
||||
data = buf;
|
||||
}
|
||||
break;
|
||||
|
||||
case x_statement: break; // not supported
|
||||
case x_rowid: break; // not supported
|
||||
case x_blob: break; // not supported
|
||||
}
|
||||
|
||||
SQLRETURN cliRC = SQLBindCol(statement_.hStmt, static_cast<SQLUSMALLINT>(position++),
|
||||
cType, data, size, indptr);
|
||||
if (cliRC != SQL_SUCCESS)
|
||||
{
|
||||
throw db2_soci_error("Error while pre-fetching into vector",cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
void db2_vector_into_type_backend::pre_fetch()
|
||||
{
|
||||
// nothing to do for the supported types
|
||||
}
|
||||
|
||||
void db2_vector_into_type_backend::post_fetch(bool gotData, indicator *ind)
|
||||
{
|
||||
if (gotData)
|
||||
{
|
||||
// first, deal with data
|
||||
|
||||
// only std::string, std::tm and Statement need special handling
|
||||
if (type == x_char)
|
||||
{
|
||||
std::vector<char> *vp
|
||||
= static_cast<std::vector<char> *>(data);
|
||||
|
||||
std::vector<char> &v(*vp);
|
||||
char *pos = buf;
|
||||
std::size_t const vsize = v.size();
|
||||
for (std::size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
v[i] = *pos;
|
||||
pos += colSize;
|
||||
}
|
||||
}
|
||||
if (type == x_stdstring)
|
||||
{
|
||||
std::vector<std::string> *vp
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
|
||||
std::vector<std::string> &v(*vp);
|
||||
|
||||
char *pos = buf;
|
||||
std::size_t const vsize = v.size();
|
||||
for (std::size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
v[i].assign(pos, strlen(pos));
|
||||
pos += colSize;
|
||||
}
|
||||
}
|
||||
else if (type == x_stdtm)
|
||||
{
|
||||
std::vector<std::tm> *vp
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
|
||||
std::vector<std::tm> &v(*vp);
|
||||
char *pos = buf;
|
||||
std::size_t const vsize = v.size();
|
||||
for (std::size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
std::tm t;
|
||||
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(pos);
|
||||
t.tm_isdst = -1;
|
||||
t.tm_year = ts->year - 1900;
|
||||
t.tm_mon = ts->month - 1;
|
||||
t.tm_mday = ts->day;
|
||||
t.tm_hour = ts->hour;
|
||||
t.tm_min = ts->minute;
|
||||
t.tm_sec = ts->second;
|
||||
|
||||
// normalize and compute the remaining fields
|
||||
std::mktime(&t);
|
||||
v[i] = t;
|
||||
pos += colSize;
|
||||
}
|
||||
}
|
||||
|
||||
// then - deal with indicators
|
||||
if (ind != NULL)
|
||||
{
|
||||
std::size_t const indSize = statement_.get_number_of_rows();
|
||||
for (std::size_t i = 0; i != indSize; ++i)
|
||||
{
|
||||
if (indVec[i] > 0)
|
||||
{
|
||||
ind[i] = i_ok;
|
||||
}
|
||||
else if (indVec[i] == SQL_NULL_DATA)
|
||||
{
|
||||
ind[i] = i_null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ind[i] = i_truncated;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::size_t const indSize = statement_.get_number_of_rows();
|
||||
for (std::size_t i = 0; i != indSize; ++i)
|
||||
{
|
||||
if (indVec[i] == SQL_NULL_DATA)
|
||||
{
|
||||
// fetched null and no indicator - programming error!
|
||||
throw soci_error(
|
||||
"Null value fetched and no indicator defined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // gotData == false
|
||||
{
|
||||
// nothing to do here, vectors are truncated anyway
|
||||
}
|
||||
}
|
||||
|
||||
void db2_vector_into_type_backend::resize(std::size_t sz)
|
||||
{
|
||||
indVec.resize(sz);
|
||||
switch (type)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
{
|
||||
std::vector<char> *v = static_cast<std::vector<char> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
std::vector<short> *v = static_cast<std::vector<short> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
std::vector<SQLINTEGER> *v = static_cast<std::vector<SQLINTEGER> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
std::vector<long long> *v
|
||||
= static_cast<std::vector<long long> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
std::vector<unsigned long long> *v
|
||||
= static_cast<std::vector<unsigned long long> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
std::vector<double> *v
|
||||
= static_cast<std::vector<double> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::vector<std::string> *v
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::vector<std::tm> *v
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
v->resize(sz);
|
||||
}
|
||||
break;
|
||||
|
||||
case x_statement: break; // not supported
|
||||
case x_rowid: break; // not supported
|
||||
case x_blob: break; // not supported
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t db2_vector_into_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
{
|
||||
std::vector<char> *v = static_cast<std::vector<char> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
std::vector<short> *v = static_cast<std::vector<short> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
std::vector<SQLINTEGER> *v = static_cast<std::vector<SQLINTEGER> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
std::vector<long long> *v
|
||||
= static_cast<std::vector<long long> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
std::vector<unsigned long long> *v
|
||||
= static_cast<std::vector<unsigned long long> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
std::vector<double> *v
|
||||
= static_cast<std::vector<double> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::vector<std::string> *v
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::vector<std::tm> *v
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
sz = v->size();
|
||||
}
|
||||
break;
|
||||
|
||||
case x_statement: break; // not supported
|
||||
case x_rowid: break; // not supported
|
||||
case x_blob: break; // not supported
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void db2_vector_into_type_backend::clean_up()
|
||||
{
|
||||
if (buf != NULL)
|
||||
{
|
||||
delete [] buf;
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
395
src/backends/db2/vector-use-type.cpp
Normal file
395
src/backends/db2/vector-use-type.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
//
|
||||
// Copyright (C) 2011-2013 Denis Chapligin
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_DB2_SOURCE
|
||||
#include "soci-db2.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disables the warning about converting int to void*. This is a 64 bit compatibility
|
||||
// warning, but odbc requires the value to be converted on this line
|
||||
// SQLSetStmtAttr(statement_.hstmt_, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)arraySize, 0);
|
||||
#pragma warning(disable:4312)
|
||||
#endif
|
||||
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
void db2_vector_use_type_backend::prepare_indicators(std::size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
throw soci_error("Vectors of size 0 are not allowed.");
|
||||
}
|
||||
|
||||
indVec.resize(size);
|
||||
indptr = &indVec[0];
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::prepare_for_bind(void *&data, SQLUINTEGER &size,
|
||||
SQLSMALLINT &sqlType, SQLSMALLINT &cType)
|
||||
{
|
||||
switch (type)
|
||||
{ // simple cases
|
||||
case x_short:
|
||||
{
|
||||
sqlType = SQL_SMALLINT;
|
||||
cType = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
std::vector<short> *vp = static_cast<std::vector<short> *>(data);
|
||||
std::vector<short> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
sqlType = SQL_INTEGER;
|
||||
cType = SQL_C_SLONG;
|
||||
size = sizeof(int);
|
||||
std::vector<int> *vp = static_cast<std::vector<int> *>(data);
|
||||
std::vector<int> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
std::vector<long long> *vp
|
||||
= static_cast<std::vector<long long> *>(data);
|
||||
std::vector<long long> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
std::vector<unsigned long long> *vp
|
||||
= static_cast<std::vector<unsigned long long> *>(data);
|
||||
std::vector<unsigned long long> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
sqlType = SQL_DOUBLE;
|
||||
cType = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
std::vector<double> *vp = static_cast<std::vector<double> *>(data);
|
||||
std::vector<double> &v(*vp);
|
||||
prepare_indicators(v.size());
|
||||
data = &v[0];
|
||||
}
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
case x_char:
|
||||
{
|
||||
std::vector<char> *vp
|
||||
= static_cast<std::vector<char> *>(data);
|
||||
std::size_t const vsize = vp->size();
|
||||
|
||||
prepare_indicators(vsize);
|
||||
|
||||
size = sizeof(char) * 2;
|
||||
buf = new char[size * vsize];
|
||||
|
||||
char *pos = buf;
|
||||
|
||||
for (std::size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
*pos++ = (*vp)[i];
|
||||
*pos++ = 0;
|
||||
}
|
||||
|
||||
sqlType = SQL_CHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
data = buf;
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
sqlType = SQL_CHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
|
||||
std::vector<std::string> *vp
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
std::vector<std::string> &v(*vp);
|
||||
|
||||
std::size_t maxSize = 0;
|
||||
std::size_t const vecSize = v.size();
|
||||
prepare_indicators(vecSize);
|
||||
for (std::size_t i = 0; i != vecSize; ++i)
|
||||
{
|
||||
std::size_t sz = v[i].length() + 1; // add one for null
|
||||
indVec[i] = static_cast<long>(sz);
|
||||
maxSize = sz > maxSize ? sz : maxSize;
|
||||
}
|
||||
|
||||
buf = new char[maxSize * vecSize];
|
||||
memset(buf, 0, maxSize * vecSize);
|
||||
|
||||
char *pos = buf;
|
||||
for (std::size_t i = 0; i != vecSize; ++i)
|
||||
{
|
||||
strncpy(pos, v[i].c_str(), v[i].length());
|
||||
pos += maxSize;
|
||||
}
|
||||
|
||||
data = buf;
|
||||
size = static_cast<SQLINTEGER>(maxSize);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::vector<std::tm> *vp
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
|
||||
prepare_indicators(vp->size());
|
||||
|
||||
buf = new char[sizeof(TIMESTAMP_STRUCT) * vp->size()];
|
||||
|
||||
sqlType = SQL_TYPE_TIMESTAMP;
|
||||
cType = SQL_C_TYPE_TIMESTAMP;
|
||||
data = buf;
|
||||
size = 19; // This number is not the size in bytes, but the number
|
||||
// of characters in the date if it was written out
|
||||
// yyyy-mm-dd hh:mm:ss
|
||||
}
|
||||
break;
|
||||
|
||||
case x_statement: break; // not supported
|
||||
case x_rowid: break; // not supported
|
||||
case x_blob: break; // not supported
|
||||
}
|
||||
|
||||
colSize = size;
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::bind_helper(int &position, void *data, details::exchange_type type)
|
||||
{
|
||||
this->data = data; // for future reference
|
||||
this->type = type; // for future reference
|
||||
|
||||
SQLSMALLINT sqlType;
|
||||
SQLSMALLINT cType;
|
||||
SQLUINTEGER size;
|
||||
|
||||
prepare_for_bind(data, size, sqlType, cType);
|
||||
|
||||
SQLINTEGER arraySize = (SQLINTEGER)indVec.size();
|
||||
SQLSetStmtAttr(statement_.hStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)arraySize, 0);
|
||||
|
||||
SQLRETURN cliRC = SQLBindParameter(statement_.hStmt, static_cast<SQLUSMALLINT>(position++),
|
||||
SQL_PARAM_INPUT, cType, sqlType, size, 0,
|
||||
static_cast<SQLPOINTER>(data), size, indptr);
|
||||
|
||||
if ( cliRC != SQL_SUCCESS )
|
||||
{
|
||||
throw db2_soci_error("Error while binding value to column", cliRC);
|
||||
}
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::bind_by_pos(int &position,
|
||||
void *data, exchange_type type)
|
||||
{
|
||||
if (statement_.use_binding_method_ == details::db2::BOUND_BY_NAME)
|
||||
{
|
||||
throw soci_error("Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
statement_.use_binding_method_ = details::db2::BOUND_BY_POSITION;
|
||||
|
||||
bind_helper(position, data, type);
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::bind_by_name(
|
||||
std::string const &name, void *data, exchange_type type)
|
||||
{
|
||||
int position = -1;
|
||||
int count = 1;
|
||||
|
||||
if (statement_.use_binding_method_ == details::db2::BOUND_BY_POSITION)
|
||||
{
|
||||
throw soci_error("Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
statement_.use_binding_method_ = details::db2::BOUND_BY_NAME;
|
||||
|
||||
for (std::vector<std::string>::iterator it = statement_.names.begin();
|
||||
it != statement_.names.end(); ++it)
|
||||
{
|
||||
if (*it == name)
|
||||
{
|
||||
position = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (position != -1)
|
||||
{
|
||||
bind_helper(position, data, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Unable to find name '" << name << "' to bind to";
|
||||
throw soci_error(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::pre_use(indicator const *ind)
|
||||
{
|
||||
// first deal with data
|
||||
if (type == x_stdtm)
|
||||
{
|
||||
std::vector<std::tm> *vp
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
|
||||
std::vector<std::tm> &v(*vp);
|
||||
|
||||
char *pos = buf;
|
||||
std::size_t const vsize = v.size();
|
||||
for (std::size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
std::tm t = v[i];
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(pos);
|
||||
|
||||
ts->year = static_cast<SQLSMALLINT>(t.tm_year + 1900);
|
||||
ts->month = static_cast<SQLUSMALLINT>(t.tm_mon + 1);
|
||||
ts->day = static_cast<SQLUSMALLINT>(t.tm_mday);
|
||||
ts->hour = static_cast<SQLUSMALLINT>(t.tm_hour);
|
||||
ts->minute = static_cast<SQLUSMALLINT>(t.tm_min);
|
||||
ts->second = static_cast<SQLUSMALLINT>(t.tm_sec);
|
||||
ts->fraction = 0;
|
||||
pos += sizeof(TIMESTAMP_STRUCT);
|
||||
}
|
||||
}
|
||||
|
||||
// then handle indicators
|
||||
if (ind != NULL)
|
||||
{
|
||||
std::size_t const vsize = size();
|
||||
for (std::size_t i = 0; i != vsize; ++i, ++ind)
|
||||
{
|
||||
if (*ind == i_null)
|
||||
{
|
||||
indVec[i] = SQL_NULL_DATA; // null
|
||||
}
|
||||
else
|
||||
{
|
||||
// for strings we have already set the values
|
||||
if (type != x_stdstring)
|
||||
{
|
||||
indVec[i] = SQL_NTS; // value is OK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no indicators - treat all fields as OK
|
||||
std::size_t const vsize = size();
|
||||
for (std::size_t i = 0; i != vsize; ++i, ++ind)
|
||||
{
|
||||
// for strings we have already set the values
|
||||
if (type != x_stdstring)
|
||||
{
|
||||
indVec[i] = SQL_NTS; // value is OK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t db2_vector_use_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
{
|
||||
std::vector<char> *vp = static_cast<std::vector<char> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
std::vector<short> *vp = static_cast<std::vector<short> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
std::vector<int> *vp = static_cast<std::vector<int> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
std::vector<long long> *vp
|
||||
= static_cast<std::vector<long long> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
std::vector<unsigned long long> *vp
|
||||
= static_cast<std::vector<unsigned long long> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
std::vector<double> *vp
|
||||
= static_cast<std::vector<double> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::vector<std::string> *vp
|
||||
= static_cast<std::vector<std::string> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::vector<std::tm> *vp
|
||||
= static_cast<std::vector<std::tm> *>(data);
|
||||
sz = vp->size();
|
||||
}
|
||||
break;
|
||||
|
||||
case x_statement: break; // not supported
|
||||
case x_rowid: break; // not supported
|
||||
case x_blob: break; // not supported
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void db2_vector_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf != NULL)
|
||||
{
|
||||
delete [] buf;
|
||||
buf = NULL;
|
||||
}
|
||||
}
|
||||
17
src/backends/empty/CMakeLists.txt
Normal file
17
src/backends/empty/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend(Empty
|
||||
HEADERS soci-empty.h
|
||||
DESCRIPTION "SOCI backend skeleton for development of new backends"
|
||||
AUTHORS "Maciej Sobczak, Stephen Hutton"
|
||||
MAINTAINERS "Maciej Sobczak")
|
||||
|
||||
add_subdirectory(test)
|
||||
88
src/backends/empty/Makefile.basic
Normal file
88
src/backends/empty/Makefile.basic
Normal file
@@ -0,0 +1,88 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
EMPTYINCLUDEDIR =
|
||||
|
||||
# The rest of the Makefile is indepentent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
CXXFLAGSSO = ${CXXFLAGS} -fPIC
|
||||
INCLUDEDIRS = -I../../core ${EMPTYINCLUDEDIR}
|
||||
|
||||
|
||||
OBJECTS = blob.o factory.o row-id.o session.o standard-into-type.o \
|
||||
standard-use-type.o statement.o vector-into-type.o vector-use-type.o
|
||||
|
||||
OBJECTSSO = blob-s.o factory-s.o row-id-s.o session-s.o \
|
||||
standard-into-type-s.o standard-use-type-s.o statement-s.o \
|
||||
vector-into-type-s.o vector-use-type-s.o
|
||||
|
||||
|
||||
libsoci_empty.a : ${OBJECTS}
|
||||
ar rv $@ $?
|
||||
rm *.o
|
||||
|
||||
|
||||
blob.o : blob.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
factory.o : factory.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
row-id.o : row-id.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
session.o : session.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type.o : standard-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type.o : standard-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
statement.o : statement.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type.o : vector-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type.o : vector-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
|
||||
shared : ${OBJECTSSO}
|
||||
${COMPILER} -shared -o libsoci_empty.so ${OBJECTSSO}
|
||||
rm *.o
|
||||
|
||||
blob-s.o : blob.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
factory-s.o : factory.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
row-id-s.o : row-id.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
session-s.o : session.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type-s.o : standard-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type-s.o : standard-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
statement-s.o : statement.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type-s.o : vector-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type-s.o : vector-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f libsoci_empty.a libsoci_empty.so
|
||||
61
src/backends/empty/blob.cpp
Normal file
61
src/backends/empty/blob.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
empty_blob_backend::empty_blob_backend(empty_session_backend &session)
|
||||
: session_(session)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
empty_blob_backend::~empty_blob_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
std::size_t empty_blob_backend::get_len()
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t empty_blob_backend::read(
|
||||
std::size_t /* offset */, char * /* buf */, std::size_t /* toRead */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t empty_blob_backend::write(
|
||||
std::size_t /* offset */, char const * /* buf */,
|
||||
std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t empty_blob_backend::append(
|
||||
char const * /* buf */, std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void empty_blob_backend::trim(std::size_t /* newLen */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
42
src/backends/empty/factory.cpp
Normal file
42
src/backends/empty/factory.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
#include <backend-loader.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
// concrete factory for Empty concrete strategies
|
||||
empty_session_backend* empty_backend_factory::make_session(
|
||||
connection_parameters const& parameters) const
|
||||
{
|
||||
return new empty_session_backend(parameters);
|
||||
}
|
||||
|
||||
empty_backend_factory const soci::empty;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_EMPTY_DECL backend_factory const* factory_empty()
|
||||
{
|
||||
return &soci::empty;
|
||||
}
|
||||
|
||||
SOCI_EMPTY_DECL void register_factory_empty()
|
||||
{
|
||||
soci::dynamic_backends::register_backend("empty", soci::empty);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
27
src/backends/empty/row-id.cpp
Normal file
27
src/backends/empty/row-id.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
empty_rowid_backend::empty_rowid_backend(empty_session_backend & /* session */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
empty_rowid_backend::~empty_rowid_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
63
src/backends/empty/session.cpp
Normal file
63
src/backends/empty/session.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
empty_session_backend::empty_session_backend(
|
||||
connection_parameters const & /* parameters */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
empty_session_backend::~empty_session_backend()
|
||||
{
|
||||
clean_up();
|
||||
}
|
||||
|
||||
void empty_session_backend::begin()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_session_backend::commit()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_session_backend::rollback()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_session_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
empty_statement_backend * empty_session_backend::make_statement_backend()
|
||||
{
|
||||
return new empty_statement_backend(*this);
|
||||
}
|
||||
|
||||
empty_rowid_backend * empty_session_backend::make_rowid_backend()
|
||||
{
|
||||
return new empty_rowid_backend(*this);
|
||||
}
|
||||
|
||||
empty_blob_backend * empty_session_backend::make_blob_backend()
|
||||
{
|
||||
return new empty_blob_backend(*this);
|
||||
}
|
||||
193
src/backends/empty/soci-empty.h
Normal file
193
src/backends/empty/soci-empty.h
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_EMPTY_H_INCLUDED
|
||||
#define SOCI_EMPTY_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef SOCI_DLL
|
||||
# ifdef SOCI_EMPTY_SOURCE
|
||||
# define SOCI_EMPTY_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define SOCI_EMPTY_DECL __declspec(dllimport)
|
||||
# endif // SOCI_EMPTY_SOURCE
|
||||
# endif // SOCI_DLL
|
||||
#endif // _WIN32
|
||||
//
|
||||
// If SOCI_EMPTY_DECL isn't defined yet define it now
|
||||
#ifndef SOCI_EMPTY_DECL
|
||||
# define SOCI_EMPTY_DECL
|
||||
#endif
|
||||
|
||||
#include "soci-backend.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
struct empty_statement_backend;
|
||||
|
||||
struct SOCI_EMPTY_DECL empty_standard_into_type_backend : details::standard_into_type_backend
|
||||
{
|
||||
empty_standard_into_type_backend(empty_statement_backend &st)
|
||||
: statement_(st)
|
||||
{}
|
||||
|
||||
void define_by_pos(int& position, void* data, details::exchange_type type);
|
||||
|
||||
void pre_fetch();
|
||||
void post_fetch(bool gotData, bool calledFromFetch, indicator* ind);
|
||||
|
||||
void clean_up();
|
||||
|
||||
empty_statement_backend& statement_;
|
||||
};
|
||||
|
||||
struct SOCI_EMPTY_DECL empty_vector_into_type_backend : details::vector_into_type_backend
|
||||
{
|
||||
empty_vector_into_type_backend(empty_statement_backend &st)
|
||||
: statement_(st)
|
||||
{}
|
||||
|
||||
void define_by_pos(int& position, void* data, details::exchange_type type);
|
||||
|
||||
void pre_fetch();
|
||||
void post_fetch(bool gotData, indicator* ind);
|
||||
|
||||
void resize(std::size_t sz);
|
||||
std::size_t size();
|
||||
|
||||
void clean_up();
|
||||
|
||||
empty_statement_backend& statement_;
|
||||
};
|
||||
|
||||
struct SOCI_EMPTY_DECL empty_standard_use_type_backend : details::standard_use_type_backend
|
||||
{
|
||||
empty_standard_use_type_backend(empty_statement_backend &st)
|
||||
: statement_(st)
|
||||
{}
|
||||
|
||||
void bind_by_pos(int& position, void* data, details::exchange_type type, bool readOnly);
|
||||
void bind_by_name(std::string const& name, void* data, details::exchange_type type, bool readOnly);
|
||||
|
||||
void pre_use(indicator const* ind);
|
||||
void post_use(bool gotData, indicator* ind);
|
||||
|
||||
void clean_up();
|
||||
|
||||
empty_statement_backend& statement_;
|
||||
};
|
||||
|
||||
struct SOCI_EMPTY_DECL empty_vector_use_type_backend : details::vector_use_type_backend
|
||||
{
|
||||
empty_vector_use_type_backend(empty_statement_backend &st)
|
||||
: statement_(st) {}
|
||||
|
||||
void bind_by_pos(int& position, void* data, details::exchange_type type);
|
||||
void bind_by_name(std::string const& name, void* data, details::exchange_type type);
|
||||
|
||||
void pre_use(indicator const* ind);
|
||||
|
||||
std::size_t size();
|
||||
|
||||
void clean_up();
|
||||
|
||||
empty_statement_backend& statement_;
|
||||
};
|
||||
|
||||
struct empty_session_backend;
|
||||
struct SOCI_EMPTY_DECL empty_statement_backend : details::statement_backend
|
||||
{
|
||||
empty_statement_backend(empty_session_backend &session);
|
||||
|
||||
void alloc();
|
||||
void clean_up();
|
||||
void prepare(std::string const& query, details::statement_type eType);
|
||||
|
||||
exec_fetch_result execute(int number);
|
||||
exec_fetch_result fetch(int number);
|
||||
|
||||
long long get_affected_rows();
|
||||
int get_number_of_rows();
|
||||
|
||||
std::string rewrite_for_procedure_call(std::string const& query);
|
||||
|
||||
int prepare_for_describe();
|
||||
void describe_column(int colNum, data_type& dtype, std::string& columnName);
|
||||
|
||||
empty_standard_into_type_backend* make_into_type_backend();
|
||||
empty_standard_use_type_backend* make_use_type_backend();
|
||||
empty_vector_into_type_backend* make_vector_into_type_backend();
|
||||
empty_vector_use_type_backend* make_vector_use_type_backend();
|
||||
|
||||
empty_session_backend& session_;
|
||||
};
|
||||
|
||||
struct empty_rowid_backend : details::rowid_backend
|
||||
{
|
||||
empty_rowid_backend(empty_session_backend &session);
|
||||
|
||||
~empty_rowid_backend();
|
||||
};
|
||||
|
||||
struct empty_blob_backend : details::blob_backend
|
||||
{
|
||||
empty_blob_backend(empty_session_backend& session);
|
||||
|
||||
~empty_blob_backend();
|
||||
|
||||
std::size_t get_len();
|
||||
std::size_t read(std::size_t offset, char* buf, std::size_t toRead);
|
||||
std::size_t write(std::size_t offset, char const* buf, std::size_t toWrite);
|
||||
std::size_t append(char const* buf, std::size_t toWrite);
|
||||
void trim(std::size_t newLen);
|
||||
|
||||
empty_session_backend& session_;
|
||||
};
|
||||
|
||||
struct empty_session_backend : details::session_backend
|
||||
{
|
||||
empty_session_backend(connection_parameters const& parameters);
|
||||
|
||||
~empty_session_backend();
|
||||
|
||||
void begin();
|
||||
void commit();
|
||||
void rollback();
|
||||
|
||||
std::string get_backend_name() const { return "empty"; }
|
||||
|
||||
void clean_up();
|
||||
|
||||
empty_statement_backend* make_statement_backend();
|
||||
empty_rowid_backend* make_rowid_backend();
|
||||
empty_blob_backend* make_blob_backend();
|
||||
};
|
||||
|
||||
struct SOCI_EMPTY_DECL empty_backend_factory : backend_factory
|
||||
{
|
||||
empty_backend_factory() {}
|
||||
empty_session_backend* make_session(connection_parameters const& parameters) const;
|
||||
};
|
||||
|
||||
extern SOCI_EMPTY_DECL empty_backend_factory const empty;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_EMPTY_DECL backend_factory const* factory_empty();
|
||||
SOCI_EMPTY_DECL void register_factory_empty();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_EMPTY_H_INCLUDED
|
||||
39
src/backends/empty/standard-into-type.cpp
Normal file
39
src/backends/empty/standard-into-type.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
void empty_standard_into_type_backend::define_by_pos(
|
||||
int & /* position */, void * /* data */, exchange_type /* type */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_into_type_backend::pre_fetch()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_into_type_backend::post_fetch(
|
||||
bool /* gotData */, bool /* calledFromFetch */, indicator * /* ind */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_into_type_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
47
src/backends/empty/standard-use-type.cpp
Normal file
47
src/backends/empty/standard-use-type.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
void empty_standard_use_type_backend::bind_by_pos(
|
||||
int & /* position */, void * /* data */,
|
||||
exchange_type /* type */, bool /* readOnly */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_use_type_backend::bind_by_name(
|
||||
std::string const & /* name */, void * /* data */,
|
||||
exchange_type /* type */, bool /* readOnly */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_use_type_backend::pre_use(indicator const * /* ind */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_use_type_backend::post_use(
|
||||
bool /* gotData */, indicator * /* ind */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_standard_use_type_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
103
src/backends/empty/statement.cpp
Normal file
103
src/backends/empty/statement.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
empty_statement_backend::empty_statement_backend(empty_session_backend &session)
|
||||
: session_(session)
|
||||
{
|
||||
}
|
||||
|
||||
void empty_statement_backend::alloc()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_statement_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_statement_backend::prepare(std::string const & /* query */,
|
||||
statement_type /* eType */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
empty_statement_backend::execute(int /* number */)
|
||||
{
|
||||
// ...
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
empty_statement_backend::fetch(int /* number */)
|
||||
{
|
||||
// ...
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
long long empty_statement_backend::get_affected_rows()
|
||||
{
|
||||
// ...
|
||||
return -1;
|
||||
}
|
||||
|
||||
int empty_statement_backend::get_number_of_rows()
|
||||
{
|
||||
// ...
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string empty_statement_backend::rewrite_for_procedure_call(
|
||||
std::string const &query)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
int empty_statement_backend::prepare_for_describe()
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void empty_statement_backend::describe_column(int /* colNum */,
|
||||
data_type & /* type */, std::string & /* columnName */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
empty_standard_into_type_backend * empty_statement_backend::make_into_type_backend()
|
||||
{
|
||||
return new empty_standard_into_type_backend(*this);
|
||||
}
|
||||
|
||||
empty_standard_use_type_backend * empty_statement_backend::make_use_type_backend()
|
||||
{
|
||||
return new empty_standard_use_type_backend(*this);
|
||||
}
|
||||
|
||||
empty_vector_into_type_backend *
|
||||
empty_statement_backend::make_vector_into_type_backend()
|
||||
{
|
||||
return new empty_vector_into_type_backend(*this);
|
||||
}
|
||||
|
||||
empty_vector_use_type_backend * empty_statement_backend::make_vector_use_type_backend()
|
||||
{
|
||||
return new empty_vector_use_type_backend(*this);
|
||||
}
|
||||
1
src/backends/empty/test/.gitignore
vendored
Normal file
1
src/backends/empty/test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test_empty
|
||||
14
src/backends/empty/test/CMakeLists.txt
Normal file
14
src/backends/empty/test/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend_test(
|
||||
BACKEND Empty
|
||||
SOURCE test-empty.cpp
|
||||
CONNSTR "dummy")
|
||||
13
src/backends/empty/test/Makefile.basic
Normal file
13
src/backends/empty/test/Makefile.basic
Normal file
@@ -0,0 +1,13 @@
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
INCLUDEDIRS = -I.. -I../../../core
|
||||
LIBDIRS = -L.. -L../../../core
|
||||
LIBS = -lsoci_core -lsoci_empty -ldl
|
||||
|
||||
|
||||
test-empty : test-empty.cpp
|
||||
${COMPILER} -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} ${LIBDIRS} ${LIBS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f test-empty
|
||||
170
src/backends/empty/test/test-empty.cpp
Normal file
170
src/backends/empty/test/test-empty.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-empty.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
using namespace soci;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_empty();
|
||||
|
||||
|
||||
// NOTE:
|
||||
// This file is supposed to serve two purposes:
|
||||
// 1. To be a starting point for implementing new tests (for new backends).
|
||||
// 2. To exercise (at least some of) the syntax and try the SOCI library
|
||||
// against different compilers, even in those environments where there
|
||||
// is no database. SOCI uses advanced template techniques which are known
|
||||
// to cause problems on different versions of popular compilers, and this
|
||||
// test is handy to verify that the code is accepted by as many compilers
|
||||
// as possible.
|
||||
//
|
||||
// Both of these purposes mean that the actual code here is meaningless
|
||||
// from the database-development point of view. For new tests, you may wish
|
||||
// to remove this code and keep only the general structure of this file.
|
||||
|
||||
struct Person
|
||||
{
|
||||
int id;
|
||||
std::string firstName;
|
||||
std::string lastName;
|
||||
};
|
||||
|
||||
namespace soci
|
||||
{
|
||||
template<> struct type_conversion<Person>
|
||||
{
|
||||
typedef values base_type;
|
||||
static void from_base(values & /* r */, indicator /* ind */,
|
||||
Person & /* p */)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void test1()
|
||||
{
|
||||
{
|
||||
session sql(backEnd, connectString);
|
||||
|
||||
sql << "Do what I want.";
|
||||
sql << "Do what I want " << 123 << " times.";
|
||||
|
||||
std::string query = "some query";
|
||||
sql << query;
|
||||
|
||||
int i = 7;
|
||||
sql << "insert", use(i);
|
||||
sql << "select", into(i);
|
||||
|
||||
#if defined (__LP64__) || ( __WORDSIZE == 64 )
|
||||
long int li = 9;
|
||||
sql << "insert", use(li);
|
||||
sql << "select", into(li);
|
||||
#endif
|
||||
|
||||
long long ll = 11;
|
||||
sql << "insert", use(ll);
|
||||
sql << "select", into(ll);
|
||||
|
||||
indicator ind = i_ok;
|
||||
sql << "insert", use(i, ind);
|
||||
sql << "select", into(i, ind);
|
||||
|
||||
std::vector<int> numbers(100);
|
||||
sql << "insert", use(numbers);
|
||||
sql << "select", into(numbers);
|
||||
|
||||
std::vector<indicator> inds(100);
|
||||
sql << "insert", use(numbers, inds);
|
||||
sql << "select", into(numbers, inds);
|
||||
|
||||
{
|
||||
statement st = (sql.prepare << "select", into(i));
|
||||
st.execute();
|
||||
st.fetch();
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "select", into(i, ind));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "select", into(numbers));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "select", into(numbers, inds));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "insert", use(i));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "insert", use(i, ind));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "insert", use(numbers));
|
||||
}
|
||||
{
|
||||
statement st = (sql.prepare << "insert", use(numbers, inds));
|
||||
}
|
||||
{
|
||||
Person p;
|
||||
sql << "select person", into(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::cout << "test 1 passed" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "usage: " << argv[0]
|
||||
<< " connectstring\n"
|
||||
<< "example: " << argv[0]
|
||||
<< " \'connect_string_for_empty_backend\'\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
test1();
|
||||
// test2();
|
||||
// ...
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
50
src/backends/empty/vector-into-type.cpp
Normal file
50
src/backends/empty/vector-into-type.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
void empty_vector_into_type_backend::define_by_pos(
|
||||
int & /* position */, void * /* data */, exchange_type /* type */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_vector_into_type_backend::pre_fetch()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_vector_into_type_backend::post_fetch(
|
||||
bool /* gotData */, indicator * /* ind */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_vector_into_type_backend::resize(std::size_t /* sz */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
std::size_t empty_vector_into_type_backend::size()
|
||||
{
|
||||
// ...
|
||||
return 1;
|
||||
}
|
||||
|
||||
void empty_vector_into_type_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
46
src/backends/empty/vector-use-type.cpp
Normal file
46
src/backends/empty/vector-use-type.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_EMPTY_SOURCE
|
||||
#include "soci-empty.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
void empty_vector_use_type_backend::bind_by_pos(int & /* position */,
|
||||
void * /* data */, exchange_type /* type */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_vector_use_type_backend::bind_by_name(
|
||||
std::string const & /* name */, void * /* data */,
|
||||
exchange_type /* type */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void empty_vector_use_type_backend::pre_use(indicator const * /* ind */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
std::size_t empty_vector_use_type_backend::size()
|
||||
{
|
||||
// ...
|
||||
return 1;
|
||||
}
|
||||
|
||||
void empty_vector_use_type_backend::clean_up()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
19
src/backends/firebird/CMakeLists.txt
Normal file
19
src/backends/firebird/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2013 Viacheslav Naydenov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
soci_backend(Firebird
|
||||
DEPENDS Firebird
|
||||
HEADERS soci-firebird.h common.h
|
||||
DESCRIPTION "SOCI backend for Firebird database engine"
|
||||
AUTHORS "Rafał Bobrowski"
|
||||
MAINTAINERS "Viacheslav Naydenov")
|
||||
|
||||
add_subdirectory(test)
|
||||
101
src/backends/firebird/Makefile.basic
Normal file
101
src/backends/firebird/Makefile.basic
Normal file
@@ -0,0 +1,101 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
FIREBIRDINCLUDEDIR = -I/usr/local/firebird/include
|
||||
|
||||
# The rest of the Makefile is indepentent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
CXXFLAGSSO = ${CXXFLAGS} -fPIC
|
||||
INCLUDEDIRS = -I../../core ${FIREBIRDINCLUDEDIR}
|
||||
|
||||
OBJECTS = blob.o factory.o row-id.o session.o standard-into-type.o \
|
||||
standard-use-type.o statement.o vector-into-type.o vector-use-type.o \
|
||||
error-firebird.o common.o
|
||||
|
||||
OBJECTSSO = blob-s.o factory-s.o row-id-s.o session-s.o \
|
||||
standard-into-type-s.o standard-use-type-s.o statement-s.o \
|
||||
vector-into-type-s.o vector-use-type-s.o error-firebird-s.o common-s.o
|
||||
|
||||
FIREBIRDLIBS = -lfbclient -lpthread
|
||||
|
||||
libsoci_firebird.a : ${OBJECTS}
|
||||
ar rv $@ $?
|
||||
rm *.o
|
||||
|
||||
soci-firebird.o : soci-firebird.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
blob.o : blob.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
error-firebird.o : error-firebird.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
common.o : common.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
factory.o : factory.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
row-id.o : row-id.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
session.o : session.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type.o : standard-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type.o : standard-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
statement.o : statement.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type.o : vector-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type.o : vector-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
shared : ${OBJECTSSO}
|
||||
${COMPILER} -shared -o libsoci_firebird.so ${OBJECTSSO} ${FIREBIRDLIBS}
|
||||
rm *.o
|
||||
|
||||
blob-s.o : blob.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
error-firebird-s.o : error-firebird.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
common-s.o : common.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
factory-s.o : factory.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
row-id-s.o : row-id.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
session-s.o : session.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type-s.o : standard-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type-s.o : standard-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
statement-s.o : statement.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type-s.o : vector-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type-s.o : vector-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
clean :
|
||||
rm -f *.o libsoci_firebird.a libsoci_firebird.so
|
||||
301
src/backends/firebird/blob.cpp
Normal file
301
src/backends/firebird/blob.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "error-firebird.h"
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
firebird_blob_backend::firebird_blob_backend(firebird_session_backend &session)
|
||||
: session_(session), from_db_(false), bhp_(0), loaded_(false),
|
||||
max_seg_size_(0)
|
||||
{}
|
||||
|
||||
firebird_blob_backend::~firebird_blob_backend()
|
||||
{
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
std::size_t firebird_blob_backend::get_len()
|
||||
{
|
||||
if (from_db_ && bhp_ == 0)
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
std::size_t firebird_blob_backend::read(
|
||||
std::size_t offset, char * buf, std::size_t toRead)
|
||||
{
|
||||
if (from_db_ && (loaded_ == false))
|
||||
{
|
||||
// this is blob fetched from database, but not loaded yet
|
||||
load();
|
||||
}
|
||||
|
||||
std::size_t size = data_.size();
|
||||
|
||||
if (offset > size)
|
||||
{
|
||||
throw soci_error("Can't read past-the-end of BLOB data");
|
||||
}
|
||||
|
||||
char * itr = buf;
|
||||
std::size_t limit = size - offset < toRead ? size - offset : toRead;
|
||||
std::size_t index = 0;
|
||||
|
||||
while (index < limit)
|
||||
{
|
||||
*itr = data_[offset+index];
|
||||
++index;
|
||||
++itr;
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
std::size_t firebird_blob_backend::write(std::size_t offset, char const * buf,
|
||||
std::size_t toWrite)
|
||||
{
|
||||
if (from_db_ && (loaded_ == false))
|
||||
{
|
||||
// this is blob fetched from database, but not loaded yet
|
||||
load();
|
||||
}
|
||||
|
||||
std::size_t size = data_.size();
|
||||
|
||||
if (offset > size)
|
||||
{
|
||||
throw soci_error("Can't write past-the-end of BLOB data");
|
||||
}
|
||||
|
||||
// make sure there is enough space in buffer
|
||||
if (toWrite > (size - offset))
|
||||
{
|
||||
data_.resize(size + (toWrite - (size - offset)));
|
||||
}
|
||||
|
||||
writeBuffer(offset, buf, toWrite);
|
||||
|
||||
return toWrite;
|
||||
}
|
||||
|
||||
std::size_t firebird_blob_backend::append(
|
||||
char const * buf, std::size_t toWrite)
|
||||
{
|
||||
if (from_db_ && (loaded_ == false))
|
||||
{
|
||||
// this is blob fetched from database, but not loaded yet
|
||||
load();
|
||||
}
|
||||
|
||||
std::size_t size = data_.size();
|
||||
data_.resize(size + toWrite);
|
||||
|
||||
writeBuffer(size, buf, toWrite);
|
||||
|
||||
return toWrite;
|
||||
}
|
||||
|
||||
void firebird_blob_backend::trim(std::size_t newLen)
|
||||
{
|
||||
if (from_db_ && (loaded_ == false))
|
||||
{
|
||||
// this is blob fetched from database, but not loaded yet
|
||||
load();
|
||||
}
|
||||
|
||||
data_.resize(newLen);
|
||||
}
|
||||
|
||||
void firebird_blob_backend::writeBuffer(std::size_t offset,
|
||||
char const * buf, std::size_t toWrite)
|
||||
{
|
||||
char const * itr = buf;
|
||||
char const * end_itr = buf + toWrite;
|
||||
|
||||
while (itr!=end_itr)
|
||||
{
|
||||
data_[offset++] = *itr++;
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_blob_backend::open()
|
||||
{
|
||||
if (bhp_ != 0)
|
||||
{
|
||||
// BLOB already opened
|
||||
return;
|
||||
}
|
||||
|
||||
ISC_STATUS stat[20];
|
||||
|
||||
if (isc_open_blob2(stat, &session_.dbhp_, &session_.trhp_, &bhp_,
|
||||
&bid_, 0, NULL))
|
||||
{
|
||||
bhp_ = 0L;
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
// get basic blob info
|
||||
long blob_size = getBLOBInfo();
|
||||
|
||||
data_.resize(blob_size);
|
||||
}
|
||||
|
||||
void firebird_blob_backend::cleanUp()
|
||||
{
|
||||
from_db_ = false;
|
||||
loaded_ = false;
|
||||
max_seg_size_ = 0;
|
||||
data_.resize(0);
|
||||
|
||||
if (bhp_ != 0)
|
||||
{
|
||||
// close blob
|
||||
ISC_STATUS stat[20];
|
||||
if (isc_close_blob(stat, &bhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
bhp_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// loads blob data into internal buffer
|
||||
void firebird_blob_backend::load()
|
||||
{
|
||||
if (bhp_ == 0)
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
ISC_STATUS stat[20];
|
||||
unsigned short bytes;
|
||||
std::vector<char>::size_type total_bytes = 0;
|
||||
bool keep_reading = false;
|
||||
|
||||
do
|
||||
{
|
||||
bytes = 0;
|
||||
// next segment of data
|
||||
// data_ is large-enough because we know total size of blob
|
||||
isc_get_segment(stat, &bhp_, &bytes, static_cast<short>(max_seg_size_),
|
||||
&data_[total_bytes]);
|
||||
|
||||
total_bytes += bytes;
|
||||
|
||||
if (total_bytes == data_.size())
|
||||
{
|
||||
// we have all BLOB data
|
||||
keep_reading = false;
|
||||
}
|
||||
else if (stat[1] == 0 || stat[1] == isc_segment)
|
||||
{
|
||||
// there is more data to read from current segment (0)
|
||||
// or there is next segment (isc_segment)
|
||||
keep_reading = true;
|
||||
}
|
||||
else if (stat[1] == isc_segstr_eof)
|
||||
{
|
||||
// BLOB is shorter then we expected ???
|
||||
keep_reading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// an error has occured
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
while (keep_reading);
|
||||
|
||||
loaded_ = true;
|
||||
}
|
||||
|
||||
// this method saves BLOB content to database
|
||||
// (a new BLOB will be created at this point)
|
||||
// BLOB will be closed after save.
|
||||
void firebird_blob_backend::save()
|
||||
{
|
||||
// close old blob if necessary
|
||||
ISC_STATUS stat[20];
|
||||
if (bhp_ != 0)
|
||||
{
|
||||
if (isc_close_blob(stat, &bhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
bhp_ = 0;
|
||||
}
|
||||
|
||||
// create new blob
|
||||
if (isc_create_blob(stat, &session_.dbhp_, &session_.trhp_,
|
||||
&bhp_, &bid_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
if (data_.size() > 0)
|
||||
{
|
||||
// write data
|
||||
if (isc_put_segment(stat, &bhp_,
|
||||
static_cast<unsigned short>(data_.size()), &data_[0]))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
cleanUp();
|
||||
from_db_ = true;
|
||||
}
|
||||
|
||||
// retrives number of segments and total length of BLOB
|
||||
// returns total length of BLOB
|
||||
long firebird_blob_backend::getBLOBInfo()
|
||||
{
|
||||
char blob_items[] = {isc_info_blob_max_segment, isc_info_blob_total_length};
|
||||
char res_buffer[20], *p, item;
|
||||
short length;
|
||||
long total_length = 0;
|
||||
|
||||
ISC_STATUS stat[20];
|
||||
|
||||
if (isc_blob_info(stat, &bhp_, sizeof(blob_items), blob_items,
|
||||
sizeof(res_buffer), res_buffer))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
for (p = res_buffer; *p != isc_info_end ;)
|
||||
{
|
||||
item = *p++;
|
||||
length = static_cast<short>(isc_vax_integer(p, 2));
|
||||
p += 2;
|
||||
switch (item)
|
||||
{
|
||||
case isc_info_blob_max_segment:
|
||||
max_seg_size_ = isc_vax_integer(p, length);
|
||||
break;
|
||||
case isc_info_blob_total_length:
|
||||
total_length = isc_vax_integer(p, length);
|
||||
break;
|
||||
case isc_info_truncated:
|
||||
throw soci_error("Fatal Error: BLOB info truncated!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
p += length;
|
||||
}
|
||||
|
||||
return total_length;
|
||||
}
|
||||
215
src/backends/firebird/common.cpp
Normal file
215
src/backends/firebird/common.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "common.h"
|
||||
#include <soci-backend.h>
|
||||
#include <ibase.h> // FireBird
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
namespace firebird
|
||||
{
|
||||
|
||||
char * allocBuffer(XSQLVAR* var)
|
||||
{
|
||||
std::size_t size;
|
||||
int type = var->sqltype & ~1;
|
||||
if (type == SQL_VARYING)
|
||||
{
|
||||
size = var->sqllen + sizeof(short);
|
||||
}
|
||||
else if (type == SQL_TIMESTAMP || type == SQL_TYPE_TIME
|
||||
|| type == SQL_TYPE_DATE)
|
||||
{
|
||||
size = sizeof(std::tm);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = var->sqllen;
|
||||
}
|
||||
|
||||
return new char[size];
|
||||
}
|
||||
|
||||
void tmEncode(short type, std::tm * src, void * dst)
|
||||
{
|
||||
switch (type & ~1)
|
||||
{
|
||||
// In Interbase v6 DATE represents a date-only data type,
|
||||
// in InterBase v5 DATE represents a date+time data type.
|
||||
case SQL_TIMESTAMP:
|
||||
isc_encode_timestamp(src, static_cast<ISC_TIMESTAMP*>(dst));
|
||||
break;
|
||||
case SQL_TYPE_TIME:
|
||||
isc_encode_sql_time(src, static_cast<ISC_TIME*>(dst));
|
||||
break;
|
||||
case SQL_TYPE_DATE:
|
||||
isc_encode_sql_date(src, static_cast<ISC_DATE*>(dst));
|
||||
break;
|
||||
default:
|
||||
std::ostringstream msg;
|
||||
msg << "Unexpected type of date/time field (" << type << ")";
|
||||
throw soci_error(msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
void tmDecode(short type, void * src, std::tm * dst)
|
||||
{
|
||||
switch (type & ~1)
|
||||
{
|
||||
case SQL_TIMESTAMP:
|
||||
isc_decode_timestamp(static_cast<ISC_TIMESTAMP*>(src), dst);
|
||||
break;
|
||||
case SQL_TYPE_TIME:
|
||||
isc_decode_sql_time(static_cast<ISC_TIME*>(src), dst);
|
||||
break;
|
||||
case SQL_TYPE_DATE:
|
||||
isc_decode_sql_date(static_cast<ISC_DATE*>(src), dst);
|
||||
break;
|
||||
default:
|
||||
std::ostringstream msg;
|
||||
msg << "Unexpected type of date/time field (" << type << ")";
|
||||
throw soci_error(msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
void setTextParam(char const * s, std::size_t size, char * buf_,
|
||||
XSQLVAR * var)
|
||||
{
|
||||
//std::cerr << "setTextParam: var->sqltype=" << var->sqltype << std::endl;
|
||||
short sz = 0;
|
||||
if (size < static_cast<std::size_t>(var->sqllen))
|
||||
{
|
||||
sz = static_cast<short>(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
sz = var->sqllen;
|
||||
}
|
||||
|
||||
if ((var->sqltype & ~1) == SQL_VARYING)
|
||||
{
|
||||
std::memcpy(buf_, &sz, sizeof(short));
|
||||
std::memcpy(buf_ + sizeof(short), s, sz);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_TEXT)
|
||||
{
|
||||
std::memcpy(buf_, s, sz);
|
||||
if (sz < var->sqllen)
|
||||
{
|
||||
std::memset(buf_+sz, ' ', var->sqllen - sz);
|
||||
}
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_SHORT)
|
||||
{
|
||||
parse_decimal<short, unsigned short>(buf_, var, s);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_LONG)
|
||||
{
|
||||
parse_decimal<int, unsigned int>(buf_, var, s);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_INT64)
|
||||
{
|
||||
parse_decimal<long long, unsigned long long>(buf_, var, s);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_TIMESTAMP
|
||||
|| (var->sqltype & ~1) == SQL_TYPE_DATE)
|
||||
{
|
||||
unsigned short year, month, day, hour, min, sec;
|
||||
if (std::sscanf(s, "%hu-%hu-%hu %hu:%hu:%hu",
|
||||
&year, &month, &day, &hour, &min, &sec) != 6)
|
||||
{
|
||||
if (std::sscanf(s, "%hu-%hu-%huT%hu:%hu:%hu",
|
||||
&year, &month, &day, &hour, &min, &sec) != 6)
|
||||
{
|
||||
hour = min = sec = 0;
|
||||
if (std::sscanf(s, "%hu-%hu-%hu", &year, &month, &day) != 3)
|
||||
{
|
||||
throw soci_error("Could not parse timestamp value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
std::tm t;
|
||||
std::memset(&t, 0, sizeof(t));
|
||||
t.tm_year = year - 1900;
|
||||
t.tm_mon = month - 1;
|
||||
t.tm_mday = day;
|
||||
t.tm_hour = hour;
|
||||
t.tm_min = min;
|
||||
t.tm_sec = sec;
|
||||
std::memcpy(buf_, &t, sizeof(t));
|
||||
tmEncode(var->sqltype, &t, buf_);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_TYPE_TIME)
|
||||
{
|
||||
unsigned short hour, min, sec;
|
||||
if (std::sscanf(s, "%hu:%hu:%hu", &hour, &min, &sec) != 3)
|
||||
{
|
||||
throw soci_error("Could not parse timestamp value.");
|
||||
}
|
||||
std::tm t;
|
||||
std::memset(&t, 0, sizeof(t));
|
||||
t.tm_hour = hour;
|
||||
t.tm_min = min;
|
||||
t.tm_sec = sec;
|
||||
std::memcpy(buf_, &t, sizeof(t));
|
||||
tmEncode(var->sqltype, &t, buf_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error("Unexpected string type.");
|
||||
}
|
||||
}
|
||||
|
||||
std::string getTextParam(XSQLVAR const *var)
|
||||
{
|
||||
//std::cerr << "getTextParam: var->sqltype=" << var->sqltype << std::endl;
|
||||
short size;
|
||||
std::size_t offset = 0;
|
||||
|
||||
if ((var->sqltype & ~1) == SQL_VARYING)
|
||||
{
|
||||
size = *reinterpret_cast<short*>(var->sqldata);
|
||||
offset = sizeof(short);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_TEXT)
|
||||
{
|
||||
size = var->sqllen;
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_SHORT)
|
||||
{
|
||||
return format_decimal<short>(var->sqldata, var->sqlscale);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_LONG)
|
||||
{
|
||||
return format_decimal<int>(var->sqldata, var->sqlscale);
|
||||
}
|
||||
else if ((var->sqltype & ~1) == SQL_INT64)
|
||||
{
|
||||
return format_decimal<long long>(var->sqldata, var->sqlscale);
|
||||
}
|
||||
else
|
||||
throw soci_error("Unexpected string type");
|
||||
|
||||
return std::string(var->sqldata + offset, size);
|
||||
}
|
||||
|
||||
} // namespace firebird
|
||||
|
||||
} // namespace details
|
||||
|
||||
} // namespace soci
|
||||
236
src/backends/firebird/common.h
Normal file
236
src/backends/firebird/common.h
Normal file
@@ -0,0 +1,236 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_FIREBIRD_COMMON_H_INCLUDED
|
||||
#define SOCI_FIREBIRD_COMMON_H_INCLUDED
|
||||
|
||||
#include "soci-firebird.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
namespace firebird
|
||||
{
|
||||
|
||||
char * allocBuffer(XSQLVAR* var);
|
||||
|
||||
void tmEncode(short type, std::tm * src, void * dst);
|
||||
|
||||
void tmDecode(short type, void * src, std::tm * dst);
|
||||
|
||||
void setTextParam(char const * s, std::size_t size, char * buf_,
|
||||
XSQLVAR * var);
|
||||
|
||||
std::string getTextParam(XSQLVAR const *var);
|
||||
|
||||
template <typename IntType>
|
||||
const char *str2dec(const char * s, IntType &out, int &scale)
|
||||
{
|
||||
int sign = 1;
|
||||
if ('+' == *s)
|
||||
++s;
|
||||
else if ('-' == *s)
|
||||
{
|
||||
sign = -1;
|
||||
++s;
|
||||
}
|
||||
scale = 0;
|
||||
bool period = false;
|
||||
IntType res = 0;
|
||||
for (out = 0; *s; ++s, out = res)
|
||||
{
|
||||
if (*s == '.')
|
||||
{
|
||||
if (period)
|
||||
return s;
|
||||
period = true;
|
||||
continue;
|
||||
}
|
||||
int d = *s - '0';
|
||||
if (d < 0 || d > 9)
|
||||
return s;
|
||||
res = res * 10 + d * sign;
|
||||
if (1 == sign)
|
||||
{
|
||||
if (res < out)
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res > out)
|
||||
return s;
|
||||
}
|
||||
if (period)
|
||||
++scale;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename T1>
|
||||
void to_isc(void * val, XSQLVAR * var, int x_scale = 0)
|
||||
{
|
||||
T1 value = *reinterpret_cast<T1*>(val);
|
||||
short scale = var->sqlscale + x_scale;
|
||||
short type = var->sqltype & ~1;
|
||||
long long divisor = 1, multiplier = 1;
|
||||
|
||||
if ((std::numeric_limits<T1>::is_integer == false) && scale >= 0 &&
|
||||
(type == SQL_SHORT || type == SQL_LONG || type == SQL_INT64))
|
||||
{
|
||||
throw soci_error("Can't convert non-integral value to integral column type");
|
||||
}
|
||||
|
||||
for (int i = 0; i > scale; --i)
|
||||
multiplier *= 10;
|
||||
for (int i = 0; i < scale; ++i)
|
||||
divisor *= 10;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SQL_SHORT:
|
||||
{
|
||||
short tmp = static_cast<short>(value*multiplier/divisor);
|
||||
std::memcpy(var->sqldata, &tmp, sizeof(short));
|
||||
}
|
||||
break;
|
||||
case SQL_LONG:
|
||||
{
|
||||
int tmp = static_cast<int>(value*multiplier/divisor);
|
||||
std::memcpy(var->sqldata, &tmp, sizeof(int));
|
||||
}
|
||||
break;
|
||||
case SQL_INT64:
|
||||
{
|
||||
long long tmp = static_cast<long long>(value*multiplier/divisor);
|
||||
std::memcpy(var->sqldata, &tmp, sizeof(long long));
|
||||
}
|
||||
break;
|
||||
case SQL_FLOAT:
|
||||
{
|
||||
float sql_value = static_cast<float>(value);
|
||||
std::memcpy(var->sqldata, &sql_value, sizeof(float));
|
||||
}
|
||||
break;
|
||||
case SQL_DOUBLE:
|
||||
{
|
||||
double sql_value = static_cast<double>(value);
|
||||
std::memcpy(var->sqldata, &sql_value, sizeof(double));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Incorrect data type for numeric conversion");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IntType, typename UIntType>
|
||||
void parse_decimal(void * val, XSQLVAR * var, const char * s)
|
||||
{
|
||||
int scale;
|
||||
UIntType t1;
|
||||
IntType t2;
|
||||
if (!*str2dec(s, t1, scale))
|
||||
std::memcpy(val, &t1, sizeof(t1));
|
||||
else if (!*str2dec(s, t2, scale))
|
||||
std::memcpy(val, &t2, sizeof(t2));
|
||||
else
|
||||
throw soci_error("Could not parse decimal value.");
|
||||
to_isc<IntType>(val, var, scale);
|
||||
}
|
||||
|
||||
template<typename IntType>
|
||||
std::string format_decimal(const void *sqldata, int sqlscale)
|
||||
{
|
||||
IntType x = *reinterpret_cast<const IntType *>(sqldata);
|
||||
std::stringstream out;
|
||||
out << x;
|
||||
std::string r = out.str();
|
||||
if (sqlscale < 0)
|
||||
{
|
||||
if (static_cast<int>(r.size()) - (x < 0) <= -sqlscale)
|
||||
{
|
||||
r = std::string(size_t(x < 0), '-') +
|
||||
std::string(-sqlscale - (r.size() - (x < 0)) + 1, '0') +
|
||||
r.substr(size_t(x < 0), std::string::npos);
|
||||
}
|
||||
return r.substr(0, r.size() + sqlscale) + '.' +
|
||||
r.substr(r.size() + sqlscale, std::string::npos);
|
||||
}
|
||||
return r + std::string(sqlscale, '0');
|
||||
}
|
||||
|
||||
template<typename T1>
|
||||
T1 from_isc(XSQLVAR * var)
|
||||
{
|
||||
short scale = var->sqlscale;
|
||||
T1 tens = 1;
|
||||
|
||||
if (scale < 0)
|
||||
{
|
||||
if (std::numeric_limits<T1>::is_integer)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg << "Can't convert value with scale " << -scale
|
||||
<< " to integral type";
|
||||
throw soci_error(msg.str());
|
||||
}
|
||||
|
||||
for (int i = 0; i > scale; --i)
|
||||
{
|
||||
tens *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
switch (var->sqltype & ~1)
|
||||
{
|
||||
case SQL_SHORT:
|
||||
return static_cast<T1>(*reinterpret_cast<short*>(var->sqldata)/tens);
|
||||
case SQL_LONG:
|
||||
return static_cast<T1>(*reinterpret_cast<int*>(var->sqldata)/tens);
|
||||
case SQL_INT64:
|
||||
return static_cast<T1>(*reinterpret_cast<long long*>(var->sqldata)/tens);
|
||||
case SQL_FLOAT:
|
||||
return static_cast<T1>(*reinterpret_cast<float*>(var->sqldata));
|
||||
case SQL_DOUBLE:
|
||||
return static_cast<T1>(*reinterpret_cast<double*>(var->sqldata));
|
||||
default:
|
||||
throw soci_error("Incorrect data type for numeric conversion");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t getVectorSize(void *p)
|
||||
{
|
||||
std::vector<T> *v = static_cast<std::vector<T> *>(p);
|
||||
return v->size();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void resizeVector(void *p, std::size_t sz)
|
||||
{
|
||||
std::vector<T> *v = static_cast<std::vector<T> *>(p);
|
||||
v->resize(sz);
|
||||
}
|
||||
|
||||
} // namespace firebird
|
||||
|
||||
} // namespace details
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_FIREBIRD_COMMON_H_INCLUDED
|
||||
87
src/backends/firebird/error-firebird.cpp
Normal file
87
src/backends/firebird/error-firebird.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "error-firebird.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
firebird_soci_error::firebird_soci_error(std::string const & msg, ISC_STATUS const * status)
|
||||
: soci_error(msg)
|
||||
{
|
||||
if (status != 0)
|
||||
{
|
||||
std::size_t i = 0;
|
||||
while (i < stat_size && status[i] != 0)
|
||||
{
|
||||
status_.push_back(status[i++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
namespace firebird
|
||||
{
|
||||
|
||||
void get_iscerror_details(ISC_STATUS * status_vector, std::string &msg)
|
||||
{
|
||||
char msg_buffer[SOCI_FIREBIRD_ERRMSG];
|
||||
const ISC_STATUS *pvector = status_vector;
|
||||
|
||||
try
|
||||
{
|
||||
// fetching first error message
|
||||
fb_interpret(msg_buffer, SOCI_FIREBIRD_ERRMSG, &pvector);
|
||||
msg = msg_buffer;
|
||||
|
||||
// fetching next errors
|
||||
while (fb_interpret(msg_buffer, SOCI_FIREBIRD_ERRMSG, &pvector))
|
||||
{
|
||||
msg += "\n";
|
||||
msg += msg_buffer;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw firebird_soci_error("Exception catched while fetching error information");
|
||||
}
|
||||
}
|
||||
|
||||
bool check_iscerror(ISC_STATUS const * status_vector, long errNum)
|
||||
{
|
||||
std::size_t i=0;
|
||||
while (status_vector[i] != 0)
|
||||
{
|
||||
if (status_vector[i] == 1 && status_vector[i+1] == errNum)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void throw_iscerror(ISC_STATUS * status_vector)
|
||||
{
|
||||
std::string msg;
|
||||
|
||||
get_iscerror_details(status_vector, msg);
|
||||
throw firebird_soci_error(msg, status_vector);
|
||||
}
|
||||
|
||||
} // namespace firebird
|
||||
|
||||
} // namespace details
|
||||
|
||||
} // namespace soci
|
||||
35
src/backends/firebird/error-firebird.h
Normal file
35
src/backends/firebird/error-firebird.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_FIREBIRD_ERROR_H_INCLUDED
|
||||
#define SOCI_FIREBIRD_ERROR_H_INCLUDED
|
||||
|
||||
#include "soci-firebird.h"
|
||||
#include <string>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
namespace firebird
|
||||
{
|
||||
|
||||
void SOCI_FIREBIRD_DECL get_iscerror_details(ISC_STATUS * status_vector, std::string &msg);
|
||||
|
||||
bool SOCI_FIREBIRD_DECL check_iscerror(ISC_STATUS const * status_vector, long errNum);
|
||||
|
||||
void SOCI_FIREBIRD_DECL throw_iscerror(ISC_STATUS * status_vector);
|
||||
|
||||
} // namespace firebird
|
||||
|
||||
} // namespace details
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_FIREBIRD_ERROR_H_INCLUDED
|
||||
36
src/backends/firebird/factory.cpp
Normal file
36
src/backends/firebird/factory.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include <backend-loader.h>
|
||||
|
||||
using namespace soci;
|
||||
|
||||
firebird_session_backend * firebird_backend_factory::make_session(
|
||||
connection_parameters const & parameters) const
|
||||
{
|
||||
return new firebird_session_backend(parameters);
|
||||
}
|
||||
|
||||
firebird_backend_factory const soci::firebird;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_FIREBIRD_DECL backend_factory const * factory_firebird()
|
||||
{
|
||||
return &soci::firebird;
|
||||
}
|
||||
|
||||
SOCI_FIREBIRD_DECL void register_factory_firebird()
|
||||
{
|
||||
soci::dynamic_backends::register_backend("firebird", soci::firebird);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
21
src/backends/firebird/row-id.cpp
Normal file
21
src/backends/firebird/row-id.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
|
||||
using namespace soci;
|
||||
|
||||
firebird_rowid_backend::firebird_rowid_backend(firebird_session_backend & /* session */)
|
||||
{
|
||||
// Unsupported in Firebird backend
|
||||
throw soci_error("RowIDs are not supported");
|
||||
}
|
||||
|
||||
firebird_rowid_backend::~firebird_rowid_backend()
|
||||
{
|
||||
}
|
||||
375
src/backends/firebird/session.cpp
Normal file
375
src/backends/firebird/session.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "error-firebird.h"
|
||||
#include "session.h"
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Helpers of explodeISCConnectString() for reading words from a string. "Word"
|
||||
// here is defined very loosely as just a sequence of non-space characters.
|
||||
//
|
||||
// All these helper functions update the input iterator to point to the first
|
||||
// character not consumed by them.
|
||||
|
||||
// Advance the input iterator until the first non-space character or end of the
|
||||
// string.
|
||||
void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end)
|
||||
{
|
||||
std::locale const loc;
|
||||
for (; i != end; ++i)
|
||||
{
|
||||
if (!std::isspace(*i, loc))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the string of all characters until the first space or the specified
|
||||
// delimiter.
|
||||
//
|
||||
// Throws if the first non-space character after the end of the word is not the
|
||||
// delimiter. However just returns en empty string, without throwing, if
|
||||
// nothing is left at all in the string except for white space.
|
||||
std::string
|
||||
getWordUntil(std::string const &s, std::string::const_iterator &i, char delim)
|
||||
{
|
||||
std::string::const_iterator const end = s.end();
|
||||
skipWhiteSpace(i, end);
|
||||
|
||||
// We need to handle this case specially because it's not an error if
|
||||
// nothing at all remains in the string. But if anything does remain, then
|
||||
// we must have the delimiter.
|
||||
if (i == end)
|
||||
return std::string();
|
||||
|
||||
// Simply put anything until the delimiter into the word, stopping at the
|
||||
// first white space character.
|
||||
std::string word;
|
||||
std::locale const loc;
|
||||
for (; i != end; ++i)
|
||||
{
|
||||
if (*i == delim)
|
||||
break;
|
||||
|
||||
if (std::isspace(*i, loc))
|
||||
{
|
||||
skipWhiteSpace(i, end);
|
||||
if (i == end || *i != delim)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Expected '" << delim << "' at position "
|
||||
<< (i - s.begin() + 1)
|
||||
<< " in Firebird connection string \""
|
||||
<< s << "\".";
|
||||
|
||||
throw soci_error(os.str());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
word += *i;
|
||||
}
|
||||
|
||||
if (i == end)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Expected '" << delim
|
||||
<< "' not found before the end of the string "
|
||||
<< "in Firebird connection string \""
|
||||
<< s << "\".";
|
||||
|
||||
throw soci_error(os.str());
|
||||
}
|
||||
|
||||
++i; // Skip the delimiter itself.
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
// Return a possibly quoted word, i.e. either just a sequence of non-space
|
||||
// characters or everything inside a double-quoted string.
|
||||
//
|
||||
// Throws if the word is quoted and the closing quote is not found. However
|
||||
// doesn't throw, just returns an empty string if there is nothing left.
|
||||
std::string
|
||||
getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i)
|
||||
{
|
||||
std::string::const_iterator const end = s.end();
|
||||
skipWhiteSpace(i, end);
|
||||
|
||||
std::string word;
|
||||
|
||||
if (i != end && *i == '"')
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (++i == end)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Expected '\"' not found before the end of the string "
|
||||
"in Firebird connection string \""
|
||||
<< s << "\".";
|
||||
|
||||
throw soci_error(os.str());
|
||||
}
|
||||
|
||||
if (*i == '"')
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
|
||||
word += *i;
|
||||
}
|
||||
}
|
||||
else // Not quoted.
|
||||
{
|
||||
std::locale const loc;
|
||||
for (; i != end; ++i)
|
||||
{
|
||||
if (std::isspace(*i, loc))
|
||||
break;
|
||||
|
||||
word += *i;
|
||||
}
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
// retrieves parameters from the uniform connect string which is supposed to be
|
||||
// in the form "key=value[ key2=value2 ...]" and the values may be quoted to
|
||||
// allow including spaces into them. Notice that currently there is no way to
|
||||
// include both a space and a double quote in a value.
|
||||
std::map<std::string, std::string>
|
||||
explodeISCConnectString(std::string const &connectString)
|
||||
{
|
||||
std::map<std::string, std::string> parameters;
|
||||
|
||||
std::string key, value;
|
||||
for (std::string::const_iterator i = connectString.begin(); ; )
|
||||
{
|
||||
key = getWordUntil(connectString, i, '=');
|
||||
if (key.empty())
|
||||
break;
|
||||
|
||||
value = getPossiblyQuotedWord(connectString, i);
|
||||
|
||||
parameters.insert(std::pair<std::string, std::string>(key, value));
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// extracts given parameter from map previusly build with explodeISCConnectString
|
||||
bool getISCConnectParameter(std::map<std::string, std::string> const & m, std::string const & key,
|
||||
std::string & value)
|
||||
{
|
||||
std::map <std::string, std::string> :: const_iterator i;
|
||||
value.clear();
|
||||
|
||||
i = m.find(key);
|
||||
|
||||
if (i != m.end())
|
||||
{
|
||||
value = i->second;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
firebird_session_backend::firebird_session_backend(
|
||||
connection_parameters const & parameters) : dbhp_(0), trhp_(0)
|
||||
, decimals_as_strings_(false)
|
||||
{
|
||||
// extract connection parameters
|
||||
std::map<std::string, std::string>
|
||||
params(explodeISCConnectString(parameters.get_connect_string()));
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
std::string param;
|
||||
|
||||
// preparing connection options
|
||||
if (getISCConnectParameter(params, "user", param))
|
||||
{
|
||||
setDPBOption(isc_dpb_user_name, param);
|
||||
}
|
||||
|
||||
if (getISCConnectParameter(params, "password", param))
|
||||
{
|
||||
setDPBOption(isc_dpb_password, param);
|
||||
}
|
||||
|
||||
if (getISCConnectParameter(params, "role", param))
|
||||
{
|
||||
setDPBOption(isc_dpb_sql_role_name, param);
|
||||
}
|
||||
|
||||
if (getISCConnectParameter(params, "charset", param))
|
||||
{
|
||||
setDPBOption(isc_dpb_lc_ctype, param);
|
||||
}
|
||||
|
||||
if (getISCConnectParameter(params, "service", param) == false)
|
||||
{
|
||||
throw soci_error("Service name not specified.");
|
||||
}
|
||||
|
||||
// connecting data base
|
||||
if (isc_attach_database(stat, static_cast<short>(param.size()),
|
||||
const_cast<char*>(param.c_str()), &dbhp_,
|
||||
static_cast<short>(dpb_.size()), const_cast<char*>(dpb_.c_str())))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
if (getISCConnectParameter(params, "decimals_as_strings", param))
|
||||
{
|
||||
decimals_as_strings_ = param == "1" || param == "Y" || param == "y";
|
||||
}
|
||||
// starting transaction
|
||||
begin();
|
||||
}
|
||||
|
||||
|
||||
void firebird_session_backend::begin()
|
||||
{
|
||||
// Transaction is always started in ctor, because Firebird can't work
|
||||
// without active transaction.
|
||||
// Transaction will be automatically commited in cleanUp method.
|
||||
if (trhp_ == 0)
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
if (isc_start_transaction(stat, &trhp_, 1, &dbhp_, 0, NULL))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
firebird_session_backend::~firebird_session_backend()
|
||||
{
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
void firebird_session_backend::setDPBOption(int const option, std::string const & value)
|
||||
{
|
||||
|
||||
if (dpb_.size() == 0)
|
||||
{
|
||||
dpb_.append(1, static_cast<char>(isc_dpb_version1));
|
||||
}
|
||||
|
||||
// now we are adding new option
|
||||
dpb_.append(1, static_cast<char>(option));
|
||||
dpb_.append(1, static_cast<char>(value.size()));
|
||||
dpb_.append(value);
|
||||
}
|
||||
|
||||
void firebird_session_backend::commit()
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
if (trhp_ != 0)
|
||||
{
|
||||
if (isc_commit_transaction(stat, &trhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
trhp_ = 0;
|
||||
}
|
||||
|
||||
#ifndef SOCI_FIREBIRD_NORESTARTTRANSACTION
|
||||
begin();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void firebird_session_backend::rollback()
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
if (trhp_ != 0)
|
||||
{
|
||||
if (isc_rollback_transaction(stat, &trhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
trhp_ = 0;
|
||||
}
|
||||
|
||||
#ifndef SOCI_FIREBIRD_NORESTARTTRANSACTION
|
||||
begin();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void firebird_session_backend::cleanUp()
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
// at the end of session our transaction is finally commited.
|
||||
if (trhp_ != 0)
|
||||
{
|
||||
if (isc_commit_transaction(stat, &trhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
trhp_ = 0;
|
||||
}
|
||||
|
||||
if (isc_detach_database(stat, &dbhp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
dbhp_ = 0L;
|
||||
}
|
||||
|
||||
bool firebird_session_backend::get_next_sequence_value(
|
||||
session & s, std::string const & sequence, long & value)
|
||||
{
|
||||
// We could use isq_execute2() directly but this is even simpler.
|
||||
s << "select next value for " + sequence + " from rdb$database",
|
||||
into(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
firebird_statement_backend * firebird_session_backend::make_statement_backend()
|
||||
{
|
||||
return new firebird_statement_backend(*this);
|
||||
}
|
||||
|
||||
firebird_rowid_backend * firebird_session_backend::make_rowid_backend()
|
||||
{
|
||||
return new firebird_rowid_backend(*this);
|
||||
}
|
||||
|
||||
firebird_blob_backend * firebird_session_backend::make_blob_backend()
|
||||
{
|
||||
return new firebird_blob_backend(*this);
|
||||
}
|
||||
349
src/backends/firebird/soci-firebird.h
Normal file
349
src/backends/firebird/soci-firebird.h
Normal file
@@ -0,0 +1,349 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef SOCI_FIREBIRD_H_INCLUDED
|
||||
#define SOCI_FIREBIRD_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef SOCI_DLL
|
||||
# ifdef SOCI_FIREBIRD_SOURCE
|
||||
# define SOCI_FIREBIRD_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define SOCI_FIREBIRD_DECL __declspec(dllimport)
|
||||
# endif // SOCI_DLL
|
||||
# endif // SOCI_FIREBIRD_SOURCE
|
||||
#endif // _WIN32
|
||||
|
||||
//
|
||||
// If SOCI_FIREBIRD_DECL isn't defined yet define it now
|
||||
#ifndef SOCI_FIREBIRD_DECL
|
||||
# define SOCI_FIREBIRD_DECL
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ciso646> // To understand and/or/not on MSVC9
|
||||
#endif
|
||||
#include <soci-backend.h>
|
||||
#include <ibase.h> // FireBird
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
std::size_t const stat_size = 20;
|
||||
|
||||
// size of buffer for error messages. All examples use this value.
|
||||
// Anyone knows, where it is stated that 512 bytes is enough ?
|
||||
std::size_t const SOCI_FIREBIRD_ERRMSG = 512;
|
||||
|
||||
class SOCI_FIREBIRD_DECL firebird_soci_error : public soci_error
|
||||
{
|
||||
public:
|
||||
firebird_soci_error(std::string const & msg,
|
||||
ISC_STATUS const * status = 0);
|
||||
|
||||
~firebird_soci_error() throw() {};
|
||||
|
||||
std::vector<ISC_STATUS> status_;
|
||||
};
|
||||
|
||||
enum BuffersType
|
||||
{
|
||||
eStandard, eVector
|
||||
};
|
||||
|
||||
struct firebird_statement_backend;
|
||||
struct firebird_standard_into_type_backend : details::standard_into_type_backend
|
||||
{
|
||||
firebird_standard_into_type_backend(firebird_statement_backend &st)
|
||||
: statement_(st), buf_(NULL)
|
||||
{}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, bool calledFromFetch,
|
||||
indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
firebird_statement_backend &statement_;
|
||||
virtual void exchangeData();
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
|
||||
char *buf_;
|
||||
short indISCHolder_;
|
||||
};
|
||||
|
||||
struct firebird_vector_into_type_backend : details::vector_into_type_backend
|
||||
{
|
||||
firebird_vector_into_type_backend(firebird_statement_backend &st)
|
||||
: statement_(st), buf_(NULL)
|
||||
{}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, indicator *ind);
|
||||
|
||||
virtual void resize(std::size_t sz);
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
firebird_statement_backend &statement_;
|
||||
virtual void exchangeData(std::size_t row);
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
|
||||
char *buf_;
|
||||
short indISCHolder_;
|
||||
};
|
||||
|
||||
struct firebird_standard_use_type_backend : details::standard_use_type_backend
|
||||
{
|
||||
firebird_standard_use_type_backend(firebird_statement_backend &st)
|
||||
: statement_(st), buf_(NULL), indISCHolder_(0)
|
||||
{}
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
virtual void post_use(bool gotData, indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
firebird_statement_backend &statement_;
|
||||
virtual void exchangeData();
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
|
||||
char *buf_;
|
||||
short indISCHolder_;
|
||||
};
|
||||
|
||||
struct firebird_vector_use_type_backend : details::vector_use_type_backend
|
||||
{
|
||||
firebird_vector_use_type_backend(firebird_statement_backend &st)
|
||||
: statement_(st), inds_(NULL), buf_(NULL), indISCHolder_(0)
|
||||
{}
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
firebird_statement_backend &statement_;
|
||||
virtual void exchangeData(std::size_t row);
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
indicator const *inds_;
|
||||
|
||||
char *buf_;
|
||||
short indISCHolder_;
|
||||
};
|
||||
|
||||
struct firebird_session_backend;
|
||||
struct firebird_statement_backend : details::statement_backend
|
||||
{
|
||||
firebird_statement_backend(firebird_session_backend &session);
|
||||
|
||||
virtual void alloc();
|
||||
virtual void clean_up();
|
||||
virtual void prepare(std::string const &query,
|
||||
details::statement_type eType);
|
||||
|
||||
virtual exec_fetch_result execute(int number);
|
||||
virtual exec_fetch_result fetch(int number);
|
||||
|
||||
virtual long long get_affected_rows();
|
||||
virtual int get_number_of_rows();
|
||||
|
||||
virtual std::string rewrite_for_procedure_call(std::string const &query);
|
||||
|
||||
virtual int prepare_for_describe();
|
||||
virtual void describe_column(int colNum, data_type &dtype,
|
||||
std::string &columnName);
|
||||
|
||||
virtual firebird_standard_into_type_backend * make_into_type_backend();
|
||||
virtual firebird_standard_use_type_backend * make_use_type_backend();
|
||||
virtual firebird_vector_into_type_backend * make_vector_into_type_backend();
|
||||
virtual firebird_vector_use_type_backend * make_vector_use_type_backend();
|
||||
|
||||
firebird_session_backend &session_;
|
||||
|
||||
isc_stmt_handle stmtp_;
|
||||
XSQLDA * sqldap_;
|
||||
XSQLDA * sqlda2p_;
|
||||
|
||||
bool boundByName_;
|
||||
bool boundByPos_;
|
||||
|
||||
friend struct firebird_vector_into_type_backend;
|
||||
friend struct firebird_standard_into_type_backend;
|
||||
friend struct firebird_vector_use_type_backend;
|
||||
friend struct firebird_standard_use_type_backend;
|
||||
|
||||
protected:
|
||||
int rowsFetched_;
|
||||
bool endOfRowSet_;
|
||||
|
||||
long long rowsAffectedBulk_; // number of rows affected by the last bulk operation
|
||||
|
||||
virtual void exchangeData(bool gotData, int row);
|
||||
virtual void prepareSQLDA(XSQLDA ** sqldap, int size = 10);
|
||||
virtual void rewriteQuery(std::string const & query,
|
||||
std::vector<char> & buffer);
|
||||
virtual void rewriteParameters(std::string const & src,
|
||||
std::vector<char> & dst);
|
||||
|
||||
BuffersType intoType_;
|
||||
BuffersType useType_;
|
||||
|
||||
std::vector<std::vector<indicator> > inds_;
|
||||
std::vector<void*> intos_;
|
||||
std::vector<void*> uses_;
|
||||
|
||||
// named parameters
|
||||
std::map <std::string, int> names_;
|
||||
|
||||
bool procedure_;
|
||||
};
|
||||
|
||||
struct firebird_rowid_backend : details::rowid_backend
|
||||
{
|
||||
firebird_rowid_backend(firebird_session_backend &session);
|
||||
|
||||
~firebird_rowid_backend();
|
||||
};
|
||||
|
||||
struct firebird_blob_backend : details::blob_backend
|
||||
{
|
||||
firebird_blob_backend(firebird_session_backend &session);
|
||||
|
||||
~firebird_blob_backend();
|
||||
|
||||
virtual std::size_t get_len();
|
||||
virtual std::size_t read(std::size_t offset, char *buf,
|
||||
std::size_t toRead);
|
||||
virtual std::size_t write(std::size_t offset, char const *buf,
|
||||
std::size_t toWrite);
|
||||
virtual std::size_t append(char const *buf, std::size_t toWrite);
|
||||
virtual void trim(std::size_t newLen);
|
||||
|
||||
firebird_session_backend &session_;
|
||||
|
||||
virtual void save();
|
||||
virtual void assign(ISC_QUAD const & bid)
|
||||
{
|
||||
cleanUp();
|
||||
|
||||
bid_ = bid;
|
||||
from_db_ = true;
|
||||
}
|
||||
|
||||
// BLOB id from in database
|
||||
ISC_QUAD bid_;
|
||||
|
||||
// BLOB id was fetched from database (true)
|
||||
// or this is new BLOB
|
||||
bool from_db_;
|
||||
|
||||
// BLOB handle
|
||||
isc_blob_handle bhp_;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void open();
|
||||
virtual long getBLOBInfo();
|
||||
virtual void load();
|
||||
virtual void writeBuffer(std::size_t offset, char const * buf,
|
||||
std::size_t toWrite);
|
||||
virtual void cleanUp();
|
||||
|
||||
// buffer for BLOB data
|
||||
std::vector<char> data_;
|
||||
|
||||
bool loaded_;
|
||||
long max_seg_size_;
|
||||
};
|
||||
|
||||
struct firebird_session_backend : details::session_backend
|
||||
{
|
||||
firebird_session_backend(connection_parameters const & parameters);
|
||||
|
||||
~firebird_session_backend();
|
||||
|
||||
virtual void begin();
|
||||
virtual void commit();
|
||||
virtual void rollback();
|
||||
|
||||
virtual bool get_next_sequence_value(session & s,
|
||||
std::string const & sequence, long & value);
|
||||
|
||||
virtual std::string get_backend_name() const { return "firebird"; }
|
||||
|
||||
void cleanUp();
|
||||
|
||||
virtual firebird_statement_backend * make_statement_backend();
|
||||
virtual firebird_rowid_backend * make_rowid_backend();
|
||||
virtual firebird_blob_backend * make_blob_backend();
|
||||
|
||||
virtual void setDPBOption(int const option, std::string const & value);
|
||||
|
||||
bool get_option_decimals_as_strings() { return decimals_as_strings_; }
|
||||
|
||||
isc_db_handle dbhp_;
|
||||
isc_tr_handle trhp_;
|
||||
std::string dpb_;
|
||||
bool decimals_as_strings_;
|
||||
};
|
||||
|
||||
struct firebird_backend_factory : backend_factory
|
||||
{
|
||||
firebird_backend_factory() {}
|
||||
virtual firebird_session_backend * make_session(
|
||||
connection_parameters const & parameters) const;
|
||||
};
|
||||
|
||||
extern SOCI_FIREBIRD_DECL firebird_backend_factory const firebird;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_FIREBIRD_DECL backend_factory const * factory_firebird();
|
||||
SOCI_FIREBIRD_DECL void register_factory_firebird();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_FIREBIRD_H_INCLUDED
|
||||
147
src/backends/firebird/standard-into-type.cpp
Normal file
147
src/backends/firebird/standard-into-type.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "common.h"
|
||||
#include <soci.h>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
void firebird_standard_into_type_backend::define_by_pos(
|
||||
int & position, void * data, exchange_type type)
|
||||
{
|
||||
position_ = position-1;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
++position;
|
||||
|
||||
statement_.intoType_ = eStandard;
|
||||
statement_.intos_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqldap_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
}
|
||||
|
||||
void firebird_standard_into_type_backend::pre_fetch()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void firebird_standard_into_type_backend::post_fetch(
|
||||
bool gotData, bool calledFromFetch, indicator * ind)
|
||||
{
|
||||
if (calledFromFetch && (gotData == false))
|
||||
{
|
||||
// this is a normal end-of-rowset condition,
|
||||
// no need to set anything (fetch() will return false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gotData)
|
||||
{
|
||||
if (i_null == statement_.inds_[position_][0] && NULL == ind)
|
||||
{
|
||||
throw soci_error("Null value fetched and no indicator defined.");
|
||||
}
|
||||
else if (NULL != ind)
|
||||
{
|
||||
*ind = statement_.inds_[position_][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void firebird_standard_into_type_backend::exchangeData()
|
||||
{
|
||||
XSQLVAR *var = statement_.sqldap_->sqlvar+position_;
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
*reinterpret_cast<char*>(data_) = getTextParam(var)[0];
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
short t = from_isc<short>(var);
|
||||
*reinterpret_cast<short*>(data_) = t;
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
int t = from_isc<int>(var);
|
||||
*reinterpret_cast<int *>(data_) = t;
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
long long t = from_isc<long long>(var);
|
||||
*reinterpret_cast<long long *>(data_) = t;
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
double t = from_isc<double>(var);
|
||||
*reinterpret_cast<double*>(data_) = t;
|
||||
}
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
case x_stdstring:
|
||||
*(reinterpret_cast<std::string*>(data_)) = getTextParam(var);
|
||||
break;
|
||||
case x_stdtm:
|
||||
tmDecode(var->sqltype,
|
||||
buf_, static_cast<std::tm*>(data_));
|
||||
|
||||
// isc_decode_timestamp() used by tmDecode() incorrectly sets
|
||||
// tm_isdst to 0 in the struct that it creates, see
|
||||
// http://tracker.firebirdsql.org/browse/CORE-3877, work around it
|
||||
// by pretending the DST is actually unknown.
|
||||
static_cast<std::tm*>(data_)->tm_isdst = -1;
|
||||
break;
|
||||
|
||||
// cases that require special handling
|
||||
case x_blob:
|
||||
{
|
||||
blob *tmp = reinterpret_cast<blob*>(data_);
|
||||
|
||||
firebird_blob_backend *blob =
|
||||
dynamic_cast<firebird_blob_backend*>(tmp->get_backend());
|
||||
|
||||
if (0 == blob)
|
||||
{
|
||||
throw soci_error("Can't get Firebid BLOB BackEnd");
|
||||
}
|
||||
|
||||
blob->assign(*reinterpret_cast<ISC_QUAD*>(buf_));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Into element used with non-supported type.");
|
||||
} // switch
|
||||
}
|
||||
|
||||
void firebird_standard_into_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
std::vector<void*>::iterator it =
|
||||
std::find(statement_.intos_.begin(), statement_.intos_.end(), this);
|
||||
if (it != statement_.intos_.end())
|
||||
statement_.intos_.erase(it);
|
||||
}
|
||||
182
src/backends/firebird/standard-use-type.cpp
Normal file
182
src/backends/firebird/standard-use-type.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "common.h"
|
||||
#include <soci.h>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
void firebird_standard_use_type_backend::bind_by_pos(
|
||||
int & position, void * data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.boundByName_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
position_ = position-1;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
++position;
|
||||
|
||||
statement_.useType_ = eStandard;
|
||||
statement_.uses_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
|
||||
statement_.boundByPos_ = true;
|
||||
}
|
||||
|
||||
void firebird_standard_use_type_backend::bind_by_name(
|
||||
std::string const & name, void * data,
|
||||
exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.boundByPos_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
std::map <std::string, int> :: iterator idx =
|
||||
statement_.names_.find(name);
|
||||
|
||||
if (idx == statement_.names_.end())
|
||||
{
|
||||
throw soci_error("Missing use element for bind by name (" + name + ")");
|
||||
}
|
||||
|
||||
position_ = idx->second;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
statement_.useType_ = eStandard;
|
||||
statement_.uses_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
|
||||
statement_.boundByName_ = true;
|
||||
}
|
||||
|
||||
void firebird_standard_use_type_backend::pre_use(indicator const * ind)
|
||||
{
|
||||
indISCHolder_ = 0;
|
||||
if (ind)
|
||||
{
|
||||
switch (*ind)
|
||||
{
|
||||
case i_null:
|
||||
indISCHolder_ = -1;
|
||||
break;
|
||||
case i_ok:
|
||||
indISCHolder_ = 0;
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Unsupported indicator value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_standard_use_type_backend::exchangeData()
|
||||
{
|
||||
XSQLVAR *var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
if (0 != indISCHolder_)
|
||||
return;
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
setTextParam(static_cast<char*>(data_), 1, buf_, var);
|
||||
break;
|
||||
case x_short:
|
||||
to_isc<short>(data_, var);
|
||||
break;
|
||||
case x_integer:
|
||||
to_isc<int>(data_, var);
|
||||
break;
|
||||
case x_long_long:
|
||||
to_isc<long long>(data_, var);
|
||||
break;
|
||||
case x_double:
|
||||
to_isc<double>(data_, var);
|
||||
break;
|
||||
|
||||
case x_stdstring:
|
||||
{
|
||||
std::string *tmp = static_cast<std::string*>(data_);
|
||||
setTextParam(tmp->c_str(), tmp->size(), buf_, var);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
tmEncode(var->sqltype,
|
||||
static_cast<std::tm*>(data_), buf_);
|
||||
break;
|
||||
|
||||
// cases that require special handling
|
||||
case x_blob:
|
||||
{
|
||||
blob *tmp = static_cast<blob*>(data_);
|
||||
|
||||
firebird_blob_backend* blob =
|
||||
dynamic_cast<firebird_blob_backend*>(tmp->get_backend());
|
||||
|
||||
if (NULL == blob)
|
||||
{
|
||||
throw soci_error("Can't get Firebid BLOB BackEnd");
|
||||
}
|
||||
|
||||
blob->save();
|
||||
memcpy(buf_, &blob->bid_, var->sqllen);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Use element used with non-supported type.");
|
||||
} // switch
|
||||
}
|
||||
|
||||
void firebird_standard_use_type_backend::post_use(
|
||||
bool /* gotData */, indicator * /* ind */)
|
||||
{
|
||||
// TODO: Is it possible to have the bound element being overwritten
|
||||
// by the database?
|
||||
// If not, then nothing to do here, please remove this comment.
|
||||
// If yes, then use the value of the readOnly parameter:
|
||||
// - true: the given object should not be modified and the backend
|
||||
// should detect if the modification was performed on the
|
||||
// isolated buffer and throw an exception if the buffer was modified
|
||||
// (this indicates logic error, because the user used const object
|
||||
// and executed a query that attempted to modified it)
|
||||
// - false: the modification should be propagated to the given object.
|
||||
// ...
|
||||
}
|
||||
|
||||
void firebird_standard_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
std::vector<void*>::iterator it =
|
||||
std::find(statement_.uses_.begin(), statement_.uses_.end(), this);
|
||||
if (it != statement_.uses_.end())
|
||||
statement_.uses_.erase(it);
|
||||
}
|
||||
724
src/backends/firebird/statement.cpp
Normal file
724
src/backends/firebird/statement.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "error-firebird.h"
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
firebird_statement_backend::firebird_statement_backend(firebird_session_backend &session)
|
||||
: session_(session), stmtp_(0), sqldap_(NULL), sqlda2p_(NULL),
|
||||
boundByName_(false), boundByPos_(false), rowsFetched_(0), endOfRowSet_(false), rowsAffectedBulk_(-1LL),
|
||||
intoType_(eStandard), useType_(eStandard), procedure_(false)
|
||||
{}
|
||||
|
||||
void firebird_statement_backend::prepareSQLDA(XSQLDA ** sqldap, int size)
|
||||
{
|
||||
if (*sqldap != NULL)
|
||||
{
|
||||
*sqldap = reinterpret_cast<XSQLDA*>(realloc(*sqldap, XSQLDA_LENGTH(size)));
|
||||
}
|
||||
else
|
||||
{
|
||||
*sqldap = reinterpret_cast<XSQLDA*>(malloc(XSQLDA_LENGTH(size)));
|
||||
}
|
||||
|
||||
(*sqldap)->sqln = size;
|
||||
(*sqldap)->version = 1;
|
||||
}
|
||||
|
||||
void firebird_statement_backend::alloc()
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &stmtp_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_statement_backend::clean_up()
|
||||
{
|
||||
rowsAffectedBulk_ = -1LL;
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
if (stmtp_ != NULL)
|
||||
{
|
||||
if (isc_dsql_free_statement(stat, &stmtp_, DSQL_drop))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
stmtp_ = NULL;
|
||||
}
|
||||
|
||||
if (sqldap_ != NULL)
|
||||
{
|
||||
free(sqldap_);
|
||||
sqldap_ = NULL;
|
||||
}
|
||||
|
||||
if (sqlda2p_ != NULL)
|
||||
{
|
||||
free(sqlda2p_);
|
||||
sqlda2p_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_statement_backend::rewriteParameters(
|
||||
std::string const & src, std::vector<char> & dst)
|
||||
{
|
||||
std::vector<char>::iterator dst_it = dst.begin();
|
||||
|
||||
// rewrite the query by transforming all named parameters into
|
||||
// the Firebird question marks (:abc -> ?, etc.)
|
||||
|
||||
enum { eNormal, eInQuotes, eInName } state = eNormal;
|
||||
|
||||
std::string name;
|
||||
int position = 0;
|
||||
|
||||
for (std::string::const_iterator it = src.begin(), end = src.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case eNormal:
|
||||
if (*it == '\'')
|
||||
{
|
||||
*dst_it++ = *it;
|
||||
state = eInQuotes;
|
||||
}
|
||||
else if (*it == ':')
|
||||
{
|
||||
state = eInName;
|
||||
}
|
||||
else // regular character, stay in the same state
|
||||
{
|
||||
*dst_it++ = *it;
|
||||
}
|
||||
break;
|
||||
case eInQuotes:
|
||||
if (*it == '\'')
|
||||
{
|
||||
*dst_it++ = *it;
|
||||
state = eNormal;
|
||||
}
|
||||
else // regular quoted character
|
||||
{
|
||||
*dst_it++ = *it;
|
||||
}
|
||||
break;
|
||||
case eInName:
|
||||
if (std::isalnum(*it) || *it == '_')
|
||||
{
|
||||
name += *it;
|
||||
}
|
||||
else // end of name
|
||||
{
|
||||
names_.insert(std::pair<std::string, int>(name, position++));
|
||||
name.clear();
|
||||
*dst_it++ = '?';
|
||||
*dst_it++ = *it;
|
||||
state = eNormal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == eInName)
|
||||
{
|
||||
names_.insert(std::pair<std::string, int>(name, position++));
|
||||
*dst_it++ = '?';
|
||||
}
|
||||
|
||||
*dst_it = '\0';
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
int statementType(isc_stmt_handle stmt)
|
||||
{
|
||||
int stype;
|
||||
int length;
|
||||
char type_item[] = {isc_info_sql_stmt_type};
|
||||
char res_buffer[8];
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
if (isc_dsql_sql_info(stat, &stmt, sizeof(type_item),
|
||||
type_item, sizeof(res_buffer), res_buffer))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
if (res_buffer[0] == isc_info_sql_stmt_type)
|
||||
{
|
||||
length = isc_vax_integer(res_buffer+1, 2);
|
||||
stype = isc_vax_integer(res_buffer+3, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error("Can't determine statement type.");
|
||||
}
|
||||
|
||||
return stype;
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_statement_backend::rewriteQuery(
|
||||
std::string const &query, std::vector<char> &buffer)
|
||||
{
|
||||
// buffer for temporary query
|
||||
std::vector<char> tmpQuery;
|
||||
std::vector<char>::iterator qItr;
|
||||
|
||||
// buffer for query with named parameters changed to standard ones
|
||||
std::vector<char> rewQuery(query.size() + 1);
|
||||
|
||||
// take care of named parameters in original query
|
||||
rewriteParameters(query, rewQuery);
|
||||
|
||||
std::string const prefix("execute procedure ");
|
||||
std::string const prefix2("select * from ");
|
||||
|
||||
// for procedures, we are preparing statement to determine
|
||||
// type of procedure.
|
||||
if (procedure_)
|
||||
{
|
||||
tmpQuery.resize(prefix.size() + rewQuery.size());
|
||||
qItr = tmpQuery.begin();
|
||||
std::copy(prefix.begin(), prefix.end(), qItr);
|
||||
qItr += prefix.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpQuery.resize(rewQuery.size());
|
||||
qItr = tmpQuery.begin();
|
||||
}
|
||||
|
||||
// prepare temporary query
|
||||
std::copy(rewQuery.begin(), rewQuery.end(), qItr);
|
||||
|
||||
// preparing buffers for output parameters
|
||||
if (sqldap_ == NULL)
|
||||
{
|
||||
prepareSQLDA(&sqldap_);
|
||||
}
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
isc_stmt_handle tmpStmtp = 0;
|
||||
|
||||
// allocate temporary statement to determine its type
|
||||
if (isc_dsql_allocate_statement(stat, &session_.dbhp_, &tmpStmtp))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
// prepare temporary statement
|
||||
if (isc_dsql_prepare(stat, &(session_.trhp_), &tmpStmtp, 0,
|
||||
&tmpQuery[0], SQL_DIALECT_V6, sqldap_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
// get statement type
|
||||
int stType = statementType(tmpStmtp);
|
||||
|
||||
// free temporary prepared statement
|
||||
if (isc_dsql_free_statement(stat, &tmpStmtp, DSQL_drop))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
// take care of special cases
|
||||
if (procedure_)
|
||||
{
|
||||
// for procedures that return values, we need to use correct syntax
|
||||
if (sqldap_->sqld != 0)
|
||||
{
|
||||
// this is "select" procedure, so we have to change syntax
|
||||
buffer.resize(prefix2.size() + rewQuery.size());
|
||||
qItr = buffer.begin();
|
||||
std::copy(prefix2.begin(), prefix2.end(), qItr);
|
||||
qItr += prefix2.size();
|
||||
std::copy(rewQuery.begin(), rewQuery.end(), qItr);
|
||||
|
||||
// that won't be needed anymore
|
||||
procedure_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is not procedure, so syntax is ok except for named
|
||||
// parameters in ddl
|
||||
if (stType == isc_info_sql_stmt_ddl)
|
||||
{
|
||||
// this statement is a DDL - we can't rewrite named parameters
|
||||
// so, we will use original query
|
||||
buffer.resize(query.size() + 1);
|
||||
std::copy(query.begin(), query.end(), buffer.begin());
|
||||
|
||||
// that won't be needed anymore
|
||||
procedure_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// here we know, that temporary query is OK, so we leave it as is
|
||||
buffer.resize(tmpQuery.size());
|
||||
std::copy(tmpQuery.begin(), tmpQuery.end(), buffer.begin());
|
||||
|
||||
// that won't be needed anymore
|
||||
procedure_ = false;
|
||||
}
|
||||
|
||||
void firebird_statement_backend::prepare(std::string const & query,
|
||||
statement_type /* eType */)
|
||||
{
|
||||
//std::cerr << "prepare: query=" << query << std::endl;
|
||||
// clear named parametes
|
||||
names_.clear();
|
||||
|
||||
std::vector<char> queryBuffer;
|
||||
|
||||
// modify query's syntax and prepare buffer for use with
|
||||
// firebird's api
|
||||
rewriteQuery(query, queryBuffer);
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
// prepare real statement
|
||||
if (isc_dsql_prepare(stat, &(session_.trhp_), &stmtp_, 0,
|
||||
&queryBuffer[0], SQL_DIALECT_V6, sqldap_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
if (sqldap_->sqln < sqldap_->sqld)
|
||||
{
|
||||
// sqlda is too small for all columns. it must be reallocated
|
||||
prepareSQLDA(&sqldap_, sqldap_->sqld);
|
||||
|
||||
if (isc_dsql_describe(stat, &stmtp_, SQL_DIALECT_V6, sqldap_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
// preparing input parameters
|
||||
if (sqlda2p_ == NULL)
|
||||
{
|
||||
prepareSQLDA(&sqlda2p_);
|
||||
}
|
||||
|
||||
if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
if (sqlda2p_->sqln < sqlda2p_->sqld)
|
||||
{
|
||||
// sqlda is too small for all columns. it must be reallocated
|
||||
prepareSQLDA(&sqlda2p_, sqlda2p_->sqld);
|
||||
|
||||
if (isc_dsql_describe_bind(stat, &stmtp_, SQL_DIALECT_V6, sqlda2p_))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare buffers for indicators
|
||||
inds_.clear();
|
||||
inds_.resize(sqldap_->sqld);
|
||||
|
||||
// reset types of into buffers
|
||||
intoType_ = eStandard;
|
||||
intos_.resize(0);
|
||||
|
||||
// reset types of use buffers
|
||||
useType_ = eStandard;
|
||||
uses_.resize(0);
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
void checkSize(std::size_t actual, std::size_t expected,
|
||||
std::string const & name)
|
||||
{
|
||||
if (actual != expected)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg << "Incorrect number of " << name << " variables. "
|
||||
<< "Expected " << expected << ", got " << actual;
|
||||
throw soci_error(msg.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
firebird_statement_backend::execute(int number)
|
||||
{
|
||||
ISC_STATUS stat[stat_size];
|
||||
XSQLDA *t = NULL;
|
||||
|
||||
std::size_t usize = uses_.size();
|
||||
|
||||
// do we have enough into variables ?
|
||||
checkSize(intos_.size(), sqldap_->sqld, "into");
|
||||
// do we have enough use variables ?
|
||||
checkSize(usize, sqlda2p_->sqld, "use");
|
||||
|
||||
// do we have parameters ?
|
||||
if (sqlda2p_->sqld)
|
||||
{
|
||||
t = sqlda2p_;
|
||||
|
||||
if (useType_ == eStandard)
|
||||
{
|
||||
for (std::size_t col=0; col<usize; ++col)
|
||||
{
|
||||
static_cast<firebird_standard_use_type_backend*>(uses_[col])->exchangeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure there is no active cursor
|
||||
if (isc_dsql_free_statement(stat, &stmtp_, DSQL_close))
|
||||
{
|
||||
// ignore attempt to close already closed cursor
|
||||
if (check_iscerror(stat, isc_dsql_cursor_close_err) == false)
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
if (useType_ == eVector)
|
||||
{
|
||||
long long rowsAffectedBulkTemp = 0;
|
||||
|
||||
// Here we have to explicitly loop to achieve the
|
||||
// effect of inserting or updating with vector use elements.
|
||||
std::size_t rows = static_cast<firebird_vector_use_type_backend*>(uses_[0])->size();
|
||||
for (std::size_t row=0; row < rows; ++row)
|
||||
{
|
||||
// first we have to prepare input parameters
|
||||
for (std::size_t col=0; col<usize; ++col)
|
||||
{
|
||||
static_cast<firebird_vector_use_type_backend*>(uses_[col])->exchangeData(row);
|
||||
}
|
||||
|
||||
// then execute query
|
||||
if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t))
|
||||
{
|
||||
// preserve the number of rows affected so far.
|
||||
rowsAffectedBulk_ = rowsAffectedBulkTemp;
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowsAffectedBulkTemp += get_affected_rows();
|
||||
}
|
||||
// soci does not allow bulk insert/update and bulk select operations
|
||||
// in same query. So here, we know that into elements are not
|
||||
// vectors. So, there is no need to fetch data here.
|
||||
}
|
||||
rowsAffectedBulk_ = rowsAffectedBulkTemp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use elements aren't vectors
|
||||
if (isc_dsql_execute(stat, &session_.trhp_, &stmtp_, SQL_DIALECT_V6, t))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully re-executing the statement must reset the "end of rowset"
|
||||
// flag, we might be able to fetch data again now.
|
||||
endOfRowSet_ = false;
|
||||
|
||||
if (sqldap_->sqld)
|
||||
{
|
||||
// query may return some data
|
||||
if (number > 0)
|
||||
{
|
||||
// number contains size of input variables, so we may fetch() data here
|
||||
return fetch(number);
|
||||
}
|
||||
else
|
||||
{
|
||||
// execute(0) was meant to only perform the query
|
||||
return ef_success;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// query can't return any data
|
||||
return ef_no_data;
|
||||
}
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
firebird_statement_backend::fetch(int number)
|
||||
{
|
||||
if (endOfRowSet_)
|
||||
return ef_no_data;
|
||||
|
||||
ISC_STATUS stat[stat_size];
|
||||
|
||||
for (size_t i = 0; i<static_cast<unsigned int>(sqldap_->sqld); ++i)
|
||||
{
|
||||
inds_[i].resize(number > 0 ? number : 1);
|
||||
}
|
||||
|
||||
// Here we have to explicitly loop to achieve the effect of fetching
|
||||
// vector into elements. After each fetch, we have to exchange data
|
||||
// with into buffers.
|
||||
rowsFetched_ = 0;
|
||||
for (int i = 0; i < number; ++i)
|
||||
{
|
||||
long fetch_stat = isc_dsql_fetch(stat, &stmtp_, SQL_DIALECT_V6, sqldap_);
|
||||
|
||||
// there is more data to read
|
||||
if (fetch_stat == 0)
|
||||
{
|
||||
++rowsFetched_;
|
||||
exchangeData(true, i);
|
||||
}
|
||||
else if (fetch_stat == 100L)
|
||||
{
|
||||
endOfRowSet_ = true;
|
||||
return ef_no_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error
|
||||
endOfRowSet_ = true;
|
||||
throw_iscerror(stat);
|
||||
return ef_no_data; // unreachable, for compiler only
|
||||
}
|
||||
} // for
|
||||
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
// here we put data fetched from database into user buffers
|
||||
void firebird_statement_backend::exchangeData(bool gotData, int row)
|
||||
{
|
||||
if (gotData)
|
||||
{
|
||||
for (size_t i = 0; i < static_cast<unsigned int>(sqldap_->sqld); ++i)
|
||||
{
|
||||
// first save indicators
|
||||
if (((sqldap_->sqlvar+i)->sqltype & 1) == 0)
|
||||
{
|
||||
// there is no indicator for this column
|
||||
inds_[i][row] = i_ok;
|
||||
}
|
||||
else if (*((sqldap_->sqlvar+i)->sqlind) == 0)
|
||||
{
|
||||
inds_[i][row] = i_ok;
|
||||
}
|
||||
else if (*((sqldap_->sqlvar+i)->sqlind) == -1)
|
||||
{
|
||||
inds_[i][row] = i_null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error("Unknown state in firebird_statement_backend::exchangeData()");
|
||||
}
|
||||
|
||||
// then deal with data
|
||||
if (inds_[i][row] != i_null)
|
||||
{
|
||||
if (intoType_ == eVector)
|
||||
{
|
||||
static_cast<firebird_vector_into_type_backend*>(
|
||||
intos_[i])->exchangeData(row);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<firebird_standard_into_type_backend*>(
|
||||
intos_[i])->exchangeData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long long firebird_statement_backend::get_affected_rows()
|
||||
{
|
||||
if (rowsAffectedBulk_ >= 0)
|
||||
{
|
||||
return rowsAffectedBulk_;
|
||||
}
|
||||
|
||||
ISC_STATUS_ARRAY stat;
|
||||
char type_item[] = { isc_info_sql_records };
|
||||
char res_buffer[256];
|
||||
|
||||
if (isc_dsql_sql_info(stat, &stmtp_, sizeof(type_item), type_item,
|
||||
sizeof(res_buffer), res_buffer))
|
||||
{
|
||||
throw_iscerror(stat);
|
||||
}
|
||||
|
||||
// We must get back a isc_info_sql_records block, that we parse below,
|
||||
// followed by isc_info_end.
|
||||
if (res_buffer[0] != isc_info_sql_records)
|
||||
{
|
||||
throw soci_error("Can't determine the number of affected rows");
|
||||
}
|
||||
|
||||
char* sql_rec_buf = res_buffer + 1;
|
||||
const int length = isc_vax_integer(sql_rec_buf, 2);
|
||||
sql_rec_buf += 2;
|
||||
|
||||
if (sql_rec_buf[length] != isc_info_end)
|
||||
{
|
||||
throw soci_error("Unexpected isc_info_sql_records return format");
|
||||
}
|
||||
|
||||
// Examine the 4 sub-blocks each of which has a header indicating the block
|
||||
// type, its value length in bytes and the value itself.
|
||||
long long row_count = 0;
|
||||
|
||||
for ( char* p = sql_rec_buf; !row_count && p < sql_rec_buf + length; )
|
||||
{
|
||||
switch (*p++)
|
||||
{
|
||||
case isc_info_req_select_count:
|
||||
case isc_info_req_insert_count:
|
||||
case isc_info_req_update_count:
|
||||
case isc_info_req_delete_count:
|
||||
{
|
||||
int len = isc_vax_integer(p, 2);
|
||||
p += 2;
|
||||
|
||||
row_count += isc_vax_integer(p, len);
|
||||
p += len;
|
||||
}
|
||||
break;
|
||||
|
||||
case isc_info_end:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Unknown record counter");
|
||||
}
|
||||
}
|
||||
|
||||
return row_count;
|
||||
}
|
||||
|
||||
int firebird_statement_backend::get_number_of_rows()
|
||||
{
|
||||
return rowsFetched_;
|
||||
}
|
||||
|
||||
std::string firebird_statement_backend::rewrite_for_procedure_call(
|
||||
std::string const &query)
|
||||
{
|
||||
procedure_ = true;
|
||||
return query;
|
||||
}
|
||||
|
||||
int firebird_statement_backend::prepare_for_describe()
|
||||
{
|
||||
return static_cast<int>(sqldap_->sqld);
|
||||
}
|
||||
|
||||
void firebird_statement_backend::describe_column(int colNum,
|
||||
data_type & type, std::string & columnName)
|
||||
{
|
||||
XSQLVAR * var = sqldap_->sqlvar+(colNum-1);
|
||||
|
||||
columnName.assign(var->aliasname, var->aliasname_length);
|
||||
|
||||
switch (var->sqltype & ~1)
|
||||
{
|
||||
case SQL_TEXT:
|
||||
case SQL_VARYING:
|
||||
type = dt_string;
|
||||
break;
|
||||
case SQL_TYPE_DATE:
|
||||
case SQL_TYPE_TIME:
|
||||
case SQL_TIMESTAMP:
|
||||
type = dt_date;
|
||||
break;
|
||||
case SQL_FLOAT:
|
||||
case SQL_DOUBLE:
|
||||
type = dt_double;
|
||||
break;
|
||||
case SQL_SHORT:
|
||||
case SQL_LONG:
|
||||
if (var->sqlscale < 0)
|
||||
{
|
||||
if (session_.get_option_decimals_as_strings())
|
||||
type = dt_string;
|
||||
else
|
||||
type = dt_double;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = dt_integer;
|
||||
}
|
||||
break;
|
||||
case SQL_INT64:
|
||||
if (var->sqlscale < 0)
|
||||
{
|
||||
if (session_.get_option_decimals_as_strings())
|
||||
type = dt_string;
|
||||
else
|
||||
type = dt_double;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = dt_long_long;
|
||||
}
|
||||
break;
|
||||
/* case SQL_BLOB:
|
||||
case SQL_ARRAY:*/
|
||||
default:
|
||||
std::ostringstream msg;
|
||||
msg << "Type of column ["<< colNum << "] \"" << columnName
|
||||
<< "\" is not supported for dynamic queries";
|
||||
throw soci_error(msg.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
firebird_standard_into_type_backend * firebird_statement_backend::make_into_type_backend()
|
||||
{
|
||||
return new firebird_standard_into_type_backend(*this);
|
||||
}
|
||||
|
||||
firebird_standard_use_type_backend * firebird_statement_backend::make_use_type_backend()
|
||||
{
|
||||
return new firebird_standard_use_type_backend(*this);
|
||||
}
|
||||
|
||||
firebird_vector_into_type_backend * firebird_statement_backend::make_vector_into_type_backend()
|
||||
{
|
||||
return new firebird_vector_into_type_backend(*this);
|
||||
}
|
||||
|
||||
firebird_vector_use_type_backend * firebird_statement_backend::make_vector_use_type_backend()
|
||||
{
|
||||
return new firebird_vector_use_type_backend(*this);
|
||||
}
|
||||
15
src/backends/firebird/test/CMakeLists.txt
Normal file
15
src/backends/firebird/test/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2013 Viacheslav Naydenov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
soci_backend_test(
|
||||
BACKEND Firebird
|
||||
SOURCE test-firebird.cpp
|
||||
CONNSTR "dummy")
|
||||
22
src/backends/firebird/test/Makefile.basic
Normal file
22
src/backends/firebird/test/Makefile.basic
Normal file
@@ -0,0 +1,22 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
FIREBIRDINCLUDEDIR = -I/usr/local/firebird/include
|
||||
FIREBIRDLIBDIR = -L/usr/local/firebird/lib
|
||||
FIREBIRDLIBS = -lfbclient -lpthread
|
||||
|
||||
# The rest of the Makefile is indepentent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
INCLUDEDIRS = -I.. -I../../../core ${FIREBIRDINCLUDEDIR}
|
||||
LIBDIRS = -L.. -L../../../core ${FIREBIRDLIBDIR}
|
||||
LIBS = -lsoci_core -lsoci_firebird -ldl ${FIREBIRDLIBS}
|
||||
|
||||
|
||||
test-firebird : test-firebird.cpp
|
||||
${COMPILER} -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} ${LIBDIRS} ${LIBS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f *.o test-firebird
|
||||
1361
src/backends/firebird/test/test-firebird.cpp
Normal file
1361
src/backends/firebird/test/test-firebird.cpp
Normal file
File diff suppressed because it is too large
Load Diff
208
src/backends/firebird/vector-into-type.cpp
Normal file
208
src/backends/firebird/vector-into-type.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "common.h"
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
void firebird_vector_into_type_backend::define_by_pos(
|
||||
int & position, void * data, exchange_type type)
|
||||
{
|
||||
position_ = position-1;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
++position;
|
||||
|
||||
statement_.intoType_ = eVector;
|
||||
statement_.intos_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqldap_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
}
|
||||
|
||||
void firebird_vector_into_type_backend::pre_fetch()
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
namespace // anonymous
|
||||
{
|
||||
template <typename T>
|
||||
void setIntoVector(void *p, std::size_t indx, T const &val)
|
||||
{
|
||||
std::vector<T> *dest =
|
||||
static_cast<std::vector<T> *>(p);
|
||||
|
||||
std::vector<T> &v = *dest;
|
||||
v[indx] = val;
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
// this will exchange data with vector user buffers
|
||||
void firebird_vector_into_type_backend::exchangeData(std::size_t row)
|
||||
{
|
||||
XSQLVAR *var = statement_.sqldap_->sqlvar+position_;
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
setIntoVector(data_, row, getTextParam(var)[0]);
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
short tmp = from_isc<short>(var);
|
||||
setIntoVector(data_, row, tmp);
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
int tmp = from_isc<int>(var);
|
||||
setIntoVector(data_, row, tmp);
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
long long tmp = from_isc<long long>(var);
|
||||
setIntoVector(data_, row, tmp);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
double tmp = from_isc<double>(var);
|
||||
setIntoVector(data_, row, tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
case x_stdstring:
|
||||
setIntoVector(data_, row, getTextParam(var));
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::tm data;
|
||||
tmDecode(var->sqltype, buf_, &data);
|
||||
setIntoVector(data_, row, data);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into vector element used with non-supported type.");
|
||||
} // switch
|
||||
|
||||
}
|
||||
|
||||
void firebird_vector_into_type_backend::post_fetch(
|
||||
bool gotData, indicator * ind)
|
||||
{
|
||||
// Here we have to set indicators only. Data was exchanged with user
|
||||
// buffers during fetch()
|
||||
if (gotData)
|
||||
{
|
||||
std::size_t rows = statement_.rowsFetched_;
|
||||
|
||||
for (std::size_t i = 0; i<rows; ++i)
|
||||
{
|
||||
if (statement_.inds_[position_][i] == i_null && (ind == NULL))
|
||||
{
|
||||
throw soci_error("Null value fetched and no indicator defined.");
|
||||
}
|
||||
else if (ind != NULL)
|
||||
{
|
||||
ind[i] = statement_.inds_[position_][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_vector_into_type_backend::resize(std::size_t sz)
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
resizeVector<char> (data_, sz);
|
||||
break;
|
||||
case x_short:
|
||||
resizeVector<short> (data_, sz);
|
||||
break;
|
||||
case x_integer:
|
||||
resizeVector<int> (data_, sz);
|
||||
break;
|
||||
case x_long_long:
|
||||
resizeVector<long long> (data_, sz);
|
||||
break;
|
||||
case x_double:
|
||||
resizeVector<double> (data_, sz);
|
||||
break;
|
||||
case x_stdstring:
|
||||
resizeVector<std::string> (data_, sz);
|
||||
break;
|
||||
case x_stdtm:
|
||||
resizeVector<std::tm> (data_, sz);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into vector element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t firebird_vector_into_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
sz = getVectorSize<char> (data_);
|
||||
break;
|
||||
case x_short:
|
||||
sz = getVectorSize<short> (data_);
|
||||
break;
|
||||
case x_integer:
|
||||
sz = getVectorSize<int> (data_);
|
||||
break;
|
||||
case x_long_long:
|
||||
sz = getVectorSize<long long> (data_);
|
||||
break;
|
||||
case x_double:
|
||||
sz = getVectorSize<double> (data_);
|
||||
break;
|
||||
case x_stdstring:
|
||||
sz = getVectorSize<std::string> (data_);
|
||||
break;
|
||||
case x_stdtm:
|
||||
sz = getVectorSize<std::tm> (data_);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into vector element used with non-supported type.");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void firebird_vector_into_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
std::vector<void*>::iterator it =
|
||||
std::find(statement_.intos_.begin(), statement_.intos_.end(), this);
|
||||
if (it != statement_.intos_.end())
|
||||
statement_.intos_.erase(it);
|
||||
}
|
||||
207
src/backends/firebird/vector-use-type.cpp
Normal file
207
src/backends/firebird/vector-use-type.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, Rafal Bobrowski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_FIREBIRD_SOURCE
|
||||
#include "soci-firebird.h"
|
||||
#include "common.h"
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::firebird;
|
||||
|
||||
void firebird_vector_use_type_backend::bind_by_pos(int & position,
|
||||
void * data, exchange_type type)
|
||||
{
|
||||
if (statement_.boundByName_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
position_ = position-1;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
++position;
|
||||
|
||||
statement_.useType_ = eVector;
|
||||
statement_.uses_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
|
||||
statement_.boundByPos_ = true;
|
||||
}
|
||||
|
||||
void firebird_vector_use_type_backend::bind_by_name(
|
||||
std::string const & name, void * data, exchange_type type)
|
||||
{
|
||||
if (statement_.boundByPos_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
std::map <std::string, int> :: iterator idx =
|
||||
statement_.names_.find(name);
|
||||
|
||||
if (idx == statement_.names_.end())
|
||||
{
|
||||
throw soci_error("Missing use element for bind by name (" + name + ")");
|
||||
}
|
||||
|
||||
position_ = idx->second;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
statement_.useType_ = eVector;
|
||||
statement_.uses_.push_back(static_cast<void*>(this));
|
||||
|
||||
XSQLVAR *var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
buf_ = allocBuffer(var);
|
||||
var->sqldata = buf_;
|
||||
var->sqlind = &indISCHolder_;
|
||||
|
||||
statement_.boundByName_ = true;
|
||||
}
|
||||
|
||||
void firebird_vector_use_type_backend::pre_use(indicator const * ind)
|
||||
{
|
||||
inds_ = ind;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
T* getUseVectorValue(void *v, std::size_t index)
|
||||
{
|
||||
std::vector<T> *src =
|
||||
static_cast<std::vector<T> *>(v);
|
||||
|
||||
std::vector<T> &v_ = *src;
|
||||
return &(v_[index]);
|
||||
}
|
||||
}
|
||||
|
||||
void firebird_vector_use_type_backend::exchangeData(std::size_t row)
|
||||
{
|
||||
// first prepare indicators
|
||||
if (inds_ != NULL)
|
||||
{
|
||||
switch (inds_[row])
|
||||
{
|
||||
case i_null:
|
||||
indISCHolder_ = -1;
|
||||
break;
|
||||
case i_ok:
|
||||
indISCHolder_ = 0;
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Use element used with non-supported indicator type.");
|
||||
}
|
||||
}
|
||||
|
||||
XSQLVAR * var = statement_.sqlda2p_->sqlvar+position_;
|
||||
|
||||
// then set parameters for query execution
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
setTextParam(getUseVectorValue<char>(data_, row), 1, buf_, var);
|
||||
break;
|
||||
case x_short:
|
||||
to_isc<short>(
|
||||
static_cast<void*>(getUseVectorValue<short>(data_, row)),
|
||||
var);
|
||||
break;
|
||||
case x_integer:
|
||||
to_isc<int>(
|
||||
static_cast<void*>(getUseVectorValue<int>(data_, row)),
|
||||
var);
|
||||
break;
|
||||
case x_long_long:
|
||||
to_isc<long long>(
|
||||
static_cast<void*>(getUseVectorValue<long long>(data_, row)),
|
||||
var);
|
||||
break;
|
||||
case x_double:
|
||||
to_isc<double>(
|
||||
static_cast<void*>(getUseVectorValue<double>(data_, row)),
|
||||
var);
|
||||
break;
|
||||
|
||||
// cases that require adjustments and buffer management
|
||||
case x_stdstring:
|
||||
{
|
||||
std::string *tmp = getUseVectorValue<std::string>(data_, row);
|
||||
setTextParam(tmp->c_str(), tmp->size(), buf_, var);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
tmEncode(var->sqltype,
|
||||
getUseVectorValue<std::tm>(data_, row), buf_);
|
||||
break;
|
||||
// Not supported
|
||||
// case x_cstring:
|
||||
// case x_blob:
|
||||
default:
|
||||
throw soci_error("Use element used with non-supported type.");
|
||||
} // switch
|
||||
}
|
||||
|
||||
std::size_t firebird_vector_use_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char:
|
||||
sz = getVectorSize<char> (data_);
|
||||
break;
|
||||
case x_short:
|
||||
sz = getVectorSize<short> (data_);
|
||||
break;
|
||||
case x_integer:
|
||||
sz = getVectorSize<int> (data_);
|
||||
break;
|
||||
case x_long_long:
|
||||
sz = getVectorSize<long long> (data_);
|
||||
break;
|
||||
case x_double:
|
||||
sz = getVectorSize<double> (data_);
|
||||
break;
|
||||
case x_stdstring:
|
||||
sz = getVectorSize<std::string> (data_);
|
||||
break;
|
||||
case x_stdtm:
|
||||
sz = getVectorSize<std::tm> (data_);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Use vector element used with non-supported type.");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void firebird_vector_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
std::vector<void*>::iterator it =
|
||||
std::find(statement_.uses_.begin(), statement_.uses_.end(), this);
|
||||
if (it != statement_.uses_.end())
|
||||
statement_.uses_.erase(it);
|
||||
}
|
||||
18
src/backends/mysql/CMakeLists.txt
Normal file
18
src/backends/mysql/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend(MySQL
|
||||
DEPENDS MySQL
|
||||
HEADERS soci-mysql.h common.h
|
||||
DESCRIPTION "SOCI backend for MySQL database engine"
|
||||
AUTHORS "Pawel Aleksander Fedorynski"
|
||||
MAINTAINERS "Pawel Aleksander Fedorynski")
|
||||
|
||||
add_subdirectory(test)
|
||||
97
src/backends/mysql/Makefile.basic
Normal file
97
src/backends/mysql/Makefile.basic
Normal file
@@ -0,0 +1,97 @@
|
||||
# The following variables are specific to this backend and their correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
MYSQLLIBDIR = -L/usr/lib/mysql
|
||||
MYSQLLIBS = -lmysqlclient -lz
|
||||
MYSQLINCLUDEDIR = -I/usr/include/mysql
|
||||
|
||||
# The rest of the Makefile is independent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
CXXFLAGSSO = ${CXXFLAGS} -fPIC
|
||||
INCLUDEDIRS = -I../../core ${MYSQLINCLUDEDIR}
|
||||
|
||||
|
||||
OBJECTS = blob.o factory.o row-id.o session.o standard-into-type.o \
|
||||
standard-use-type.o statement.o vector-into-type.o vector-use-type.o \
|
||||
common.o
|
||||
|
||||
|
||||
OBJECTSSO = blob-s.o factory-s.o row-id-s.o session-s.o \
|
||||
standard-into-type-s.o standard-use-type-s.o statement-s.o \
|
||||
vector-into-type-s.o vector-use-type-s.o common-s.o
|
||||
|
||||
|
||||
libsoci_mysql.a : ${OBJECTS}
|
||||
ar rv $@ $?
|
||||
rm *.o
|
||||
|
||||
|
||||
blob.o : blob.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
common.o : common.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
factory.o : factory.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
row-id.o : row-id.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
session.o : session.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type.o : standard-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type.o : standard-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
statement.o : statement.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type.o : vector-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type.o : vector-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
shared : ${OBJECTSSO}
|
||||
${COMPILER} -shared -o libsoci_mysql.so ${OBJECTSSO}
|
||||
rm *.o
|
||||
|
||||
blob-s.o : blob.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
common-s.o : common.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
factory-s.o : factory.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
row-id-s.o : row-id.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
session-s.o : session.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type-s.o : standard-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type-s.o : standard-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
statement-s.o : statement.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type-s.o : vector-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type-s.o : vector-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f libsoci_mysql.a libsoci_mysql.so
|
||||
62
src/backends/mysql/blob.cpp
Normal file
62
src/backends/mysql/blob.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include <ciso646>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4355 4702)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
mysql_blob_backend::mysql_blob_backend(mysql_session_backend &session)
|
||||
: session_(session)
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
mysql_blob_backend::~mysql_blob_backend()
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t mysql_blob_backend::get_len()
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
std::size_t mysql_blob_backend::read(
|
||||
std::size_t /* offset */, char * /* buf */, std::size_t /* toRead */)
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
std::size_t mysql_blob_backend::write(
|
||||
std::size_t /* offset */, char const * /* buf */,
|
||||
std::size_t /* toWrite */)
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
std::size_t mysql_blob_backend::append(
|
||||
char const * /* buf */, std::size_t /* toWrite */)
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
void mysql_blob_backend::trim(std::size_t /* newLen */)
|
||||
{
|
||||
throw soci_error("BLOBs are not supported.");
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
87
src/backends/mysql/common.cpp
Normal file
87
src/backends/mysql/common.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "common.h"
|
||||
#include "soci-backend.h"
|
||||
#include <ciso646>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
namespace // anonymous
|
||||
{
|
||||
|
||||
// helper function for parsing decimal data (for std::tm)
|
||||
long parse10(char const *&p1, char *&p2, const char *msg)
|
||||
{
|
||||
long v = std::strtol(p1, &p2, 10);
|
||||
if (p2 != p1)
|
||||
{
|
||||
p1 = p2 + 1;
|
||||
return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci::soci_error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
|
||||
void soci::details::mysql::parse_std_tm(char const *buf, std::tm &t)
|
||||
{
|
||||
char const *p1 = buf;
|
||||
char *p2;
|
||||
long year, month, day;
|
||||
long hour = 0, minute = 0, second = 0;
|
||||
|
||||
const char *errMsg = "Cannot convert data to std::tm.";
|
||||
|
||||
if (strchr(buf, '-') != NULL)
|
||||
{
|
||||
year = parse10(p1, p2, errMsg);
|
||||
month = parse10(p1, p2, errMsg);
|
||||
day = parse10(p1, p2, errMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
year = 2000;
|
||||
month = 1;
|
||||
day = 1;
|
||||
}
|
||||
|
||||
|
||||
if (strchr(buf, ':') != NULL)
|
||||
{
|
||||
// there is also the time of day available
|
||||
hour = parse10(p1, p2, errMsg);
|
||||
minute = parse10(p1, p2, errMsg);
|
||||
second = parse10(p1, p2, errMsg);
|
||||
}
|
||||
|
||||
t.tm_isdst = -1;
|
||||
t.tm_year = year - 1900;
|
||||
t.tm_mon = month - 1;
|
||||
t.tm_mday = day;
|
||||
t.tm_hour = hour;
|
||||
t.tm_min = minute;
|
||||
t.tm_sec = second;
|
||||
|
||||
std::mktime(&t);
|
||||
}
|
||||
|
||||
char * soci::details::mysql::quote(MYSQL * conn, const char *s, int len)
|
||||
{
|
||||
char *retv = new char[2 * len + 3];
|
||||
retv[0] = '\'';
|
||||
int len_esc = mysql_real_escape_string(conn, retv + 1, s, len);
|
||||
retv[len_esc + 1] = '\'';
|
||||
retv[len_esc + 2] = '\0';
|
||||
|
||||
return retv;
|
||||
}
|
||||
76
src/backends/mysql/common.h
Normal file
76
src/backends/mysql/common.h
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_MYSQL_COMMON_H_INCLUDED
|
||||
#define SOCI_MYSQL_COMMON_H_INCLUDED
|
||||
|
||||
#include "soci-mysql.h"
|
||||
// std
|
||||
#include <cstddef>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
namespace mysql
|
||||
{
|
||||
|
||||
// helper function for parsing datetime values
|
||||
void parse_std_tm(char const *buf, std::tm &t);
|
||||
|
||||
// The idea is that infinity - infinity gives NaN, and NaN != NaN is true.
|
||||
//
|
||||
// This should work on any IEEE-754-compliant implementation, which is
|
||||
// another way of saying that it does not always work (in particular,
|
||||
// according to stackoverflow, it won't work with gcc with the --fast-math
|
||||
// option), but I know of no better way of testing this portably in C++ prior
|
||||
// to C++11. When soci moves to C++11 this should be replaced
|
||||
// with std::isfinite().
|
||||
template <typename T>
|
||||
bool is_infinity_or_nan(T x)
|
||||
{
|
||||
T y = x - x;
|
||||
return (y != y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void parse_num(char const *buf, T &x)
|
||||
{
|
||||
std::istringstream iss(buf);
|
||||
iss >> x;
|
||||
if (iss.fail() || (iss.eof() == false))
|
||||
{
|
||||
throw soci_error("Cannot convert data.");
|
||||
}
|
||||
if (is_infinity_or_nan(x)) {
|
||||
throw soci_error("Cannot convert data.");
|
||||
}
|
||||
}
|
||||
|
||||
// helper for escaping strings
|
||||
char * quote(MYSQL * conn, const char *s, int len);
|
||||
|
||||
// helper for vector operations
|
||||
template <typename T>
|
||||
std::size_t get_vector_size(void *p)
|
||||
{
|
||||
std::vector<T> *v = static_cast<std::vector<T> *>(p);
|
||||
return v->size();
|
||||
}
|
||||
|
||||
} // namespace mysql
|
||||
|
||||
} // namespace details
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_MYSQL_COMMON_H_INCLUDED
|
||||
45
src/backends/mysql/factory.cpp
Normal file
45
src/backends/mysql/factory.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include <backend-loader.h>
|
||||
#include <ciso646>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
// concrete factory for MySQL concrete strategies
|
||||
mysql_session_backend * mysql_backend_factory::make_session(
|
||||
connection_parameters const & parameters) const
|
||||
{
|
||||
return new mysql_session_backend(parameters);
|
||||
}
|
||||
|
||||
mysql_backend_factory const soci::mysql;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_MYSQL_DECL backend_factory const * factory_mysql()
|
||||
{
|
||||
return &soci::mysql;
|
||||
}
|
||||
|
||||
SOCI_MYSQL_DECL void register_factory_mysql()
|
||||
{
|
||||
soci::dynamic_backends::register_backend("mysql", soci::mysql);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
32
src/backends/mysql/row-id.cpp
Normal file
32
src/backends/mysql/row-id.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4355 4702)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
mysql_rowid_backend::mysql_rowid_backend(
|
||||
mysql_session_backend & /* session */)
|
||||
{
|
||||
throw soci_error("RowIDs are not supported.");
|
||||
}
|
||||
|
||||
mysql_rowid_backend::~mysql_rowid_backend()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
389
src/backends/mysql/session.cpp
Normal file
389
src/backends/mysql/session.cpp
Normal file
@@ -0,0 +1,389 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include <connection-parameters.h>
|
||||
// std
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <ciso646>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using std::string;
|
||||
|
||||
|
||||
namespace
|
||||
{ // anonymous
|
||||
|
||||
void skip_white(std::string::const_iterator *i,
|
||||
std::string::const_iterator const & end, bool endok)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (*i == end)
|
||||
{
|
||||
if (endok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error("Unexpected end of connection string.");
|
||||
}
|
||||
}
|
||||
if (std::isspace(**i))
|
||||
{
|
||||
++*i;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string param_name(std::string::const_iterator *i,
|
||||
std::string::const_iterator const & end)
|
||||
{
|
||||
std::string val("");
|
||||
for (;;)
|
||||
{
|
||||
if (*i == end or (not std::isalpha(**i) and **i != '_'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
val += **i;
|
||||
++*i;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
string param_value(string::const_iterator *i,
|
||||
string::const_iterator const & end)
|
||||
{
|
||||
string err = "Malformed connection string.";
|
||||
bool quot;
|
||||
if (**i == '\'')
|
||||
{
|
||||
quot = true;
|
||||
++*i;
|
||||
}
|
||||
else
|
||||
{
|
||||
quot = false;
|
||||
}
|
||||
string val("");
|
||||
for (;;)
|
||||
{
|
||||
if (*i == end)
|
||||
{
|
||||
if (quot)
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (**i == '\'')
|
||||
{
|
||||
if (quot)
|
||||
{
|
||||
++*i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
}
|
||||
if (not quot and std::isspace(**i))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (**i == '\\')
|
||||
{
|
||||
++*i;
|
||||
if (*i == end)
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
}
|
||||
val += **i;
|
||||
++*i;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
bool valid_int(const string & s)
|
||||
{
|
||||
char *tail;
|
||||
const char *cstr = s.c_str();
|
||||
errno = 0;
|
||||
long n = std::strtol(cstr, &tail, 10);
|
||||
if (errno != 0 or n > INT_MAX or n < INT_MIN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (*tail != '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_connect_string(const string & connectString,
|
||||
string *host, bool *host_p,
|
||||
string *user, bool *user_p,
|
||||
string *password, bool *password_p,
|
||||
string *db, bool *db_p,
|
||||
string *unix_socket, bool *unix_socket_p,
|
||||
int *port, bool *port_p, string *ssl_ca, bool *ssl_ca_p,
|
||||
string *ssl_cert, bool *ssl_cert_p, string *ssl_key, bool *ssl_key_p,
|
||||
int *local_infile, bool *local_infile_p,
|
||||
string *charset, bool *charset_p)
|
||||
{
|
||||
*host_p = false;
|
||||
*user_p = false;
|
||||
*password_p = false;
|
||||
*db_p = false;
|
||||
*unix_socket_p = false;
|
||||
*port_p = false;
|
||||
*ssl_ca_p = false;
|
||||
*ssl_cert_p = false;
|
||||
*ssl_key_p = false;
|
||||
*local_infile_p = false;
|
||||
*charset_p = false;
|
||||
string err = "Malformed connection string.";
|
||||
string::const_iterator i = connectString.begin(),
|
||||
end = connectString.end();
|
||||
while (i != end)
|
||||
{
|
||||
skip_white(&i, end, true);
|
||||
if (i == end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string par = param_name(&i, end);
|
||||
skip_white(&i, end, false);
|
||||
if (*i == '=')
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
skip_white(&i, end, false);
|
||||
string val = param_value(&i, end);
|
||||
if (par == "port" and not *port_p)
|
||||
{
|
||||
if (not valid_int(val))
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
*port = std::atoi(val.c_str());
|
||||
if (port < 0)
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
*port_p = true;
|
||||
}
|
||||
else if (par == "host" and not *host_p)
|
||||
{
|
||||
*host = val;
|
||||
*host_p = true;
|
||||
}
|
||||
else if (par == "user" and not *user_p)
|
||||
{
|
||||
*user = val;
|
||||
*user_p = true;
|
||||
}
|
||||
else if ((par == "pass" or par == "password") and not *password_p)
|
||||
{
|
||||
*password = val;
|
||||
*password_p = true;
|
||||
}
|
||||
else if ((par == "db" or par == "dbname" or par == "service") and
|
||||
not *db_p)
|
||||
{
|
||||
*db = val;
|
||||
*db_p = true;
|
||||
}
|
||||
else if (par == "unix_socket" and not *unix_socket_p)
|
||||
{
|
||||
*unix_socket = val;
|
||||
*unix_socket_p = true;
|
||||
}
|
||||
else if (par == "sslca" and not *ssl_ca_p)
|
||||
{
|
||||
*ssl_ca = val;
|
||||
*ssl_ca_p = true;
|
||||
}
|
||||
else if (par == "sslcert" and not *ssl_cert_p)
|
||||
{
|
||||
*ssl_cert = val;
|
||||
*ssl_cert_p = true;
|
||||
}
|
||||
else if (par == "sslkey" and not *ssl_key_p)
|
||||
{
|
||||
*ssl_key = val;
|
||||
*ssl_key_p = true;
|
||||
}
|
||||
else if (par == "local_infile" and not *local_infile_p)
|
||||
{
|
||||
if (not valid_int(val))
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
*local_infile = std::atoi(val.c_str());
|
||||
if (*local_infile != 0 and *local_infile != 1)
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
*local_infile_p = true;
|
||||
} else if (par == "charset" and not *charset_p)
|
||||
{
|
||||
*charset = val;
|
||||
*charset_p = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw soci_error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
mysql_session_backend::mysql_session_backend(
|
||||
connection_parameters const & parameters)
|
||||
{
|
||||
string host, user, password, db, unix_socket, ssl_ca, ssl_cert, ssl_key,
|
||||
charset;
|
||||
int port, local_infile;
|
||||
bool host_p, user_p, password_p, db_p, unix_socket_p, port_p,
|
||||
ssl_ca_p, ssl_cert_p, ssl_key_p, local_infile_p, charset_p;
|
||||
parse_connect_string(parameters.get_connect_string(), &host, &host_p, &user, &user_p,
|
||||
&password, &password_p, &db, &db_p,
|
||||
&unix_socket, &unix_socket_p, &port, &port_p,
|
||||
&ssl_ca, &ssl_ca_p, &ssl_cert, &ssl_cert_p, &ssl_key, &ssl_key_p,
|
||||
&local_infile, &local_infile_p, &charset, &charset_p);
|
||||
conn_ = mysql_init(NULL);
|
||||
if (conn_ == NULL)
|
||||
{
|
||||
throw soci_error("mysql_init() failed.");
|
||||
}
|
||||
if (charset_p)
|
||||
{
|
||||
if (0 != mysql_options(conn_, MYSQL_SET_CHARSET_NAME, charset.c_str()))
|
||||
{
|
||||
clean_up();
|
||||
throw soci_error("mysql_options(MYSQL_SET_CHARSET_NAME) failed.");
|
||||
}
|
||||
}
|
||||
if (ssl_ca_p)
|
||||
{
|
||||
mysql_ssl_set(conn_, ssl_key_p ? ssl_key.c_str() : NULL,
|
||||
ssl_cert_p ? ssl_cert.c_str() : NULL,
|
||||
ssl_ca_p ? ssl_ca.c_str() : NULL, 0, 0);
|
||||
}
|
||||
if (local_infile_p and local_infile == 1)
|
||||
{
|
||||
if (0 != mysql_options(conn_, MYSQL_OPT_LOCAL_INFILE, NULL))
|
||||
{
|
||||
clean_up();
|
||||
throw soci_error(
|
||||
"mysql_options() failed when trying to set local-infile.");
|
||||
}
|
||||
}
|
||||
if (mysql_real_connect(conn_,
|
||||
host_p ? host.c_str() : NULL,
|
||||
user_p ? user.c_str() : NULL,
|
||||
password_p ? password.c_str() : NULL,
|
||||
db_p ? db.c_str() : NULL,
|
||||
port_p ? port : 0,
|
||||
unix_socket_p ? unix_socket.c_str() : NULL,
|
||||
CLIENT_FOUND_ROWS | CLIENT_MULTI_RESULTS) == NULL)
|
||||
{
|
||||
string errMsg = mysql_error(conn_);
|
||||
unsigned int errNum = mysql_errno(conn_);
|
||||
clean_up();
|
||||
throw mysql_soci_error(errMsg, errNum);
|
||||
}
|
||||
}
|
||||
|
||||
mysql_session_backend::~mysql_session_backend()
|
||||
{
|
||||
clean_up();
|
||||
}
|
||||
|
||||
namespace // unnamed
|
||||
{
|
||||
|
||||
// helper function for hardcoded queries
|
||||
void hard_exec(MYSQL *conn, const string & query)
|
||||
{
|
||||
if (0 != mysql_real_query(conn, query.c_str(),
|
||||
static_cast<unsigned long>(query.size())))
|
||||
{
|
||||
throw soci_error(mysql_error(conn));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unnamed
|
||||
|
||||
void mysql_session_backend::begin()
|
||||
{
|
||||
hard_exec(conn_, "BEGIN");
|
||||
}
|
||||
|
||||
void mysql_session_backend::commit()
|
||||
{
|
||||
hard_exec(conn_, "COMMIT");
|
||||
}
|
||||
|
||||
void mysql_session_backend::rollback()
|
||||
{
|
||||
hard_exec(conn_, "ROLLBACK");
|
||||
}
|
||||
|
||||
void mysql_session_backend::clean_up()
|
||||
{
|
||||
if (conn_ != NULL)
|
||||
{
|
||||
mysql_close(conn_);
|
||||
conn_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mysql_statement_backend * mysql_session_backend::make_statement_backend()
|
||||
{
|
||||
return new mysql_statement_backend(*this);
|
||||
}
|
||||
|
||||
mysql_rowid_backend * mysql_session_backend::make_rowid_backend()
|
||||
{
|
||||
return new mysql_rowid_backend(*this);
|
||||
}
|
||||
|
||||
mysql_blob_backend * mysql_session_backend::make_blob_backend()
|
||||
{
|
||||
return new mysql_blob_backend(*this);
|
||||
}
|
||||
273
src/backends/mysql/soci-mysql.h
Normal file
273
src/backends/mysql/soci-mysql.h
Normal file
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_MYSQL_H_INCLUDED
|
||||
#define SOCI_MYSQL_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef SOCI_DLL
|
||||
# ifdef SOCI_MYSQL_SOURCE
|
||||
# define SOCI_MYSQL_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define SOCI_MYSQL_DECL __declspec(dllimport)
|
||||
# endif // SOCI_DLL
|
||||
# endif // SOCI_MYSQL_SOURCE
|
||||
#endif // _WIN32
|
||||
//
|
||||
// If SOCI_MYSQL_DECL isn't defined yet define it now
|
||||
#ifndef SOCI_MYSQL_DECL
|
||||
# define SOCI_MYSQL_DECL
|
||||
#endif
|
||||
|
||||
#include "soci-backend.h"
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h> // SOCKET
|
||||
#endif // _WIN32
|
||||
#include <mysql.h> // MySQL Client
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
class mysql_soci_error : public soci_error
|
||||
{
|
||||
public:
|
||||
mysql_soci_error(std::string const & msg, int errNum)
|
||||
: soci_error(msg), err_num_(errNum) {}
|
||||
|
||||
unsigned int err_num_;
|
||||
};
|
||||
|
||||
struct mysql_statement_backend;
|
||||
struct mysql_standard_into_type_backend : details::standard_into_type_backend
|
||||
{
|
||||
mysql_standard_into_type_backend(mysql_statement_backend &st)
|
||||
: statement_(st) {}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, bool calledFromFetch,
|
||||
indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
mysql_statement_backend &statement_;
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
};
|
||||
|
||||
struct mysql_vector_into_type_backend : details::vector_into_type_backend
|
||||
{
|
||||
mysql_vector_into_type_backend(mysql_statement_backend &st)
|
||||
: statement_(st) {}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, indicator *ind);
|
||||
|
||||
virtual void resize(std::size_t sz);
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
mysql_statement_backend &statement_;
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
};
|
||||
|
||||
struct mysql_standard_use_type_backend : details::standard_use_type_backend
|
||||
{
|
||||
mysql_standard_use_type_backend(mysql_statement_backend &st)
|
||||
: statement_(st), position_(0), buf_(NULL) {}
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
virtual void post_use(bool gotData, indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
mysql_statement_backend &statement_;
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
std::string name_;
|
||||
char *buf_;
|
||||
};
|
||||
|
||||
struct mysql_vector_use_type_backend : details::vector_use_type_backend
|
||||
{
|
||||
mysql_vector_use_type_backend(mysql_statement_backend &st)
|
||||
: statement_(st), position_(0) {}
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
mysql_statement_backend &statement_;
|
||||
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
std::string name_;
|
||||
std::vector<char *> buffers_;
|
||||
};
|
||||
|
||||
struct mysql_session_backend;
|
||||
struct mysql_statement_backend : details::statement_backend
|
||||
{
|
||||
mysql_statement_backend(mysql_session_backend &session);
|
||||
|
||||
virtual void alloc();
|
||||
virtual void clean_up();
|
||||
virtual void prepare(std::string const &query,
|
||||
details::statement_type eType);
|
||||
|
||||
virtual exec_fetch_result execute(int number);
|
||||
virtual exec_fetch_result fetch(int number);
|
||||
|
||||
virtual long long get_affected_rows();
|
||||
virtual int get_number_of_rows();
|
||||
|
||||
virtual std::string rewrite_for_procedure_call(std::string const &query);
|
||||
|
||||
virtual int prepare_for_describe();
|
||||
virtual void describe_column(int colNum, data_type &dtype,
|
||||
std::string &columnName);
|
||||
|
||||
virtual mysql_standard_into_type_backend * make_into_type_backend();
|
||||
virtual mysql_standard_use_type_backend * make_use_type_backend();
|
||||
virtual mysql_vector_into_type_backend * make_vector_into_type_backend();
|
||||
virtual mysql_vector_use_type_backend * make_vector_use_type_backend();
|
||||
|
||||
mysql_session_backend &session_;
|
||||
|
||||
MYSQL_RES *result_;
|
||||
|
||||
// The query is split into chunks, separated by the named parameters;
|
||||
// e.g. for "SELECT id FROM ttt WHERE name = :foo AND gender = :bar"
|
||||
// we will have query chunks "SELECT id FROM ttt WHERE name = ",
|
||||
// "AND gender = " and names "foo", "bar".
|
||||
std::vector<std::string> queryChunks_;
|
||||
std::vector<std::string> names_; // list of names for named binds
|
||||
|
||||
long long rowsAffectedBulk_; // number of rows affected by the last bulk operation
|
||||
|
||||
int numberOfRows_; // number of rows retrieved from the server
|
||||
int currentRow_; // "current" row number to consume in postFetch
|
||||
int rowsToConsume_; // number of rows to be consumed in postFetch
|
||||
|
||||
bool justDescribed_; // to optimize row description with immediately
|
||||
// following actual statement execution
|
||||
|
||||
// Prefetch the row offsets in order to use mysql_row_seek() for
|
||||
// random access to rows, since mysql_data_seek() is expensive.
|
||||
std::vector<MYSQL_ROW_OFFSET> resultRowOffsets_;
|
||||
|
||||
bool hasIntoElements_;
|
||||
bool hasVectorIntoElements_;
|
||||
bool hasUseElements_;
|
||||
bool hasVectorUseElements_;
|
||||
|
||||
// the following maps are used for finding data buffers according to
|
||||
// use elements specified by the user
|
||||
|
||||
typedef std::map<int, char **> UseByPosBuffersMap;
|
||||
UseByPosBuffersMap useByPosBuffers_;
|
||||
|
||||
typedef std::map<std::string, char **> UseByNameBuffersMap;
|
||||
UseByNameBuffersMap useByNameBuffers_;
|
||||
};
|
||||
|
||||
struct mysql_rowid_backend : details::rowid_backend
|
||||
{
|
||||
mysql_rowid_backend(mysql_session_backend &session);
|
||||
|
||||
~mysql_rowid_backend();
|
||||
};
|
||||
|
||||
struct mysql_blob_backend : details::blob_backend
|
||||
{
|
||||
mysql_blob_backend(mysql_session_backend &session);
|
||||
|
||||
~mysql_blob_backend();
|
||||
|
||||
virtual std::size_t get_len();
|
||||
virtual std::size_t read(std::size_t offset, char *buf,
|
||||
std::size_t toRead);
|
||||
virtual std::size_t write(std::size_t offset, char const *buf,
|
||||
std::size_t toWrite);
|
||||
virtual std::size_t append(char const *buf, std::size_t toWrite);
|
||||
virtual void trim(std::size_t newLen);
|
||||
|
||||
mysql_session_backend &session_;
|
||||
};
|
||||
|
||||
struct mysql_session_backend : details::session_backend
|
||||
{
|
||||
mysql_session_backend(connection_parameters const & parameters);
|
||||
|
||||
~mysql_session_backend();
|
||||
|
||||
virtual void begin();
|
||||
virtual void commit();
|
||||
virtual void rollback();
|
||||
|
||||
virtual std::string get_backend_name() const { return "mysql"; }
|
||||
|
||||
void clean_up();
|
||||
|
||||
virtual mysql_statement_backend * make_statement_backend();
|
||||
virtual mysql_rowid_backend * make_rowid_backend();
|
||||
virtual mysql_blob_backend * make_blob_backend();
|
||||
|
||||
MYSQL *conn_;
|
||||
};
|
||||
|
||||
|
||||
struct mysql_backend_factory : backend_factory
|
||||
{
|
||||
mysql_backend_factory() {}
|
||||
virtual mysql_session_backend * make_session(
|
||||
connection_parameters const & parameters) const;
|
||||
};
|
||||
|
||||
extern SOCI_MYSQL_DECL mysql_backend_factory const mysql;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_MYSQL_DECL backend_factory const * factory_mysql();
|
||||
SOCI_MYSQL_DECL void register_factory_mysql();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_MYSQL_H_INCLUDED
|
||||
141
src/backends/mysql/standard-into-type.cpp
Normal file
141
src/backends/mysql/standard-into-type.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include <soci-platform.h>
|
||||
#include "common.h"
|
||||
// std
|
||||
#include <cassert>
|
||||
#include <ciso646>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::mysql;
|
||||
|
||||
|
||||
void mysql_standard_into_type_backend::define_by_pos(
|
||||
int &position, void *data, exchange_type type)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
position_ = position++;
|
||||
}
|
||||
|
||||
void mysql_standard_into_type_backend::pre_fetch()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
void mysql_standard_into_type_backend::post_fetch(
|
||||
bool gotData, bool calledFromFetch, indicator *ind)
|
||||
{
|
||||
if (calledFromFetch == true && gotData == false)
|
||||
{
|
||||
// this is a normal end-of-rowset condition,
|
||||
// no need to do anything (fetch() will return false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gotData)
|
||||
{
|
||||
int pos = position_ - 1;
|
||||
//mysql_data_seek(statement_.result_, statement_.currentRow_);
|
||||
mysql_row_seek(statement_.result_,
|
||||
statement_.resultRowOffsets_[statement_.currentRow_]);
|
||||
MYSQL_ROW row = mysql_fetch_row(statement_.result_);
|
||||
if (row[pos] == NULL)
|
||||
{
|
||||
if (ind == NULL)
|
||||
{
|
||||
throw soci_error(
|
||||
"Null value fetched and no indicator defined.");
|
||||
}
|
||||
*ind = i_null;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ind != NULL)
|
||||
{
|
||||
*ind = i_ok;
|
||||
}
|
||||
}
|
||||
const char *buf = row[pos] != NULL ? row[pos] : "";
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
{
|
||||
char *dest = static_cast<char*>(data_);
|
||||
*dest = *buf;
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::string *dest = static_cast<std::string *>(data_);
|
||||
unsigned long * lengths =
|
||||
mysql_fetch_lengths(statement_.result_);
|
||||
dest->assign(buf, lengths[pos]);
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
short *dest = static_cast<short*>(data_);
|
||||
parse_num(buf, *dest);
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
int *dest = static_cast<int*>(data_);
|
||||
parse_num(buf, *dest);
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
long long *dest = static_cast<long long *>(data_);
|
||||
parse_num(buf, *dest);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
unsigned long long *dest =
|
||||
static_cast<unsigned long long*>(data_);
|
||||
parse_num(buf, *dest);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
double *dest = static_cast<double*>(data_);
|
||||
parse_num(buf, *dest);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
// attempt to parse the string and convert to std::tm
|
||||
std::tm *dest = static_cast<std::tm *>(data_);
|
||||
parse_std_tm(buf, *dest);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Into element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mysql_standard_into_type_backend::clean_up()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
172
src/backends/mysql/standard-use-type.cpp
Normal file
172
src/backends/mysql/standard-use-type.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include "common.h"
|
||||
#include <soci-platform.h>
|
||||
// std
|
||||
#include <ciso646>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::mysql;
|
||||
|
||||
|
||||
void mysql_standard_use_type_backend::bind_by_pos(
|
||||
int &position, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
position_ = position++;
|
||||
}
|
||||
|
||||
void mysql_standard_use_type_backend::bind_by_name(
|
||||
std::string const &name, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
void mysql_standard_use_type_backend::pre_use(indicator const *ind)
|
||||
{
|
||||
if (ind != NULL && *ind == i_null)
|
||||
{
|
||||
buf_ = new char[5];
|
||||
std::strcpy(buf_, "NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
// allocate and fill the buffer with text-formatted client data
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
{
|
||||
char buf[] = { *static_cast<char*>(data_), '\0' };
|
||||
buf_ = quote(statement_.session_.conn_, buf, 1);
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::string *s = static_cast<std::string *>(data_);
|
||||
buf_ = quote(statement_.session_.conn_,
|
||||
s->c_str(), s->size());
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<short>::digits10 + 3;
|
||||
buf_ = new char[bufSize];
|
||||
snprintf(buf_, bufSize, "%d",
|
||||
static_cast<int>(*static_cast<short*>(data_)));
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<int>::digits10 + 3;
|
||||
buf_ = new char[bufSize];
|
||||
snprintf(buf_, bufSize, "%d", *static_cast<int*>(data_));
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<long long>::digits10 + 3;
|
||||
buf_ = new char[bufSize];
|
||||
snprintf(buf_, bufSize, "%" LL_FMT_FLAGS "d", *static_cast<long long *>(data_));
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<unsigned long long>::digits10 + 3;
|
||||
buf_ = new char[bufSize];
|
||||
snprintf(buf_, bufSize, "%" LL_FMT_FLAGS "u",
|
||||
*static_cast<unsigned long long *>(data_));
|
||||
}
|
||||
break;
|
||||
|
||||
case x_double:
|
||||
{
|
||||
if (is_infinity_or_nan(*static_cast<double*>(data_))) {
|
||||
throw soci_error(
|
||||
"Use element used with infinity or NaN, which are "
|
||||
"not supported by the MySQL server.");
|
||||
}
|
||||
|
||||
std::size_t const bufSize = 100;
|
||||
buf_ = new char[bufSize];
|
||||
|
||||
snprintf(buf_, bufSize, "%.20g",
|
||||
*static_cast<double*>(data_));
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::size_t const bufSize = 22;
|
||||
buf_ = new char[bufSize];
|
||||
|
||||
std::tm *t = static_cast<std::tm *>(data_);
|
||||
snprintf(buf_, bufSize,
|
||||
"\'%d-%02d-%02d %02d:%02d:%02d\'",
|
||||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Use element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
|
||||
if (position_ > 0)
|
||||
{
|
||||
// binding by position
|
||||
statement_.useByPosBuffers_[position_] = &buf_;
|
||||
}
|
||||
else
|
||||
{
|
||||
// binding by name
|
||||
statement_.useByNameBuffers_[name_] = &buf_;
|
||||
}
|
||||
}
|
||||
|
||||
void mysql_standard_use_type_backend::post_use(bool /*gotData*/, indicator* /*ind*/)
|
||||
{
|
||||
// TODO: Is it possible to have the bound element being overwritten
|
||||
// by the database?
|
||||
// If not, then nothing to do here, please remove this comment.
|
||||
// If yes, then use the value of the readOnly parameter:
|
||||
// - true: the given object should not be modified and the backend
|
||||
// should detect if the modification was performed on the
|
||||
// isolated buffer and throw an exception if the buffer was modified
|
||||
// (this indicates logic error, because the user used const object
|
||||
// and executed a query that attempted to modified it)
|
||||
// - false: the modification should be propagated to the given object.
|
||||
// ...
|
||||
|
||||
clean_up();
|
||||
}
|
||||
|
||||
void mysql_standard_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
}
|
||||
480
src/backends/mysql/statement.cpp
Normal file
480
src/backends/mysql/statement.cpp
Normal file
@@ -0,0 +1,480 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include <cctype>
|
||||
#include <ciso646>
|
||||
//#include <iostream>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using std::string;
|
||||
|
||||
|
||||
mysql_statement_backend::mysql_statement_backend(
|
||||
mysql_session_backend &session)
|
||||
: session_(session), result_(NULL),
|
||||
rowsAffectedBulk_(-1LL), justDescribed_(false),
|
||||
hasIntoElements_(false), hasVectorIntoElements_(false),
|
||||
hasUseElements_(false), hasVectorUseElements_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void mysql_statement_backend::alloc()
|
||||
{
|
||||
// nothing to do here.
|
||||
}
|
||||
|
||||
void mysql_statement_backend::clean_up()
|
||||
{
|
||||
// 'reset' the value for a
|
||||
// potential new execution.
|
||||
rowsAffectedBulk_ = -1;
|
||||
|
||||
if (result_ != NULL)
|
||||
{
|
||||
mysql_free_result(result_);
|
||||
result_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void mysql_statement_backend::prepare(std::string const & query,
|
||||
statement_type /* eType */)
|
||||
{
|
||||
queryChunks_.clear();
|
||||
enum { eNormal, eInQuotes, eInName } state = eNormal;
|
||||
|
||||
std::string name;
|
||||
queryChunks_.push_back("");
|
||||
|
||||
bool escaped = false;
|
||||
for (std::string::const_iterator it = query.begin(), end = query.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case eNormal:
|
||||
if (*it == '\'')
|
||||
{
|
||||
queryChunks_.back() += *it;
|
||||
state = eInQuotes;
|
||||
}
|
||||
else if (*it == ':')
|
||||
{
|
||||
const std::string::const_iterator next_it = it + 1;
|
||||
// Check whether this is an assignment (e.g. @x:=y)
|
||||
// and treat it as a special case, not as a named binding.
|
||||
if (next_it != end && *next_it == '=')
|
||||
{
|
||||
queryChunks_.back() += ":=";
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = eInName;
|
||||
}
|
||||
}
|
||||
else // regular character, stay in the same state
|
||||
{
|
||||
queryChunks_.back() += *it;
|
||||
}
|
||||
break;
|
||||
case eInQuotes:
|
||||
if (*it == '\'' && !escaped)
|
||||
{
|
||||
queryChunks_.back() += *it;
|
||||
state = eNormal;
|
||||
}
|
||||
else // regular quoted character
|
||||
{
|
||||
queryChunks_.back() += *it;
|
||||
}
|
||||
escaped = *it == '\\' && !escaped;
|
||||
break;
|
||||
case eInName:
|
||||
if (std::isalnum(*it) || *it == '_')
|
||||
{
|
||||
name += *it;
|
||||
}
|
||||
else // end of name
|
||||
{
|
||||
names_.push_back(name);
|
||||
name.clear();
|
||||
queryChunks_.push_back("");
|
||||
queryChunks_.back() += *it;
|
||||
state = eNormal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == eInName)
|
||||
{
|
||||
names_.push_back(name);
|
||||
}
|
||||
/*
|
||||
cerr << "Chunks: ";
|
||||
for (std::vector<std::string>::iterator i = queryChunks_.begin();
|
||||
i != queryChunks_.end(); ++i)
|
||||
{
|
||||
cerr << "\"" << *i << "\" ";
|
||||
}
|
||||
cerr << "\nNames: ";
|
||||
for (std::vector<std::string>::iterator i = names_.begin();
|
||||
i != names_.end(); ++i)
|
||||
{
|
||||
cerr << "\"" << *i << "\" ";
|
||||
}
|
||||
cerr << endl;
|
||||
*/
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
mysql_statement_backend::execute(int number)
|
||||
{
|
||||
if (justDescribed_ == false)
|
||||
{
|
||||
clean_up();
|
||||
|
||||
if (number > 1 && hasIntoElements_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Bulk use with single into elements is not supported.");
|
||||
}
|
||||
// number - size of vectors (into/use)
|
||||
// numberOfExecutions - number of loops to perform
|
||||
int numberOfExecutions = 1;
|
||||
if (number > 0)
|
||||
{
|
||||
numberOfExecutions = hasUseElements_ ? 1 : number;
|
||||
}
|
||||
|
||||
std::string query;
|
||||
if (not useByPosBuffers_.empty() or not useByNameBuffers_.empty())
|
||||
{
|
||||
if (not useByPosBuffers_.empty() and not useByNameBuffers_.empty())
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position "
|
||||
"or by name.");
|
||||
}
|
||||
long long rowsAffectedBulkTemp = 0;
|
||||
for (int i = 0; i != numberOfExecutions; ++i)
|
||||
{
|
||||
std::vector<char *> paramValues;
|
||||
|
||||
if (not useByPosBuffers_.empty())
|
||||
{
|
||||
// use elements bind by position
|
||||
// the map of use buffers can be traversed
|
||||
// in its natural order
|
||||
|
||||
for (UseByPosBuffersMap::iterator
|
||||
it = useByPosBuffers_.begin(),
|
||||
end = useByPosBuffers_.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
char **buffers = it->second;
|
||||
//cerr<<"i: "<<i<<", buffers[i]: "<<buffers[i]<<endl;
|
||||
paramValues.push_back(buffers[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use elements bind by name
|
||||
|
||||
for (std::vector<std::string>::iterator
|
||||
it = names_.begin(), end = names_.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
UseByNameBuffersMap::iterator b
|
||||
= useByNameBuffers_.find(*it);
|
||||
if (b == useByNameBuffers_.end())
|
||||
{
|
||||
std::string msg(
|
||||
"Missing use element for bind by name (");
|
||||
msg += *it;
|
||||
msg += ").";
|
||||
throw soci_error(msg);
|
||||
}
|
||||
char **buffers = b->second;
|
||||
paramValues.push_back(buffers[i]);
|
||||
}
|
||||
}
|
||||
//cerr << "queryChunks_.size(): "<<queryChunks_.size()<<endl;
|
||||
//cerr << "paramValues.size(): "<<paramValues.size()<<endl;
|
||||
if (queryChunks_.size() != paramValues.size()
|
||||
and queryChunks_.size() != paramValues.size() + 1)
|
||||
{
|
||||
throw soci_error("Wrong number of parameters.");
|
||||
}
|
||||
|
||||
std::vector<std::string>::const_iterator ci
|
||||
= queryChunks_.begin();
|
||||
for (std::vector<char*>::const_iterator
|
||||
pi = paramValues.begin(), end = paramValues.end();
|
||||
pi != end; ++ci, ++pi)
|
||||
{
|
||||
query += *ci;
|
||||
query += *pi;
|
||||
}
|
||||
if (ci != queryChunks_.end())
|
||||
{
|
||||
query += *ci;
|
||||
}
|
||||
if (numberOfExecutions > 1)
|
||||
{
|
||||
// bulk operation
|
||||
//std::cerr << "bulk operation:\n" << query << std::endl;
|
||||
if (0 != mysql_real_query(session_.conn_, query.c_str(),
|
||||
query.size()))
|
||||
{
|
||||
// preserve the number of rows affected so far.
|
||||
rowsAffectedBulk_ = rowsAffectedBulkTemp;
|
||||
throw mysql_soci_error(mysql_error(session_.conn_),
|
||||
mysql_errno(session_.conn_));
|
||||
}
|
||||
else
|
||||
{
|
||||
rowsAffectedBulkTemp += static_cast<long long>(mysql_affected_rows(session_.conn_));
|
||||
}
|
||||
if (mysql_field_count(session_.conn_) != 0)
|
||||
{
|
||||
throw soci_error("The query shouldn't have returned"
|
||||
" any data but it did.");
|
||||
}
|
||||
query.clear();
|
||||
}
|
||||
}
|
||||
rowsAffectedBulk_ = rowsAffectedBulkTemp;
|
||||
if (numberOfExecutions > 1)
|
||||
{
|
||||
// bulk
|
||||
return ef_no_data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
query = queryChunks_.front();
|
||||
}
|
||||
|
||||
//std::cerr << query << std::endl;
|
||||
if (0 != mysql_real_query(session_.conn_, query.c_str(),
|
||||
query.size()))
|
||||
{
|
||||
throw mysql_soci_error(mysql_error(session_.conn_),
|
||||
mysql_errno(session_.conn_));
|
||||
}
|
||||
result_ = mysql_store_result(session_.conn_);
|
||||
if (result_ == NULL and mysql_field_count(session_.conn_) != 0)
|
||||
{
|
||||
throw mysql_soci_error(mysql_error(session_.conn_),
|
||||
mysql_errno(session_.conn_));
|
||||
}
|
||||
if (result_ != NULL)
|
||||
{
|
||||
// Cache the rows offsets to have random access to the rows later.
|
||||
// [mysql_data_seek() is O(n) so we don't want to use it].
|
||||
int numrows = static_cast<int>(mysql_num_rows(result_));
|
||||
resultRowOffsets_.resize(numrows);
|
||||
for (int i = 0; i < numrows; i++)
|
||||
{
|
||||
resultRowOffsets_[i] = mysql_row_tell(result_);
|
||||
mysql_fetch_row(result_);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
justDescribed_ = false;
|
||||
}
|
||||
|
||||
if (result_ != NULL)
|
||||
{
|
||||
currentRow_ = 0;
|
||||
rowsToConsume_ = 0;
|
||||
|
||||
numberOfRows_ = static_cast<int>(mysql_num_rows(result_));
|
||||
if (numberOfRows_ == 0)
|
||||
{
|
||||
return ef_no_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (number > 0)
|
||||
{
|
||||
// prepare for the subsequent data consumption
|
||||
return fetch(number);
|
||||
}
|
||||
else
|
||||
{
|
||||
// execute(0) was meant to only perform the query
|
||||
return ef_success;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// it was not a SELECT
|
||||
return ef_no_data;
|
||||
}
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
mysql_statement_backend::fetch(int number)
|
||||
{
|
||||
// Note: This function does not actually fetch anything from anywhere
|
||||
// - the data was already retrieved from the server in the execute()
|
||||
// function, and the actual consumption of this data will take place
|
||||
// in the postFetch functions, called for each into element.
|
||||
// Here, we only prepare for this to happen (to emulate "the Oracle way").
|
||||
|
||||
// forward the "cursor" from the last fetch
|
||||
currentRow_ += rowsToConsume_;
|
||||
|
||||
if (currentRow_ >= numberOfRows_)
|
||||
{
|
||||
// all rows were already consumed
|
||||
return ef_no_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentRow_ + number > numberOfRows_)
|
||||
{
|
||||
rowsToConsume_ = numberOfRows_ - currentRow_;
|
||||
|
||||
// this simulates the behaviour of Oracle
|
||||
// - when EOF is hit, we return ef_no_data even when there are
|
||||
// actually some rows fetched
|
||||
return ef_no_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
rowsToConsume_ = number;
|
||||
return ef_success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long long mysql_statement_backend::get_affected_rows()
|
||||
{
|
||||
if (rowsAffectedBulk_ >= 0)
|
||||
{
|
||||
return rowsAffectedBulk_;
|
||||
}
|
||||
return static_cast<long long>(mysql_affected_rows(session_.conn_));
|
||||
}
|
||||
|
||||
int mysql_statement_backend::get_number_of_rows()
|
||||
{
|
||||
return numberOfRows_ - currentRow_;
|
||||
}
|
||||
|
||||
std::string mysql_statement_backend::rewrite_for_procedure_call(
|
||||
std::string const &query)
|
||||
{
|
||||
std::string newQuery("select ");
|
||||
newQuery += query;
|
||||
return newQuery;
|
||||
}
|
||||
|
||||
int mysql_statement_backend::prepare_for_describe()
|
||||
{
|
||||
execute(1);
|
||||
justDescribed_ = true;
|
||||
|
||||
int columns = mysql_field_count(session_.conn_);
|
||||
return columns;
|
||||
}
|
||||
|
||||
void mysql_statement_backend::describe_column(int colNum,
|
||||
data_type & type, std::string & columnName)
|
||||
{
|
||||
int pos = colNum - 1;
|
||||
MYSQL_FIELD *field = mysql_fetch_field_direct(result_, pos);
|
||||
switch (field->type)
|
||||
{
|
||||
case FIELD_TYPE_CHAR: //MYSQL_TYPE_TINY:
|
||||
case FIELD_TYPE_SHORT: //MYSQL_TYPE_SHORT:
|
||||
case FIELD_TYPE_INT24: //MYSQL_TYPE_INT24:
|
||||
type = dt_integer;
|
||||
break;
|
||||
case FIELD_TYPE_LONG: //MYSQL_TYPE_LONG:
|
||||
type = field->flags & UNSIGNED_FLAG ? dt_long_long
|
||||
: dt_integer;
|
||||
break;
|
||||
case FIELD_TYPE_LONGLONG: //MYSQL_TYPE_LONGLONG:
|
||||
type = field->flags & UNSIGNED_FLAG ? dt_unsigned_long_long :
|
||||
dt_long_long;
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT: //MYSQL_TYPE_FLOAT:
|
||||
case FIELD_TYPE_DOUBLE: //MYSQL_TYPE_DOUBLE:
|
||||
case FIELD_TYPE_DECIMAL: //MYSQL_TYPE_DECIMAL:
|
||||
// Prior to MySQL v. 5.x there was no column type corresponding
|
||||
// to MYSQL_TYPE_NEWDECIMAL. However, MySQL server 5.x happily
|
||||
// sends field type number 246, no matter which version of libraries
|
||||
// the client is using.
|
||||
case 246: //MYSQL_TYPE_NEWDECIMAL:
|
||||
type = dt_double;
|
||||
break;
|
||||
case FIELD_TYPE_TIMESTAMP: //MYSQL_TYPE_TIMESTAMP:
|
||||
case FIELD_TYPE_DATE: //MYSQL_TYPE_DATE:
|
||||
case FIELD_TYPE_TIME: //MYSQL_TYPE_TIME:
|
||||
case FIELD_TYPE_DATETIME: //MYSQL_TYPE_DATETIME:
|
||||
case FIELD_TYPE_YEAR: //MYSQL_TYPE_YEAR:
|
||||
case FIELD_TYPE_NEWDATE: //MYSQL_TYPE_NEWDATE:
|
||||
type = dt_date;
|
||||
break;
|
||||
// case MYSQL_TYPE_VARCHAR:
|
||||
case FIELD_TYPE_VAR_STRING: //MYSQL_TYPE_VAR_STRING:
|
||||
case FIELD_TYPE_STRING: //MYSQL_TYPE_STRING:
|
||||
case FIELD_TYPE_BLOB: // TEXT OR BLOB
|
||||
case FIELD_TYPE_TINY_BLOB:
|
||||
case FIELD_TYPE_MEDIUM_BLOB:
|
||||
case FIELD_TYPE_LONG_BLOB:
|
||||
type = dt_string;
|
||||
break;
|
||||
default:
|
||||
//std::cerr << "field->type: " << field->type << std::endl;
|
||||
throw soci_error("Unknown data type.");
|
||||
}
|
||||
columnName = field->name;
|
||||
}
|
||||
|
||||
mysql_standard_into_type_backend *
|
||||
mysql_statement_backend::make_into_type_backend()
|
||||
{
|
||||
hasIntoElements_ = true;
|
||||
return new mysql_standard_into_type_backend(*this);
|
||||
}
|
||||
|
||||
mysql_standard_use_type_backend *
|
||||
mysql_statement_backend::make_use_type_backend()
|
||||
{
|
||||
hasUseElements_ = true;
|
||||
return new mysql_standard_use_type_backend(*this);
|
||||
}
|
||||
|
||||
mysql_vector_into_type_backend *
|
||||
mysql_statement_backend::make_vector_into_type_backend()
|
||||
{
|
||||
hasVectorIntoElements_ = true;
|
||||
return new mysql_vector_into_type_backend(*this);
|
||||
}
|
||||
|
||||
mysql_vector_use_type_backend *
|
||||
mysql_statement_backend::make_vector_use_type_backend()
|
||||
{
|
||||
hasVectorUseElements_ = true;
|
||||
return new mysql_vector_use_type_backend(*this);
|
||||
}
|
||||
1
src/backends/mysql/test/.gitignore
vendored
Normal file
1
src/backends/mysql/test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
test_mysql
|
||||
14
src/backends/mysql/test/CMakeLists.txt
Normal file
14
src/backends/mysql/test/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend_test(
|
||||
BACKEND MySQL
|
||||
SOURCE test-mysql.cpp
|
||||
CONNSTR "dummy")
|
||||
22
src/backends/mysql/test/Makefile.basic
Normal file
22
src/backends/mysql/test/Makefile.basic
Normal file
@@ -0,0 +1,22 @@
|
||||
# The following variables are specific to this backend and their correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
MYSQLLIBDIR = -L/usr/lib/mysql
|
||||
MYSQLLIBS = -lmysqlclient -lz
|
||||
MYSQLINCLUDEDIR = -I/usr/include/mysql
|
||||
|
||||
# The rest of the Makefile is independent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
INCLUDEDIRS = -I.. -I../../../core ${MYSQLINCLUDEDIR}
|
||||
LIBDIRS = -L.. -L../../../core ${MYSQLLIBDIR}
|
||||
LIBS = -lsoci_core -lsoci_mysql -ldl ${MYSQLLIBS}
|
||||
|
||||
|
||||
test-mysql : test-mysql.cpp
|
||||
${COMPILER} -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} ${LIBDIRS} ${LIBS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f test-mysql
|
||||
1002
src/backends/mysql/test/test-mysql.cpp
Normal file
1002
src/backends/mysql/test/test-mysql.cpp
Normal file
File diff suppressed because it is too large
Load Diff
231
src/backends/mysql/vector-into-type.cpp
Normal file
231
src/backends/mysql/vector-into-type.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include "common.h"
|
||||
#include <soci-platform.h>
|
||||
#include <ciso646>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::mysql;
|
||||
|
||||
|
||||
void mysql_vector_into_type_backend::define_by_pos(
|
||||
int &position, void *data, exchange_type type)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
position_ = position++;
|
||||
}
|
||||
|
||||
void mysql_vector_into_type_backend::pre_fetch()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
namespace // anonymous
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void set_invector_(void *p, int indx, T const &val)
|
||||
{
|
||||
std::vector<T> *dest =
|
||||
static_cast<std::vector<T> *>(p);
|
||||
|
||||
std::vector<T> &v = *dest;
|
||||
v[indx] = val;
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
void mysql_vector_into_type_backend::post_fetch(bool gotData, indicator *ind)
|
||||
{
|
||||
if (gotData)
|
||||
{
|
||||
// Here, rowsToConsume_ in the Statement object designates
|
||||
// the number of rows that need to be put in the user's buffers.
|
||||
|
||||
// MySQL column positions start at 0
|
||||
int pos = position_ - 1;
|
||||
|
||||
int const endRow = statement_.currentRow_ + statement_.rowsToConsume_;
|
||||
|
||||
//mysql_data_seek(statement_.result_, statement_.currentRow_);
|
||||
mysql_row_seek(statement_.result_,
|
||||
statement_.resultRowOffsets_[statement_.currentRow_]);
|
||||
for (int curRow = statement_.currentRow_, i = 0;
|
||||
curRow != endRow; ++curRow, ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(statement_.result_);
|
||||
// first, deal with indicators
|
||||
if (row[pos] == NULL)
|
||||
{
|
||||
if (ind == NULL)
|
||||
{
|
||||
throw soci_error(
|
||||
"Null value fetched and no indicator defined.");
|
||||
}
|
||||
|
||||
ind[i] = i_null;
|
||||
|
||||
// no need to convert data if it is null, go to next row
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ind != NULL)
|
||||
{
|
||||
ind[i] = i_ok;
|
||||
}
|
||||
}
|
||||
|
||||
// buffer with data retrieved from server, in text format
|
||||
const char *buf = row[pos] != NULL ? row[pos] : "";
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
set_invector_(data_, i, *buf);
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
unsigned long * lengths =
|
||||
mysql_fetch_lengths(statement_.result_);
|
||||
// Not sure if it's necessary, but the code below is used
|
||||
// instead of
|
||||
// set_invector_(data_, i, std::string(buf, lengths[pos]);
|
||||
// to avoid copying the (possibly large) temporary string.
|
||||
std::vector<std::string> *dest =
|
||||
static_cast<std::vector<std::string> *>(data_);
|
||||
(*dest)[i].assign(buf, lengths[pos]);
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
short val;
|
||||
parse_num(buf, val);
|
||||
set_invector_(data_, i, val);
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
int val;
|
||||
parse_num(buf, val);
|
||||
set_invector_(data_, i, val);
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
long long val;
|
||||
parse_num(buf, val);
|
||||
set_invector_(data_, i, val);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
unsigned long long val;
|
||||
parse_num(buf, val);
|
||||
set_invector_(data_, i, val);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
double val;
|
||||
parse_num(buf, val);
|
||||
set_invector_(data_, i, val);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
// attempt to parse the string and convert to std::tm
|
||||
std::tm t;
|
||||
parse_std_tm(buf, t);
|
||||
|
||||
set_invector_(data_, i, t);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else // no data retrieved
|
||||
{
|
||||
// nothing to do, into vectors are already truncated
|
||||
}
|
||||
}
|
||||
|
||||
namespace // anonymous
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void resizevector_(void *p, std::size_t sz)
|
||||
{
|
||||
std::vector<T> *v = static_cast<std::vector<T> *>(p);
|
||||
v->resize(sz);
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
void mysql_vector_into_type_backend::resize(std::size_t sz)
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char: resizevector_<char> (data_, sz); break;
|
||||
case x_short: resizevector_<short> (data_, sz); break;
|
||||
case x_integer: resizevector_<int> (data_, sz); break;
|
||||
case x_long_long: resizevector_<long long> (data_, sz); break;
|
||||
case x_unsigned_long_long:
|
||||
resizevector_<unsigned long long>(data_, sz);
|
||||
break;
|
||||
case x_double: resizevector_<double> (data_, sz); break;
|
||||
case x_stdstring: resizevector_<std::string> (data_, sz); break;
|
||||
case x_stdtm: resizevector_<std::tm> (data_, sz); break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into vector element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t mysql_vector_into_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char: sz = get_vector_size<char> (data_); break;
|
||||
case x_short: sz = get_vector_size<short> (data_); break;
|
||||
case x_integer: sz = get_vector_size<int> (data_); break;
|
||||
case x_long_long: sz = get_vector_size<long long> (data_); break;
|
||||
case x_unsigned_long_long:
|
||||
sz = get_vector_size<unsigned long long>(data_);
|
||||
break;
|
||||
case x_double: sz = get_vector_size<double> (data_); break;
|
||||
case x_stdstring: sz = get_vector_size<std::string> (data_); break;
|
||||
case x_stdtm: sz = get_vector_size<std::tm> (data_); break;
|
||||
|
||||
default:
|
||||
throw soci_error("Into vector element used with non-supported type.");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void mysql_vector_into_type_backend::clean_up()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
220
src/backends/mysql/vector-use-type.cpp
Normal file
220
src/backends/mysql/vector-use-type.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
|
||||
// MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_MYSQL_SOURCE
|
||||
#include "soci-mysql.h"
|
||||
#include "common.h"
|
||||
#include <soci-platform.h>
|
||||
// std
|
||||
#include <ciso646>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
using namespace soci::details::mysql;
|
||||
|
||||
|
||||
void mysql_vector_use_type_backend::bind_by_pos(int &position, void *data,
|
||||
exchange_type type)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
position_ = position++;
|
||||
}
|
||||
|
||||
void mysql_vector_use_type_backend::bind_by_name(
|
||||
std::string const &name, void *data, exchange_type type)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
void mysql_vector_use_type_backend::pre_use(indicator const *ind)
|
||||
{
|
||||
std::size_t const vsize = size();
|
||||
for (size_t i = 0; i != vsize; ++i)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
// the data in vector can be either i_ok or i_null
|
||||
if (ind != NULL && ind[i] == i_null)
|
||||
{
|
||||
buf = new char[5];
|
||||
std::strcpy(buf, "NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
// allocate and fill the buffer with text-formatted client data
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
{
|
||||
std::vector<char> *pv
|
||||
= static_cast<std::vector<char> *>(data_);
|
||||
std::vector<char> &v = *pv;
|
||||
|
||||
char tmp[] = { v[i], '\0' };
|
||||
buf = quote(statement_.session_.conn_, tmp, 1);
|
||||
}
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::vector<std::string> *pv
|
||||
= static_cast<std::vector<std::string> *>(data_);
|
||||
std::vector<std::string> &v = *pv;
|
||||
|
||||
buf = quote(statement_.session_.conn_,
|
||||
v[i].c_str(), v[i].size());
|
||||
}
|
||||
break;
|
||||
case x_short:
|
||||
{
|
||||
std::vector<short> *pv
|
||||
= static_cast<std::vector<short> *>(data_);
|
||||
std::vector<short> &v = *pv;
|
||||
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<short>::digits10 + 3;
|
||||
buf = new char[bufSize];
|
||||
snprintf(buf, bufSize, "%d", static_cast<int>(v[i]));
|
||||
}
|
||||
break;
|
||||
case x_integer:
|
||||
{
|
||||
std::vector<int> *pv
|
||||
= static_cast<std::vector<int> *>(data_);
|
||||
std::vector<int> &v = *pv;
|
||||
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<int>::digits10 + 3;
|
||||
buf = new char[bufSize];
|
||||
snprintf(buf, bufSize, "%d", v[i]);
|
||||
}
|
||||
break;
|
||||
case x_long_long:
|
||||
{
|
||||
std::vector<long long> *pv
|
||||
= static_cast<std::vector<long long> *>(data_);
|
||||
std::vector<long long> &v = *pv;
|
||||
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<long long>::digits10 + 3;
|
||||
buf = new char[bufSize];
|
||||
snprintf(buf, bufSize, "%" LL_FMT_FLAGS "d", v[i]);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
{
|
||||
std::vector<unsigned long long> *pv
|
||||
= static_cast<std::vector<unsigned long long> *>(data_);
|
||||
std::vector<unsigned long long> &v = *pv;
|
||||
|
||||
std::size_t const bufSize
|
||||
= std::numeric_limits<unsigned long long>::digits10 + 3;
|
||||
buf = new char[bufSize];
|
||||
snprintf(buf, bufSize, "%" LL_FMT_FLAGS "u", v[i]);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
{
|
||||
std::vector<double> *pv
|
||||
= static_cast<std::vector<double> *>(data_);
|
||||
std::vector<double> &v = *pv;
|
||||
|
||||
if (is_infinity_or_nan(v[i])) {
|
||||
throw soci_error(
|
||||
"Use element used with infinity or NaN, which are "
|
||||
"not supported by the MySQL server.");
|
||||
}
|
||||
|
||||
std::size_t const bufSize = 100;
|
||||
buf = new char[bufSize];
|
||||
|
||||
snprintf(buf, bufSize, "%.20g", v[i]);
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::vector<std::tm> *pv
|
||||
= static_cast<std::vector<std::tm> *>(data_);
|
||||
std::vector<std::tm> &v = *pv;
|
||||
|
||||
std::size_t const bufSize = 22;
|
||||
buf = new char[bufSize];
|
||||
|
||||
snprintf(buf, bufSize, "\'%d-%02d-%02d %02d:%02d:%02d\'",
|
||||
v[i].tm_year + 1900, v[i].tm_mon + 1, v[i].tm_mday,
|
||||
v[i].tm_hour, v[i].tm_min, v[i].tm_sec);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw soci_error(
|
||||
"Use vector element used with non-supported type.");
|
||||
}
|
||||
}
|
||||
|
||||
buffers_.push_back(buf);
|
||||
}
|
||||
|
||||
if (position_ > 0)
|
||||
{
|
||||
// binding by position
|
||||
statement_.useByPosBuffers_[position_] = &buffers_[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// binding by name
|
||||
statement_.useByNameBuffers_[name_] = &buffers_[0];
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t mysql_vector_use_type_backend::size()
|
||||
{
|
||||
std::size_t sz = 0; // dummy initialization to please the compiler
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_char: sz = get_vector_size<char> (data_); break;
|
||||
case x_short: sz = get_vector_size<short> (data_); break;
|
||||
case x_integer: sz = get_vector_size<int> (data_); break;
|
||||
case x_long_long: sz = get_vector_size<long long> (data_); break;
|
||||
case x_unsigned_long_long:
|
||||
sz = get_vector_size<unsigned long long>(data_);
|
||||
break;
|
||||
case x_double: sz = get_vector_size<double> (data_); break;
|
||||
case x_stdstring: sz = get_vector_size<std::string> (data_); break;
|
||||
case x_stdtm: sz = get_vector_size<std::tm> (data_); break;
|
||||
|
||||
default:
|
||||
throw soci_error("Use vector element used with non-supported type.");
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
void mysql_vector_use_type_backend::clean_up()
|
||||
{
|
||||
std::size_t const bsize = buffers_.size();
|
||||
for (std::size_t i = 0; i != bsize; ++i)
|
||||
{
|
||||
delete [] buffers_[i];
|
||||
}
|
||||
}
|
||||
18
src/backends/odbc/CMakeLists.txt
Normal file
18
src/backends/odbc/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
soci_backend(ODBC
|
||||
DEPENDS ODBC
|
||||
HEADERS soci-odbc.h utility.h
|
||||
DESCRIPTION "SOCI backend for ODBC"
|
||||
AUTHORS "Maciej Sobczak, Stephen Hutton, David Courtney"
|
||||
MAINTAINERS "Vadim Zeitlin, Mateusz Loskot, Maciej Sobczak")
|
||||
|
||||
add_subdirectory(test)
|
||||
89
src/backends/odbc/Makefile.basic
Normal file
89
src/backends/odbc/Makefile.basic
Normal file
@@ -0,0 +1,89 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
ODBCINCLUDEDIR = -I/usr/include
|
||||
|
||||
# The rest of the Makefile is indepentent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
CXXFLAGSSO = ${CXXFLAGS} -fPIC
|
||||
INCLUDEDIRS = -I../../core ${ODBCINCLUDEDIR}
|
||||
|
||||
|
||||
OBJECTS = blob.o factory.o row-id.o session.o standard-into-type.o \
|
||||
standard-use-type.o statement.o vector-into-type.o vector-use-type.o
|
||||
|
||||
OBJECTSSO = blob-s.o factory-s.o row-id-s.o session-s.o \
|
||||
standard-into-type-s.o standard-use-type-s.o statement-s.o \
|
||||
vector-into-type-s.o vector-use-type-s.o
|
||||
|
||||
|
||||
libsoci_odbc.a : ${OBJECTS}
|
||||
ar rv $@ $?
|
||||
ranlib $@
|
||||
rm *.o
|
||||
|
||||
|
||||
blob.o : blob.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
factory.o : factory.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
row-id.o : row-id.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
session.o : session.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type.o : standard-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type.o : standard-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
statement.o : statement.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type.o : vector-into-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type.o : vector-use-type.cpp
|
||||
${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS}
|
||||
|
||||
|
||||
shared : ${OBJECTSSO}
|
||||
${COMPILER} -shared -o libsoci_odbc.so ${OBJECTSSO}
|
||||
rm *.o
|
||||
|
||||
blob-s.o : blob.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
factory-s.o : factory.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
row-id-s.o : row-id.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
session-s.o : session.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-into-type-s.o : standard-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
standard-use-type-s.o : standard-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
statement-s.o : statement.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-into-type-s.o : vector-into-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
vector-use-type-s.o : vector-use-type.cpp
|
||||
${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS}
|
||||
|
||||
|
||||
clean :
|
||||
rm -f libsoci_odbc.a libsoci_odbc.so
|
||||
57
src/backends/odbc/blob.cpp
Normal file
57
src/backends/odbc/blob.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include "soci-odbc.h"
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
odbc_blob_backend::odbc_blob_backend(odbc_session_backend &session)
|
||||
: session_(session)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
odbc_blob_backend::~odbc_blob_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
std::size_t odbc_blob_backend::get_len()
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t odbc_blob_backend::read(
|
||||
std::size_t /* offset */, char * /* buf */, std::size_t /* toRead */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t odbc_blob_backend::write(
|
||||
std::size_t /* offset */, char const * /* buf */,
|
||||
std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t odbc_blob_backend::append(
|
||||
char const * /* buf */, std::size_t /* toWrite */)
|
||||
{
|
||||
// ...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void odbc_blob_backend::trim(std::size_t /* newLen */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
39
src/backends/odbc/factory.cpp
Normal file
39
src/backends/odbc/factory.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include "soci-odbc.h"
|
||||
#include <backend-loader.h>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
// concrete factory for ODBC concrete strategies
|
||||
odbc_session_backend * odbc_backend_factory::make_session(
|
||||
connection_parameters const & parameters) const
|
||||
{
|
||||
return new odbc_session_backend(parameters);
|
||||
}
|
||||
|
||||
odbc_backend_factory const soci::odbc;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_ODBC_DECL backend_factory const * factory_odbc()
|
||||
{
|
||||
return &soci::odbc;
|
||||
}
|
||||
|
||||
SOCI_ODBC_DECL void register_factory_odbc()
|
||||
{
|
||||
soci::dynamic_backends::register_backend("odbc", soci::odbc);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
48
src/backends/odbc/makefile.msvc
Normal file
48
src/backends/odbc/makefile.msvc
Normal file
@@ -0,0 +1,48 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
ODBCINCLUDEDIR="C:\Program Files\Microsoft Platform SDK\Include"
|
||||
|
||||
# The rest of the Makefile is indepentent of the target environment.
|
||||
|
||||
COMPILER = cl
|
||||
CXXFLAGS = /nologo /EHsc /D_CRT_SECURE_NO_DEPRECATE
|
||||
INCLUDEDIRS = /I..\..\core /I$(ODBCINCLUDEDIR)
|
||||
|
||||
OBJECTS = blob.obj factory.obj row-id.obj session.obj standard-into-type.obj \
|
||||
standard-use-type.obj statement.obj vector-into-type.obj \
|
||||
vector-use-type.obj
|
||||
|
||||
soci-odbc.lib : $(OBJECTS)
|
||||
lib /NOLOGO /OUT:$@ $?
|
||||
del *.obj
|
||||
|
||||
blob.obj : blob.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
factory.obj : factory.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
row-id.obj : row-id.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
session.obj : session.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
standard-into-type.obj : standard-into-type.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
standard-use-type.obj : standard-use-type.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
statement.obj : statement.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
vector-into-type.obj : vector-into-type.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
vector-use-type.obj : vector-use-type.cpp
|
||||
$(COMPILER) /c $? $(CXXFLAGS) $(INCLUDEDIRS)
|
||||
|
||||
clean :
|
||||
del soci-odbc.lib soci-odbc.dll
|
||||
23
src/backends/odbc/row-id.cpp
Normal file
23
src/backends/odbc/row-id.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include "soci-odbc.h"
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
odbc_rowid_backend::odbc_rowid_backend(odbc_session_backend & /* session */)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
odbc_rowid_backend::~odbc_rowid_backend()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
296
src/backends/odbc/session.cpp
Normal file
296
src/backends/odbc/session.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include "soci-odbc.h"
|
||||
#include "session.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
char const * soci::odbc_option_driver_complete = "odbc.driver_complete";
|
||||
|
||||
odbc_session_backend::odbc_session_backend(
|
||||
connection_parameters const & parameters)
|
||||
: henv_(0), hdbc_(0), product_(prod_uninitialized)
|
||||
{
|
||||
SQLRETURN rc;
|
||||
|
||||
// Allocate environment handle
|
||||
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw soci_error("Unable to get environment handle");
|
||||
}
|
||||
|
||||
// Set the ODBC version environment attribute
|
||||
rc = SQLSetEnvAttr(henv_, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_ENV, henv_,
|
||||
"Setting ODBC version");
|
||||
}
|
||||
|
||||
// Allocate connection handle
|
||||
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv_, &hdbc_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Allocating connection handle");
|
||||
}
|
||||
|
||||
SQLCHAR outConnString[1024];
|
||||
SQLSMALLINT strLength;
|
||||
|
||||
// Prompt the user for any missing information (typically UID/PWD) in the
|
||||
// connection string by default but allow overriding this using "prompt"
|
||||
// option.
|
||||
SQLHWND hwnd_for_prompt = NULL;
|
||||
unsigned completion = SQL_DRIVER_COMPLETE;
|
||||
std::string completionString;
|
||||
if (parameters.get_option(odbc_option_driver_complete, completionString))
|
||||
{
|
||||
// The value of the option is supposed to be just the integer value of
|
||||
// one of SQL_DRIVER_XXX constants but don't check for the exact value in
|
||||
// case more of them are added in the future, the ODBC driver will return
|
||||
// an error if we pass it an invalid value anyhow.
|
||||
if (std::sscanf(completionString.c_str(), "%u", &completion) != 1)
|
||||
{
|
||||
throw soci_error("Invalid non-numeric driver completion option value \"" +
|
||||
completionString + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (completion != SQL_DRIVER_NOPROMPT)
|
||||
hwnd_for_prompt = ::GetDesktopWindow();
|
||||
#endif // _WIN32
|
||||
|
||||
std::string const & connectString = parameters.get_connect_string();
|
||||
rc = SQLDriverConnect(hdbc_, hwnd_for_prompt,
|
||||
(SQLCHAR *)connectString.c_str(),
|
||||
(SQLSMALLINT)connectString.size(),
|
||||
outConnString, 1024, &strLength,
|
||||
static_cast<SQLUSMALLINT>(completion));
|
||||
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Error Connecting to database");
|
||||
}
|
||||
|
||||
connection_string_.assign((const char*)outConnString, strLength);
|
||||
|
||||
reset_transaction();
|
||||
}
|
||||
|
||||
odbc_session_backend::~odbc_session_backend()
|
||||
{
|
||||
clean_up();
|
||||
}
|
||||
|
||||
void odbc_session_backend::begin()
|
||||
{
|
||||
SQLRETURN rc = SQLSetConnectAttr( hdbc_, SQL_ATTR_AUTOCOMMIT,
|
||||
(SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0 );
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Begin Transaction");
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_session_backend::commit()
|
||||
{
|
||||
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_COMMIT);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Committing");
|
||||
}
|
||||
reset_transaction();
|
||||
}
|
||||
|
||||
void odbc_session_backend::rollback()
|
||||
{
|
||||
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_ROLLBACK);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Rolling back");
|
||||
}
|
||||
reset_transaction();
|
||||
}
|
||||
|
||||
bool odbc_session_backend::get_next_sequence_value(
|
||||
session & s, std::string const & sequence, long & value)
|
||||
{
|
||||
std::string query;
|
||||
|
||||
switch ( get_database_product() )
|
||||
{
|
||||
case prod_firebird:
|
||||
query = "select next value for " + sequence + " from rdb$database";
|
||||
break;
|
||||
|
||||
case prod_oracle:
|
||||
query = "select " + sequence + ".nextval from dual";
|
||||
break;
|
||||
|
||||
case prod_postgresql:
|
||||
query = "select nextval('" + sequence + "')";
|
||||
break;
|
||||
|
||||
case prod_mssql:
|
||||
case prod_mysql:
|
||||
case prod_sqlite:
|
||||
// These RDBMS implement get_last_insert_id() instead.
|
||||
return false;
|
||||
|
||||
case prod_unknown:
|
||||
// For this one we can't do anything at all.
|
||||
return false;
|
||||
|
||||
case prod_uninitialized:
|
||||
// This is not supposed to happen at all but still cover this case
|
||||
// here to avoid gcc warnings about unhandled enum values in a
|
||||
// switch.
|
||||
return false;
|
||||
}
|
||||
|
||||
s << query, into(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool odbc_session_backend::get_last_insert_id(
|
||||
session & s, std::string const & table, long & value)
|
||||
{
|
||||
std::string query;
|
||||
|
||||
switch ( get_database_product() )
|
||||
{
|
||||
case prod_mssql:
|
||||
query = "select ident_current('" + table + "')";
|
||||
break;
|
||||
|
||||
case prod_mysql:
|
||||
query = "select last_insert_id()";
|
||||
break;
|
||||
|
||||
case prod_sqlite:
|
||||
query = "select last_insert_rowid()";
|
||||
break;
|
||||
|
||||
case prod_firebird:
|
||||
case prod_oracle:
|
||||
case prod_postgresql:
|
||||
// For these RDBMS get_next_sequence_value() should have been used.
|
||||
return false;
|
||||
|
||||
|
||||
case prod_unknown:
|
||||
// For this one we can't do anything at all.
|
||||
return false;
|
||||
|
||||
case prod_uninitialized:
|
||||
// As above, this is not supposed to happen but put it here to
|
||||
// mollify gcc.
|
||||
return false;
|
||||
}
|
||||
|
||||
s << query, into(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void odbc_session_backend::reset_transaction()
|
||||
{
|
||||
SQLRETURN rc = SQLSetConnectAttr( hdbc_, SQL_ATTR_AUTOCOMMIT,
|
||||
(SQLPOINTER)SQL_AUTOCOMMIT_ON, 0 );
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"Set Auto Commit");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void odbc_session_backend::clean_up()
|
||||
{
|
||||
SQLRETURN rc = SQLDisconnect(hdbc_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"SQLDisconnect");
|
||||
}
|
||||
|
||||
rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
|
||||
"SQLFreeHandle DBC");
|
||||
}
|
||||
|
||||
rc = SQLFreeHandle(SQL_HANDLE_ENV, henv_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_ENV, henv_,
|
||||
"SQLFreeHandle ENV");
|
||||
}
|
||||
}
|
||||
|
||||
odbc_statement_backend * odbc_session_backend::make_statement_backend()
|
||||
{
|
||||
return new odbc_statement_backend(*this);
|
||||
}
|
||||
|
||||
odbc_rowid_backend * odbc_session_backend::make_rowid_backend()
|
||||
{
|
||||
return new odbc_rowid_backend(*this);
|
||||
}
|
||||
|
||||
odbc_blob_backend * odbc_session_backend::make_blob_backend()
|
||||
{
|
||||
return new odbc_blob_backend(*this);
|
||||
}
|
||||
|
||||
odbc_session_backend::database_product
|
||||
odbc_session_backend::get_database_product()
|
||||
{
|
||||
// Cache the product type, it's not going to change during our life time.
|
||||
if (product_ != prod_uninitialized)
|
||||
return product_;
|
||||
|
||||
char product_name[1024];
|
||||
SQLSMALLINT len = sizeof(product_name);
|
||||
SQLRETURN rc = SQLGetInfo(hdbc_, SQL_DBMS_NAME, product_name, len, &len);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, henv_,
|
||||
"SQLGetInfo(SQL_DBMS_NAME)");
|
||||
}
|
||||
|
||||
if (strcmp(product_name, "Firebird") == 0)
|
||||
product_ = prod_firebird;
|
||||
else if (strcmp(product_name, "Microsoft SQL Server") == 0)
|
||||
product_ = prod_mssql;
|
||||
else if (strcmp(product_name, "MySQL") == 0)
|
||||
product_ = prod_mysql;
|
||||
else if (strcmp(product_name, "Oracle") == 0)
|
||||
product_ = prod_oracle;
|
||||
else if (strcmp(product_name, "PostgreSQL") == 0)
|
||||
product_ = prod_postgresql;
|
||||
else if (strcmp(product_name, "SQLite") == 0)
|
||||
product_ = prod_sqlite;
|
||||
else
|
||||
product_ = prod_unknown;
|
||||
|
||||
return product_;
|
||||
}
|
||||
432
src/backends/odbc/soci-odbc.h
Normal file
432
src/backends/odbc/soci-odbc.h
Normal file
@@ -0,0 +1,432 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef SOCI_ODBC_H_INCLUDED
|
||||
#define SOCI_ODBC_H_INCLUDED
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef SOCI_DLL
|
||||
# ifdef SOCI_ODBC_SOURCE
|
||||
# define SOCI_ODBC_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define SOCI_ODBC_DECL __declspec(dllimport)
|
||||
# endif // SOCI_ODBC_SOURCE
|
||||
# endif // SOCI_DLL
|
||||
#endif // _WIN32
|
||||
//
|
||||
// If SOCI_ODBC_DECL isn't defined yet define it now
|
||||
#ifndef SOCI_ODBC_DECL
|
||||
# define SOCI_ODBC_DECL
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <soci-backend.h>
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
#include <soci-platform.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <sqlext.h> // ODBC
|
||||
#include <string.h> // strcpy()
|
||||
|
||||
namespace soci
|
||||
{
|
||||
|
||||
// TODO: Do we want to make it a part of public interface? --mloskot
|
||||
namespace details
|
||||
{
|
||||
std::size_t const odbc_max_buffer_length = 100 * 1024 * 1024;
|
||||
}
|
||||
|
||||
// Option allowing to specify the "driver completion" parameter of
|
||||
// SQLDriverConnect(). Its possible values are the same as the allowed values
|
||||
// for this parameter in the official ODBC, i.e. one of SQL_DRIVER_XXX (in
|
||||
// string form as all options are strings currently).
|
||||
extern SOCI_ODBC_DECL char const * odbc_option_driver_complete;
|
||||
|
||||
struct odbc_statement_backend;
|
||||
|
||||
// Helper of into and use backends.
|
||||
class odbc_standard_type_backend_base
|
||||
{
|
||||
protected:
|
||||
odbc_standard_type_backend_base(odbc_statement_backend &st)
|
||||
: statement_(st) {}
|
||||
|
||||
// Check if we need to pass 64 bit integers as strings to the database as
|
||||
// some drivers don't support them directly.
|
||||
inline bool use_string_for_bigint() const;
|
||||
|
||||
// If we do need to use strings for 64 bit integers, this constant defines
|
||||
// the maximal string length needed.
|
||||
enum
|
||||
{
|
||||
// This is the length of decimal representation of UINT64_MAX + 1.
|
||||
max_bigint_length = 21
|
||||
};
|
||||
|
||||
odbc_statement_backend &statement_;
|
||||
};
|
||||
|
||||
struct odbc_standard_into_type_backend : details::standard_into_type_backend,
|
||||
private odbc_standard_type_backend_base
|
||||
{
|
||||
odbc_standard_into_type_backend(odbc_statement_backend &st)
|
||||
: odbc_standard_type_backend_base(st), buf_(0)
|
||||
{}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, bool calledFromFetch,
|
||||
indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
char *buf_; // generic buffer
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
int position_;
|
||||
SQLSMALLINT odbcType_;
|
||||
SQLLEN valueLen_;
|
||||
};
|
||||
|
||||
struct odbc_vector_into_type_backend : details::vector_into_type_backend,
|
||||
private odbc_standard_type_backend_base
|
||||
{
|
||||
odbc_vector_into_type_backend(odbc_statement_backend &st)
|
||||
: odbc_standard_type_backend_base(st), indHolders_(NULL),
|
||||
data_(NULL), buf_(NULL) {}
|
||||
|
||||
virtual void define_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_fetch();
|
||||
virtual void post_fetch(bool gotData, indicator *ind);
|
||||
|
||||
virtual void resize(std::size_t sz);
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
// helper function for preparing indicators
|
||||
// (as part of the define_by_pos)
|
||||
void prepare_indicators(std::size_t size);
|
||||
|
||||
|
||||
SQLLEN *indHolders_;
|
||||
std::vector<SQLLEN> indHolderVec_;
|
||||
void *data_;
|
||||
char *buf_; // generic buffer
|
||||
details::exchange_type type_;
|
||||
std::size_t colSize_; // size of the string column (used for strings)
|
||||
SQLSMALLINT odbcType_;
|
||||
};
|
||||
|
||||
struct odbc_standard_use_type_backend : details::standard_use_type_backend,
|
||||
private odbc_standard_type_backend_base
|
||||
{
|
||||
odbc_standard_use_type_backend(odbc_statement_backend &st)
|
||||
: odbc_standard_type_backend_base(st),
|
||||
position_(-1), data_(0), buf_(0), indHolder_(0) {}
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type, bool readOnly);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
virtual void post_use(bool gotData, indicator *ind);
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
// Return the pointer to the buffer containing data to be used by ODBC.
|
||||
// This can be either data_ itself or buf_, that is allocated by this
|
||||
// function if necessary.
|
||||
//
|
||||
// Also fill in the size of the data and SQL and C types of it.
|
||||
void* prepare_for_bind(SQLLEN &size,
|
||||
SQLSMALLINT &sqlType, SQLSMALLINT &cType);
|
||||
|
||||
int position_;
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
char *buf_;
|
||||
SQLLEN indHolder_;
|
||||
};
|
||||
|
||||
struct odbc_vector_use_type_backend : details::vector_use_type_backend,
|
||||
private odbc_standard_type_backend_base
|
||||
{
|
||||
odbc_vector_use_type_backend(odbc_statement_backend &st)
|
||||
: odbc_standard_type_backend_base(st), indHolders_(NULL),
|
||||
data_(NULL), buf_(NULL) {}
|
||||
|
||||
// helper function for preparing indicators
|
||||
// (as part of the define_by_pos)
|
||||
void prepare_indicators(std::size_t size);
|
||||
|
||||
// common part for bind_by_pos and bind_by_name
|
||||
void prepare_for_bind(void *&data, SQLUINTEGER &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType);
|
||||
void bind_helper(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void bind_by_pos(int &position,
|
||||
void *data, details::exchange_type type);
|
||||
virtual void bind_by_name(std::string const &name,
|
||||
void *data, details::exchange_type type);
|
||||
|
||||
virtual void pre_use(indicator const *ind);
|
||||
|
||||
virtual std::size_t size();
|
||||
|
||||
virtual void clean_up();
|
||||
|
||||
|
||||
SQLLEN *indHolders_;
|
||||
std::vector<SQLLEN> indHolderVec_;
|
||||
void *data_;
|
||||
details::exchange_type type_;
|
||||
char *buf_; // generic buffer
|
||||
std::size_t colSize_; // size of the string column (used for strings)
|
||||
// used for strings only
|
||||
std::size_t maxSize_;
|
||||
};
|
||||
|
||||
struct odbc_session_backend;
|
||||
struct odbc_statement_backend : details::statement_backend
|
||||
{
|
||||
odbc_statement_backend(odbc_session_backend &session);
|
||||
|
||||
virtual void alloc();
|
||||
virtual void clean_up();
|
||||
virtual void prepare(std::string const &query,
|
||||
details::statement_type eType);
|
||||
|
||||
virtual exec_fetch_result execute(int number);
|
||||
virtual exec_fetch_result fetch(int number);
|
||||
|
||||
virtual long long get_affected_rows();
|
||||
virtual int get_number_of_rows();
|
||||
|
||||
virtual std::string rewrite_for_procedure_call(std::string const &query);
|
||||
|
||||
virtual int prepare_for_describe();
|
||||
virtual void describe_column(int colNum, data_type &dtype,
|
||||
std::string &columnName);
|
||||
|
||||
// helper for defining into vector<string>
|
||||
std::size_t column_size(int position);
|
||||
|
||||
virtual odbc_standard_into_type_backend * make_into_type_backend();
|
||||
virtual odbc_standard_use_type_backend * make_use_type_backend();
|
||||
virtual odbc_vector_into_type_backend * make_vector_into_type_backend();
|
||||
virtual odbc_vector_use_type_backend * make_vector_use_type_backend();
|
||||
|
||||
odbc_session_backend &session_;
|
||||
SQLHSTMT hstmt_;
|
||||
SQLULEN numRowsFetched_;
|
||||
bool hasVectorUseElements_;
|
||||
bool boundByName_;
|
||||
bool boundByPos_;
|
||||
|
||||
long long rowsAffected_; // number of rows affected by the last operation
|
||||
|
||||
std::string query_;
|
||||
std::vector<std::string> names_; // list of names for named binds
|
||||
|
||||
};
|
||||
|
||||
struct odbc_rowid_backend : details::rowid_backend
|
||||
{
|
||||
odbc_rowid_backend(odbc_session_backend &session);
|
||||
|
||||
~odbc_rowid_backend();
|
||||
};
|
||||
|
||||
struct odbc_blob_backend : details::blob_backend
|
||||
{
|
||||
odbc_blob_backend(odbc_session_backend &session);
|
||||
|
||||
~odbc_blob_backend();
|
||||
|
||||
virtual std::size_t get_len();
|
||||
virtual std::size_t read(std::size_t offset, char *buf,
|
||||
std::size_t toRead);
|
||||
virtual std::size_t write(std::size_t offset, char const *buf,
|
||||
std::size_t toWrite);
|
||||
virtual std::size_t append(char const *buf, std::size_t toWrite);
|
||||
virtual void trim(std::size_t newLen);
|
||||
|
||||
odbc_session_backend &session_;
|
||||
};
|
||||
|
||||
struct odbc_session_backend : details::session_backend
|
||||
{
|
||||
odbc_session_backend(connection_parameters const & parameters);
|
||||
|
||||
~odbc_session_backend();
|
||||
|
||||
virtual void begin();
|
||||
virtual void commit();
|
||||
virtual void rollback();
|
||||
|
||||
virtual bool get_next_sequence_value(session & s,
|
||||
std::string const & sequence, long & value);
|
||||
virtual bool get_last_insert_id(session & s,
|
||||
std::string const & table, long & value);
|
||||
|
||||
virtual std::string get_backend_name() const { return "odbc"; }
|
||||
|
||||
void reset_transaction();
|
||||
|
||||
void clean_up();
|
||||
|
||||
virtual odbc_statement_backend * make_statement_backend();
|
||||
virtual odbc_rowid_backend * make_rowid_backend();
|
||||
virtual odbc_blob_backend * make_blob_backend();
|
||||
|
||||
enum database_product
|
||||
{
|
||||
prod_uninitialized, // Never returned by get_database_product().
|
||||
prod_firebird,
|
||||
prod_mssql,
|
||||
prod_mysql,
|
||||
prod_oracle,
|
||||
prod_postgresql,
|
||||
prod_sqlite,
|
||||
prod_unknown = -1
|
||||
};
|
||||
|
||||
// Determine the type of the database we're connected to.
|
||||
database_product get_database_product();
|
||||
|
||||
// Return full ODBC connection string.
|
||||
std::string get_connection_string() const { return connection_string_; }
|
||||
|
||||
SQLHENV henv_;
|
||||
SQLHDBC hdbc_;
|
||||
|
||||
std::string connection_string_;
|
||||
database_product product_;
|
||||
};
|
||||
|
||||
class SOCI_ODBC_DECL odbc_soci_error : public soci_error
|
||||
{
|
||||
SQLCHAR message_[SQL_MAX_MESSAGE_LENGTH + 1];
|
||||
SQLCHAR sqlstate_[SQL_SQLSTATE_SIZE + 1];
|
||||
SQLINTEGER sqlcode_;
|
||||
|
||||
public:
|
||||
odbc_soci_error(SQLSMALLINT htype,
|
||||
SQLHANDLE hndl,
|
||||
std::string const & msg)
|
||||
: soci_error(msg)
|
||||
{
|
||||
const char* socierror = NULL;
|
||||
|
||||
SQLSMALLINT length, i = 1;
|
||||
switch ( SQLGetDiagRecA(htype, hndl, i, sqlstate_, &sqlcode_,
|
||||
message_, SQL_MAX_MESSAGE_LENGTH + 1,
|
||||
&length) )
|
||||
{
|
||||
case SQL_SUCCESS:
|
||||
// The error message was successfully retrieved.
|
||||
break;
|
||||
|
||||
case SQL_INVALID_HANDLE:
|
||||
socierror = "[SOCI]: Invalid handle.";
|
||||
break;
|
||||
|
||||
case SQL_ERROR:
|
||||
socierror = "[SOCI]: SQLGetDiagRec() error.";
|
||||
break;
|
||||
|
||||
case SQL_SUCCESS_WITH_INFO:
|
||||
socierror = "[SOCI]: Error message too long.";
|
||||
break;
|
||||
|
||||
case SQL_NO_DATA:
|
||||
socierror = "[SOCI]: No error.";
|
||||
break;
|
||||
|
||||
default:
|
||||
socierror = "[SOCI]: Unexpected SQLGetDiagRec() return value.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (socierror)
|
||||
{
|
||||
// Use our own error message if we failed to retrieve the ODBC one.
|
||||
strcpy(reinterpret_cast<char*>(message_), socierror);
|
||||
|
||||
// Use "General warning" SQLSTATE code.
|
||||
strcpy(reinterpret_cast<char*>(sqlstate_), "01000");
|
||||
|
||||
sqlcode_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SQLCHAR const * odbc_error_code() const
|
||||
{
|
||||
return reinterpret_cast<SQLCHAR const *>(sqlstate_);
|
||||
}
|
||||
SQLINTEGER native_error_code() const
|
||||
{
|
||||
return sqlcode_;
|
||||
}
|
||||
SQLCHAR const * odbc_error_message() const
|
||||
{
|
||||
return reinterpret_cast<SQLCHAR const *>(message_);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool is_odbc_error(SQLRETURN rc)
|
||||
{
|
||||
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO && rc != SQL_NO_DATA)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool odbc_standard_type_backend_base::use_string_for_bigint() const
|
||||
{
|
||||
// Oracle ODBC driver doesn't support SQL_C_[SU]BIGINT data types
|
||||
// (see appendix G.1 of Oracle Database Administrator's reference at
|
||||
// http://docs.oracle.com/cd/B19306_01/server.102/b15658/app_odbc.htm),
|
||||
// so we need a special workaround for this case and we represent 64
|
||||
// bit integers as strings and rely on ODBC driver for transforming
|
||||
// them to SQL_NUMERIC.
|
||||
return statement_.session_.get_database_product()
|
||||
== odbc_session_backend::prod_oracle;
|
||||
}
|
||||
|
||||
struct odbc_backend_factory : backend_factory
|
||||
{
|
||||
odbc_backend_factory() {}
|
||||
virtual odbc_session_backend * make_session(
|
||||
connection_parameters const & parameters) const;
|
||||
};
|
||||
|
||||
extern SOCI_ODBC_DECL odbc_backend_factory const odbc;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// for dynamic backend loading
|
||||
SOCI_ODBC_DECL backend_factory const * factory_odbc();
|
||||
SOCI_ODBC_DECL void register_factory_odbc();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace soci
|
||||
|
||||
#endif // SOCI_EMPTY_H_INCLUDED
|
||||
203
src/backends/odbc/standard-into-type.cpp
Normal file
203
src/backends/odbc/standard-into-type.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include <soci-platform.h>
|
||||
#include "soci-odbc.h"
|
||||
#include <ctime>
|
||||
#include <stdio.h> // sscanf()
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
void odbc_standard_into_type_backend::define_by_pos(
|
||||
int & position, void * data, exchange_type type)
|
||||
{
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
position_ = position++;
|
||||
|
||||
SQLUINTEGER size = 0;
|
||||
|
||||
switch (type_)
|
||||
{
|
||||
case x_char:
|
||||
odbcType_ = SQL_C_CHAR;
|
||||
size = sizeof(char) + 1;
|
||||
buf_ = new char[size];
|
||||
data = buf_;
|
||||
break;
|
||||
case x_stdstring:
|
||||
odbcType_ = SQL_C_CHAR;
|
||||
// Patch: set to min between column size and 100MB (used ot be 32769)
|
||||
// Column size for text data type can be too large for buffer allocation
|
||||
size = statement_.column_size(position_);
|
||||
size = size > odbc_max_buffer_length ? odbc_max_buffer_length : size;
|
||||
size++;
|
||||
buf_ = new char[size];
|
||||
data = buf_;
|
||||
break;
|
||||
case x_short:
|
||||
odbcType_ = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
break;
|
||||
case x_integer:
|
||||
odbcType_ = SQL_C_SLONG;
|
||||
size = sizeof(int);
|
||||
break;
|
||||
case x_long_long:
|
||||
if (use_string_for_bigint())
|
||||
{
|
||||
odbcType_ = SQL_C_CHAR;
|
||||
size = max_bigint_length;
|
||||
buf_ = new char[size];
|
||||
data = buf_;
|
||||
}
|
||||
else // Normal case, use ODBC support.
|
||||
{
|
||||
odbcType_ = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
if (use_string_for_bigint())
|
||||
{
|
||||
odbcType_ = SQL_C_CHAR;
|
||||
size = max_bigint_length;
|
||||
buf_ = new char[size];
|
||||
data = buf_;
|
||||
}
|
||||
else // Normal case, use ODBC support.
|
||||
{
|
||||
odbcType_ = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
odbcType_ = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
break;
|
||||
case x_stdtm:
|
||||
odbcType_ = SQL_C_TYPE_TIMESTAMP;
|
||||
size = sizeof(TIMESTAMP_STRUCT);
|
||||
buf_ = new char[size];
|
||||
data = buf_;
|
||||
break;
|
||||
case x_rowid:
|
||||
odbcType_ = SQL_C_ULONG;
|
||||
size = sizeof(unsigned long);
|
||||
break;
|
||||
default:
|
||||
throw soci_error("Into element used with non-supported type.");
|
||||
}
|
||||
|
||||
valueLen_ = 0;
|
||||
|
||||
SQLRETURN rc = SQLBindCol(statement_.hstmt_, static_cast<SQLUSMALLINT>(position_),
|
||||
static_cast<SQLUSMALLINT>(odbcType_), data, size, &valueLen_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, statement_.hstmt_,
|
||||
"into type pre_fetch");
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_standard_into_type_backend::pre_fetch()
|
||||
{
|
||||
//...
|
||||
}
|
||||
|
||||
void odbc_standard_into_type_backend::post_fetch(
|
||||
bool gotData, bool calledFromFetch, indicator * ind)
|
||||
{
|
||||
if (calledFromFetch == true && gotData == false)
|
||||
{
|
||||
// this is a normal end-of-rowset condition,
|
||||
// no need to do anything (fetch() will return false)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gotData)
|
||||
{
|
||||
// first, deal with indicators
|
||||
if (SQL_NULL_DATA == valueLen_)
|
||||
{
|
||||
if (ind == NULL)
|
||||
{
|
||||
throw soci_error(
|
||||
"Null value fetched and no indicator defined.");
|
||||
}
|
||||
|
||||
*ind = i_null;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ind != NULL)
|
||||
{
|
||||
*ind = i_ok;
|
||||
}
|
||||
}
|
||||
|
||||
// only std::string and std::tm need special handling
|
||||
if (type_ == x_char)
|
||||
{
|
||||
char *c = static_cast<char*>(data_);
|
||||
*c = buf_[0];
|
||||
}
|
||||
if (type_ == x_stdstring)
|
||||
{
|
||||
std::string *s = static_cast<std::string *>(data_);
|
||||
*s = buf_;
|
||||
if (s->size() >= (odbc_max_buffer_length - 1))
|
||||
{
|
||||
throw soci_error("Buffer size overflow; maybe got too large string");
|
||||
}
|
||||
}
|
||||
else if (type_ == x_stdtm)
|
||||
{
|
||||
std::tm *t = static_cast<std::tm *>(data_);
|
||||
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf_);
|
||||
t->tm_isdst = -1;
|
||||
t->tm_year = ts->year - 1900;
|
||||
t->tm_mon = ts->month - 1;
|
||||
t->tm_mday = ts->day;
|
||||
t->tm_hour = ts->hour;
|
||||
t->tm_min = ts->minute;
|
||||
t->tm_sec = ts->second;
|
||||
|
||||
// normalize and compute the remaining fields
|
||||
std::mktime(t);
|
||||
}
|
||||
else if (type_ == x_long_long && use_string_for_bigint())
|
||||
{
|
||||
long long *ll = static_cast<long long *>(data_);
|
||||
if (sscanf(buf_, "%" LL_FMT_FLAGS "d", ll) != 1)
|
||||
{
|
||||
throw soci_error("Failed to parse the returned 64-bit integer value");
|
||||
}
|
||||
}
|
||||
else if (type_ == x_unsigned_long_long && use_string_for_bigint())
|
||||
{
|
||||
unsigned long long *ll = static_cast<unsigned long long *>(data_);
|
||||
if (sscanf(buf_, "%" LL_FMT_FLAGS "u", ll) != 1)
|
||||
{
|
||||
throw soci_error("Failed to parse the returned 64-bit integer value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_standard_into_type_backend::clean_up()
|
||||
{
|
||||
if (buf_)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = 0;
|
||||
}
|
||||
}
|
||||
262
src/backends/odbc/standard-use-type.cpp
Normal file
262
src/backends/odbc/standard-use-type.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include <soci-platform.h>
|
||||
#include "soci-odbc.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
void* odbc_standard_use_type_backend::prepare_for_bind(
|
||||
SQLLEN &size, SQLSMALLINT &sqlType, SQLSMALLINT &cType)
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
// simple cases
|
||||
case x_short:
|
||||
sqlType = SQL_SMALLINT;
|
||||
cType = SQL_C_SSHORT;
|
||||
size = sizeof(short);
|
||||
break;
|
||||
case x_integer:
|
||||
sqlType = SQL_INTEGER;
|
||||
cType = SQL_C_SLONG;
|
||||
size = sizeof(int);
|
||||
break;
|
||||
case x_long_long:
|
||||
if (use_string_for_bigint())
|
||||
{
|
||||
sqlType = SQL_NUMERIC;
|
||||
cType = SQL_C_CHAR;
|
||||
size = max_bigint_length;
|
||||
buf_ = new char[size];
|
||||
snprintf(buf_, size, "%" LL_FMT_FLAGS "d",
|
||||
*static_cast<long long *>(data_));
|
||||
indHolder_ = SQL_NTS;
|
||||
}
|
||||
else // Normal case, use ODBC support.
|
||||
{
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_SBIGINT;
|
||||
size = sizeof(long long);
|
||||
}
|
||||
break;
|
||||
case x_unsigned_long_long:
|
||||
if (use_string_for_bigint())
|
||||
{
|
||||
sqlType = SQL_NUMERIC;
|
||||
cType = SQL_C_CHAR;
|
||||
size = max_bigint_length;
|
||||
buf_ = new char[size];
|
||||
snprintf(buf_, size, "%" LL_FMT_FLAGS "u",
|
||||
*static_cast<unsigned long long *>(data_));
|
||||
indHolder_ = SQL_NTS;
|
||||
}
|
||||
else // Normal case, use ODBC support.
|
||||
{
|
||||
sqlType = SQL_BIGINT;
|
||||
cType = SQL_C_UBIGINT;
|
||||
size = sizeof(unsigned long long);
|
||||
}
|
||||
break;
|
||||
case x_double:
|
||||
sqlType = SQL_DOUBLE;
|
||||
cType = SQL_C_DOUBLE;
|
||||
size = sizeof(double);
|
||||
break;
|
||||
|
||||
case x_char:
|
||||
sqlType = SQL_CHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
size = 2;
|
||||
buf_ = new char[size];
|
||||
buf_[0] = *static_cast<char*>(data_);
|
||||
buf_[1] = '\0';
|
||||
indHolder_ = SQL_NTS;
|
||||
break;
|
||||
case x_stdstring:
|
||||
{
|
||||
std::string* s = static_cast<std::string*>(data_);
|
||||
sqlType = SQL_VARCHAR;
|
||||
cType = SQL_C_CHAR;
|
||||
size = s->size();
|
||||
buf_ = new char[size+1];
|
||||
memcpy(buf_, s->c_str(), size);
|
||||
buf_[size++] = '\0';
|
||||
indHolder_ = SQL_NTS;
|
||||
}
|
||||
break;
|
||||
case x_stdtm:
|
||||
{
|
||||
std::tm *t = static_cast<std::tm *>(data_);
|
||||
|
||||
sqlType = SQL_TIMESTAMP;
|
||||
cType = SQL_C_TIMESTAMP;
|
||||
buf_ = new char[sizeof(TIMESTAMP_STRUCT)];
|
||||
size = 19; // This number is not the size in bytes, but the number
|
||||
// of characters in the date if it was written out
|
||||
// yyyy-mm-dd hh:mm:ss
|
||||
|
||||
TIMESTAMP_STRUCT * ts = reinterpret_cast<TIMESTAMP_STRUCT*>(buf_);
|
||||
|
||||
ts->year = static_cast<SQLSMALLINT>(t->tm_year + 1900);
|
||||
ts->month = static_cast<SQLUSMALLINT>(t->tm_mon + 1);
|
||||
ts->day = static_cast<SQLUSMALLINT>(t->tm_mday);
|
||||
ts->hour = static_cast<SQLUSMALLINT>(t->tm_hour);
|
||||
ts->minute = static_cast<SQLUSMALLINT>(t->tm_min);
|
||||
ts->second = static_cast<SQLUSMALLINT>(t->tm_sec);
|
||||
ts->fraction = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case x_blob:
|
||||
{
|
||||
// sqlType = SQL_VARBINARY;
|
||||
// cType = SQL_C_BINARY;
|
||||
|
||||
// BLOB *b = static_cast<BLOB *>(data);
|
||||
|
||||
// odbc_blob_backend *bbe
|
||||
// = static_cast<odbc_blob_backend *>(b->getBackEnd());
|
||||
|
||||
// size = 0;
|
||||
// indHolder_ = size;
|
||||
//TODO data = &bbe->lobp_;
|
||||
}
|
||||
break;
|
||||
case x_statement:
|
||||
case x_rowid:
|
||||
// Unsupported data types.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return either the pointer to C++ data itself or the buffer that we
|
||||
// allocated, if any.
|
||||
return buf_ ? buf_ : data_;
|
||||
}
|
||||
|
||||
void odbc_standard_use_type_backend::bind_by_pos(
|
||||
int &position, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.boundByName_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
position_ = position++;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
statement_.boundByPos_ = true;
|
||||
}
|
||||
|
||||
void odbc_standard_use_type_backend::bind_by_name(
|
||||
std::string const &name, void *data, exchange_type type, bool /* readOnly */)
|
||||
{
|
||||
if (statement_.boundByPos_)
|
||||
{
|
||||
throw soci_error(
|
||||
"Binding for use elements must be either by position or by name.");
|
||||
}
|
||||
|
||||
int position = -1;
|
||||
int count = 1;
|
||||
|
||||
for (std::vector<std::string>::iterator it = statement_.names_.begin();
|
||||
it != statement_.names_.end(); ++it)
|
||||
{
|
||||
if (*it == name)
|
||||
{
|
||||
position = count;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (position == -1)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Unable to find name '" << name << "' to bind to";
|
||||
throw soci_error(ss.str().c_str());
|
||||
}
|
||||
|
||||
position_ = position;
|
||||
data_ = data;
|
||||
type_ = type;
|
||||
|
||||
statement_.boundByName_ = true;
|
||||
}
|
||||
|
||||
void odbc_standard_use_type_backend::pre_use(indicator const *ind)
|
||||
{
|
||||
// first deal with data
|
||||
SQLSMALLINT sqlType;
|
||||
SQLSMALLINT cType;
|
||||
SQLLEN size;
|
||||
|
||||
void* const sqlData = prepare_for_bind(size, sqlType, cType);
|
||||
|
||||
SQLRETURN rc = SQLBindParameter(statement_.hstmt_,
|
||||
static_cast<SQLUSMALLINT>(position_),
|
||||
SQL_PARAM_INPUT,
|
||||
cType, sqlType, size, 0,
|
||||
sqlData, 0, &indHolder_);
|
||||
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, statement_.hstmt_,
|
||||
"Binding");
|
||||
}
|
||||
|
||||
// then handle indicators
|
||||
if (ind != NULL && *ind == i_null)
|
||||
{
|
||||
indHolder_ = SQL_NULL_DATA; // null
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_standard_use_type_backend::post_use(bool gotData, indicator *ind)
|
||||
{
|
||||
if (ind != NULL)
|
||||
{
|
||||
if (gotData)
|
||||
{
|
||||
if (indHolder_ == 0)
|
||||
{
|
||||
*ind = i_ok;
|
||||
}
|
||||
else if (indHolder_ == SQL_NULL_DATA)
|
||||
{
|
||||
*ind = i_null;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ind = i_truncated;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_standard_use_type_backend::clean_up()
|
||||
{
|
||||
if (buf_ != NULL)
|
||||
{
|
||||
delete [] buf_;
|
||||
buf_ = NULL;
|
||||
}
|
||||
}
|
||||
360
src/backends/odbc/statement.cpp
Normal file
360
src/backends/odbc/statement.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#define SOCI_ODBC_SOURCE
|
||||
#include "soci-odbc.h"
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disables the warning about converting int to void*. This is a 64 bit compatibility
|
||||
// warning, but odbc requires the value to be converted on this line
|
||||
// SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)number, 0);
|
||||
#pragma warning(disable:4312)
|
||||
#endif
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::details;
|
||||
|
||||
|
||||
odbc_statement_backend::odbc_statement_backend(odbc_session_backend &session)
|
||||
: session_(session), hstmt_(0), numRowsFetched_(0),
|
||||
hasVectorUseElements_(false), boundByName_(false), boundByPos_(false),
|
||||
rowsAffected_(-1LL)
|
||||
{
|
||||
}
|
||||
|
||||
void odbc_statement_backend::alloc()
|
||||
{
|
||||
SQLRETURN rc;
|
||||
|
||||
// Allocate environment handle
|
||||
rc = SQLAllocHandle(SQL_HANDLE_STMT, session_.hdbc_, &hstmt_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_DBC, session_.hdbc_,
|
||||
"Allocating statement");
|
||||
}
|
||||
}
|
||||
|
||||
void odbc_statement_backend::clean_up()
|
||||
{
|
||||
rowsAffected_ = -1LL;
|
||||
|
||||
SQLFreeHandle(SQL_HANDLE_STMT, hstmt_);
|
||||
}
|
||||
|
||||
|
||||
void odbc_statement_backend::prepare(std::string const & query,
|
||||
statement_type /* eType */)
|
||||
{
|
||||
// rewrite the query by transforming all named parameters into
|
||||
// the ODBC numbers ones (:abc -> $1, etc.)
|
||||
|
||||
enum { eNormal, eInQuotes, eInName, eInAccessDate } state = eNormal;
|
||||
|
||||
std::string name;
|
||||
query_.reserve(query.length());
|
||||
|
||||
for (std::string::const_iterator it = query.begin(), end = query.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case eNormal:
|
||||
if (*it == '\'')
|
||||
{
|
||||
query_ += *it;
|
||||
state = eInQuotes;
|
||||
}
|
||||
else if (*it == '#')
|
||||
{
|
||||
query_ += *it;
|
||||
state = eInAccessDate;
|
||||
}
|
||||
else if (*it == ':')
|
||||
{
|
||||
state = eInName;
|
||||
}
|
||||
else // regular character, stay in the same state
|
||||
{
|
||||
query_ += *it;
|
||||
}
|
||||
break;
|
||||
case eInQuotes:
|
||||
if (*it == '\'')
|
||||
{
|
||||
query_ += *it;
|
||||
state = eNormal;
|
||||
}
|
||||
else // regular quoted character
|
||||
{
|
||||
query_ += *it;
|
||||
}
|
||||
break;
|
||||
case eInName:
|
||||
if (std::isalnum(*it) || *it == '_')
|
||||
{
|
||||
name += *it;
|
||||
}
|
||||
else // end of name
|
||||
{
|
||||
names_.push_back(name);
|
||||
name.clear();
|
||||
query_ += "?";
|
||||
query_ += *it;
|
||||
state = eNormal;
|
||||
}
|
||||
break;
|
||||
case eInAccessDate:
|
||||
if (*it == '#')
|
||||
{
|
||||
query_ += *it;
|
||||
state = eNormal;
|
||||
}
|
||||
else // regular quoted character
|
||||
{
|
||||
query_ += *it;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == eInName)
|
||||
{
|
||||
names_.push_back(name);
|
||||
query_ += "?";
|
||||
}
|
||||
|
||||
SQLRETURN rc = SQLPrepare(hstmt_, (SQLCHAR*)query_.c_str(), (SQLINTEGER)query_.size());
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
query_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
odbc_statement_backend::execute(int number)
|
||||
{
|
||||
// Store the number of rows processed by this call.
|
||||
SQLULEN rows_processed = 0;
|
||||
if (hasVectorUseElements_)
|
||||
{
|
||||
SQLSetStmtAttr(hstmt_, SQL_ATTR_PARAMS_PROCESSED_PTR, &rows_processed, 0);
|
||||
}
|
||||
|
||||
// if we are called twice for the same statement we need to close the open
|
||||
// cursor or an "invalid cursor state" error will occur on execute
|
||||
SQLCloseCursor(hstmt_);
|
||||
|
||||
SQLRETURN rc = SQLExecute(hstmt_);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
// If executing bulk operation a partial
|
||||
// number of rows affected may be available.
|
||||
if (hasVectorUseElements_)
|
||||
{
|
||||
rowsAffected_ = 0;
|
||||
|
||||
do
|
||||
{
|
||||
SQLLEN res = 0;
|
||||
// SQLRowCount will return error after a partially executed statement.
|
||||
// SQL_DIAG_ROW_COUNT returns the same info but must be collected immediatelly after the execution.
|
||||
rc = SQLGetDiagField(SQL_HANDLE_STMT, hstmt_, 0, SQL_DIAG_ROW_COUNT, &res, 0, NULL);
|
||||
if (!is_odbc_error(rc) && res > 0) // 'res' will be -1 for the where the statement failed.
|
||||
{
|
||||
rowsAffected_ += res;
|
||||
}
|
||||
--rows_processed; // Avoid unnecessary calls to SQLGetDiagField
|
||||
}
|
||||
// Move forward to the next result while there are rows processed.
|
||||
while (rows_processed > 0 && SQLMoreResults(hstmt_) == SQL_SUCCESS);
|
||||
}
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
"Statement Execute");
|
||||
}
|
||||
// We should preserve the number of rows affected here
|
||||
// where we know for sure that a bulk operation was executed.
|
||||
else
|
||||
{
|
||||
rowsAffected_ = 0;
|
||||
|
||||
do {
|
||||
SQLLEN res = 0;
|
||||
SQLRETURN rc = SQLRowCount(hstmt_, &res);
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
"Getting number of affected rows");
|
||||
}
|
||||
rowsAffected_ += res;
|
||||
}
|
||||
// Move forward to the next result if executing a bulk operation.
|
||||
while (hasVectorUseElements_ && SQLMoreResults(hstmt_) == SQL_SUCCESS);
|
||||
}
|
||||
SQLSMALLINT colCount;
|
||||
SQLNumResultCols(hstmt_, &colCount);
|
||||
|
||||
if (number > 0 && colCount > 0)
|
||||
{
|
||||
return fetch(number);
|
||||
}
|
||||
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
statement_backend::exec_fetch_result
|
||||
odbc_statement_backend::fetch(int number)
|
||||
{
|
||||
numRowsFetched_ = 0;
|
||||
SQLULEN const row_array_size = static_cast<SQLULEN>(number);
|
||||
|
||||
SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
|
||||
SQLSetStmtAttr(hstmt_, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)row_array_size, 0);
|
||||
SQLSetStmtAttr(hstmt_, SQL_ATTR_ROWS_FETCHED_PTR, &numRowsFetched_, 0);
|
||||
|
||||
SQLRETURN rc = SQLFetch(hstmt_);
|
||||
|
||||
if (SQL_NO_DATA == rc)
|
||||
{
|
||||
return ef_no_data;
|
||||
}
|
||||
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
"Statement Fetch");
|
||||
}
|
||||
|
||||
return ef_success;
|
||||
}
|
||||
|
||||
long long odbc_statement_backend::get_affected_rows()
|
||||
{
|
||||
return rowsAffected_;
|
||||
}
|
||||
|
||||
int odbc_statement_backend::get_number_of_rows()
|
||||
{
|
||||
return numRowsFetched_;
|
||||
}
|
||||
|
||||
std::string odbc_statement_backend::rewrite_for_procedure_call(
|
||||
std::string const &query)
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
int odbc_statement_backend::prepare_for_describe()
|
||||
{
|
||||
SQLSMALLINT numCols;
|
||||
SQLNumResultCols(hstmt_, &numCols);
|
||||
return numCols;
|
||||
}
|
||||
|
||||
void odbc_statement_backend::describe_column(int colNum, data_type & type,
|
||||
std::string & columnName)
|
||||
{
|
||||
SQLCHAR colNameBuffer[2048];
|
||||
SQLSMALLINT colNameBufferOverflow;
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN colSize;
|
||||
SQLSMALLINT decDigits;
|
||||
SQLSMALLINT isNullable;
|
||||
|
||||
SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum),
|
||||
colNameBuffer, 2048,
|
||||
&colNameBufferOverflow, &dataType,
|
||||
&colSize, &decDigits, &isNullable);
|
||||
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
"describe Column");
|
||||
}
|
||||
|
||||
char const *name = reinterpret_cast<char const *>(colNameBuffer);
|
||||
columnName.assign(name, std::strlen(name));
|
||||
|
||||
switch (dataType)
|
||||
{
|
||||
case SQL_TYPE_DATE:
|
||||
case SQL_TYPE_TIME:
|
||||
case SQL_TYPE_TIMESTAMP:
|
||||
type = dt_date;
|
||||
break;
|
||||
case SQL_DOUBLE:
|
||||
case SQL_DECIMAL:
|
||||
case SQL_REAL:
|
||||
case SQL_FLOAT:
|
||||
case SQL_NUMERIC:
|
||||
type = dt_double;
|
||||
break;
|
||||
case SQL_TINYINT:
|
||||
case SQL_SMALLINT:
|
||||
case SQL_INTEGER:
|
||||
type = dt_integer;
|
||||
break;
|
||||
case SQL_BIGINT:
|
||||
type = dt_long_long;
|
||||
break;
|
||||
case SQL_CHAR:
|
||||
case SQL_VARCHAR:
|
||||
case SQL_LONGVARCHAR:
|
||||
default:
|
||||
type = dt_string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t odbc_statement_backend::column_size(int colNum)
|
||||
{
|
||||
SQLCHAR colNameBuffer[2048];
|
||||
SQLSMALLINT colNameBufferOverflow;
|
||||
SQLSMALLINT dataType;
|
||||
SQLULEN colSize;
|
||||
SQLSMALLINT decDigits;
|
||||
SQLSMALLINT isNullable;
|
||||
|
||||
SQLRETURN rc = SQLDescribeCol(hstmt_, static_cast<SQLUSMALLINT>(colNum),
|
||||
colNameBuffer, 2048,
|
||||
&colNameBufferOverflow, &dataType,
|
||||
&colSize, &decDigits, &isNullable);
|
||||
|
||||
if (is_odbc_error(rc))
|
||||
{
|
||||
throw odbc_soci_error(SQL_HANDLE_STMT, hstmt_,
|
||||
"column size");
|
||||
}
|
||||
|
||||
return colSize;
|
||||
}
|
||||
|
||||
odbc_standard_into_type_backend * odbc_statement_backend::make_into_type_backend()
|
||||
{
|
||||
return new odbc_standard_into_type_backend(*this);
|
||||
}
|
||||
|
||||
odbc_standard_use_type_backend * odbc_statement_backend::make_use_type_backend()
|
||||
{
|
||||
return new odbc_standard_use_type_backend(*this);
|
||||
}
|
||||
|
||||
odbc_vector_into_type_backend *
|
||||
odbc_statement_backend::make_vector_into_type_backend()
|
||||
{
|
||||
return new odbc_vector_into_type_backend(*this);
|
||||
}
|
||||
|
||||
odbc_vector_use_type_backend * odbc_statement_backend::make_vector_use_type_backend()
|
||||
{
|
||||
hasVectorUseElements_ = true;
|
||||
return new odbc_vector_use_type_backend(*this);
|
||||
}
|
||||
48
src/backends/odbc/test/CMakeLists.txt
Normal file
48
src/backends/odbc/test/CMakeLists.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
###############################################################################
|
||||
#
|
||||
# This file is part of CMake configuration for SOCI library
|
||||
#
|
||||
# Copyright (C) 2010 Mateusz Loskot <mateusz@loskot.net>
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
if (WIN32)
|
||||
# MDBTools driver seems unreliable
|
||||
soci_backend_test(
|
||||
NAME access
|
||||
BACKEND ODBC
|
||||
SOURCE test-odbc-access.cpp
|
||||
CONNSTR "test-access.dsn")
|
||||
|
||||
# We have no means to test SQL Server at travis-ci.org
|
||||
soci_backend_test(
|
||||
NAME mssql
|
||||
BACKEND ODBC
|
||||
SOURCE test-odbc-mssql.cpp
|
||||
CONNSTR "test-mssql.dsn")
|
||||
endif()
|
||||
|
||||
soci_backend_test(
|
||||
NAME mysql
|
||||
BACKEND ODBC
|
||||
SOURCE test-odbc-mysql.cpp
|
||||
CONNSTR "test-mysql.dsn")
|
||||
|
||||
soci_backend_test(
|
||||
NAME postgresql
|
||||
BACKEND ODBC
|
||||
SOURCE test-odbc-postgresql.cpp
|
||||
CONNSTR "test-postgresql.dsn")
|
||||
|
||||
# TODO: DB2 backend is tested by Travis CI on dedicated VM, separate from ODBC,
|
||||
# in order to test DB2 with ODBC, it would be best to install DB2 driver only.
|
||||
if (NOT $ENV{TRAVIS})
|
||||
soci_backend_test(
|
||||
NAME db2
|
||||
BACKEND ODBC
|
||||
SOURCE test-odbc-db2.cpp
|
||||
CONNSTR "test-db2.dsn")
|
||||
endif()
|
||||
24
src/backends/odbc/test/Makefile.basic
Normal file
24
src/backends/odbc/test/Makefile.basic
Normal file
@@ -0,0 +1,24 @@
|
||||
# The following variable is specific to this backend and its correct
|
||||
# values might depend on your environment - feel free to set it accordingly.
|
||||
|
||||
ODBCINCLUDEDIR = -I/usr/include
|
||||
ODBCLIBDIR = -L/usr/lib
|
||||
ODBCLIBS = -lodbc -lodbcinst
|
||||
|
||||
# The rest of the Makefile is independent of the target environment.
|
||||
|
||||
COMPILER = g++
|
||||
CXXFLAGS = -Wall -pedantic -Wno-long-long
|
||||
INCLUDEDIRS = -I.. -I../../../core ${ODBCINCLUDEDIR}
|
||||
LIBDIRS = -L.. -L../../../core ${ODBCLIBDIR}
|
||||
LIBS = -lsoci_core -lsoci_odbc -ldl ${ODBCLIBS}
|
||||
|
||||
|
||||
test-odbc-mssql: test-odbc-mssql.cpp
|
||||
${COMPILER} -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} ${LIBDIRS} ${LIBS}
|
||||
|
||||
test-odbc-access: test-odbc-access.cpp
|
||||
${COMPILER} -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} ${LIBDIRS} ${LIBS}
|
||||
|
||||
clean :
|
||||
rm -f test-odbc-access.exe test-odbc-mssql.exe
|
||||
19
src/backends/odbc/test/makefile.msvc
Normal file
19
src/backends/odbc/test/makefile.msvc
Normal file
@@ -0,0 +1,19 @@
|
||||
ODBCINCLUDEDIR="C:\Program Files\Microsoft Platform SDK\Include"
|
||||
ODBCLIBDIR="C:\Program Files\Microsoft Platform SDK\Lib"
|
||||
|
||||
COMPILER = cl
|
||||
CXXFLAGS = /nologo /EHsc
|
||||
CXXFLAGSSO = $(CXXFLAGS)
|
||||
INCLUDEDIRS = /I.. /I..\..\..\core /I..\..\..\core\test /I$(ODBCINCLUDEDIR)
|
||||
LIBS = ..\..\..\core\soci-core.lib ..\soci-odbc.lib $(ODBCLIBDIR)\uuid.lib $(ODBCLIBDIR)\odbc32.lib
|
||||
|
||||
mssql: test-odbc-mssql.cpp
|
||||
$(COMPILER) $? $(CXXFLAGS) $(INCLUDEDIRS) $(LIBS)
|
||||
|
||||
access: test-odbc-access.cpp
|
||||
$(COMPILER) $? $(CXXFLAGS) $(INCLUDEDIRS) $(LIBS)
|
||||
|
||||
clean:
|
||||
del *.exe
|
||||
del *.obj
|
||||
|
||||
13
src/backends/odbc/test/test-access.dsn
Normal file
13
src/backends/odbc/test/test-access.dsn
Normal file
@@ -0,0 +1,13 @@
|
||||
[ODBC]
|
||||
DRIVER=Microsoft Access Driver (*.mdb)
|
||||
UID=admin
|
||||
UserCommitSync=Yes
|
||||
Threads=3
|
||||
SafeTransactions=0
|
||||
PageTimeout=5
|
||||
MaxScanRows=8
|
||||
MaxBufferSize=2048
|
||||
FIL=MS Access
|
||||
DriverId=25
|
||||
DefaultDir=.\
|
||||
DBQ=.\soci_test.mdb
|
||||
8
src/backends/odbc/test/test-mssql.dsn
Normal file
8
src/backends/odbc/test/test-mssql.dsn
Normal file
@@ -0,0 +1,8 @@
|
||||
[ODBC]
|
||||
DRIVER=SQL Native Client
|
||||
UID=David
|
||||
DATABASE=soci_test
|
||||
WSID=NANO
|
||||
APP=Microsoft Data Access Components
|
||||
Trusted_Connection=Yes
|
||||
SERVER=localhost\SQLEXPRESS
|
||||
4
src/backends/odbc/test/test-mysql.dsn
Normal file
4
src/backends/odbc/test/test-mysql.dsn
Normal file
@@ -0,0 +1,4 @@
|
||||
[ODBC]
|
||||
DRIVER=MySQL
|
||||
DATABASE=soci_test
|
||||
OPTION=0
|
||||
155
src/backends/odbc/test/test-odbc-access.cpp
Normal file
155
src/backends/odbc/test/test-odbc-access.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-odbc.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_odbc();
|
||||
|
||||
// DDL Creation objects for common tests
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(id integer, val integer, c char, "
|
||||
"str varchar(20), sh integer, ul number, d float, "
|
||||
"tm timestamp, i1 integer, i2 integer, i3 integer, "
|
||||
"name varchar(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(num_float float, num_int integer,"
|
||||
" name varchar(20), sometime datetime, chr char)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(name varchar(100) not null, "
|
||||
"phone varchar(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(val integer)";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Support for SOCI Common Tests
|
||||
//
|
||||
|
||||
class test_context : public test_context_base
|
||||
{
|
||||
public:
|
||||
|
||||
test_context(backend_factory const &backEnd, std::string const &connectString)
|
||||
: test_context_base(backEnd, connectString) {}
|
||||
|
||||
table_creator_base * table_creator_1(session& s) const
|
||||
{
|
||||
return new table_creator_one(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_2(session& s) const
|
||||
{
|
||||
return new table_creator_two(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_3(session& s) const
|
||||
{
|
||||
return new table_creator_three(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string fromDual(std::string const &sql) const
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
std::string toDate(std::string const &datdt_string) const
|
||||
{
|
||||
return "#" + datdt_string + "#";
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const &datdt_string) const
|
||||
{
|
||||
return "#" + datdt_string + "#";
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
connectString = "FILEDSN=./test-access.dsn";
|
||||
}
|
||||
try
|
||||
{
|
||||
std::cout << "\nSOCI ODBC with MS Access Tests:\n\n";
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (soci::odbc_soci_error const & e)
|
||||
{
|
||||
std::cout << "ODBC Error Code: " << e.odbc_error_code() << std::endl
|
||||
<< "Native Error Code: " << e.native_error_code() << std::endl
|
||||
<< "SOCI Message: " << e.what() << std::endl
|
||||
<< "ODBC Message: " << e.odbc_error_message() << std::endl;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << "STD::EXECEPTION " << e.what() << '\n';
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
306
src/backends/odbc/test/test-odbc-db2.cpp
Normal file
306
src/backends/odbc/test/test-odbc-db2.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-odbc.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_odbc();
|
||||
|
||||
// DDL Creation objects for common tests
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(ID INTEGER, VAL SMALLINT, C CHAR, STR VARCHAR(20), SH SMALLINT, UL NUMERIC(20), D DOUBLE, "
|
||||
"TM TIMESTAMP(9), I1 INTEGER, I2 INTEGER, I3 INTEGER, NAME VARCHAR(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(NUM_FLOAT DOUBLE, NUM_INT INTEGER, NAME VARCHAR(20), SOMETIME TIMESTAMP, CHR CHAR)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(NAME VARCHAR(100) NOT NULL, PHONE VARCHAR(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST(VAL INTEGER)";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Support for SOCI Common Tests
|
||||
//
|
||||
|
||||
class test_context : public test_context_base
|
||||
{
|
||||
public:
|
||||
test_context(backend_factory const &backEnd,
|
||||
std::string const &connectString)
|
||||
: test_context_base(backEnd, connectString) {}
|
||||
|
||||
table_creator_base * table_creator_1(session& s) const
|
||||
{
|
||||
return new table_creator_one(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_2(session& s) const
|
||||
{
|
||||
return new table_creator_two(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_3(session& s) const
|
||||
{
|
||||
return new table_creator_three(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const &datdt_string) const
|
||||
{
|
||||
return "\'" + datdt_string + "\'";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_bigint : table_creator_base
|
||||
{
|
||||
table_creator_bigint(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "CREATE TABLE SOCI_TEST (VAL BIGINT)";
|
||||
}
|
||||
};
|
||||
|
||||
void test_odbc_db2_long_long()
|
||||
{
|
||||
const int num_recs = 100;
|
||||
session sql(backEnd, connectString);
|
||||
table_creator_bigint table(sql);
|
||||
|
||||
{
|
||||
long long n;
|
||||
statement st = (sql.prepare <<
|
||||
"INSERT INTO SOCI_TEST (VAL) VALUES (:val)", use(n));
|
||||
for (int i = 0; i < num_recs; i++)
|
||||
{
|
||||
n = 1000000000LL + i;
|
||||
st.execute();
|
||||
}
|
||||
}
|
||||
{
|
||||
long long n2;
|
||||
statement st = (sql.prepare <<
|
||||
"SELECT VAL FROM SOCI_TEST ORDER BY VAL", into(n2));
|
||||
st.execute();
|
||||
for (int i = 0; i < num_recs; i++)
|
||||
{
|
||||
st.fetch();
|
||||
assert(n2 == 1000000000LL + i);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "test odbc_db2_long_long passed" << std::endl;
|
||||
}
|
||||
|
||||
void test_odbc_db2_unsigned_long_long()
|
||||
{
|
||||
const int num_recs = 100;
|
||||
session sql(backEnd, connectString);
|
||||
table_creator_bigint table(sql);
|
||||
|
||||
{
|
||||
unsigned long long n;
|
||||
statement st = (sql.prepare <<
|
||||
"INSERT INTO SOCI_TEST (VAL) VALUES (:val)", use(n));
|
||||
for (int i = 0; i < num_recs; i++)
|
||||
{
|
||||
n = 1000000000LL + i;
|
||||
st.execute();
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned long long n2;
|
||||
statement st = (sql.prepare <<
|
||||
"SELECT VAL FROM SOCI_TEST ORDER BY VAL", into(n2));
|
||||
st.execute();
|
||||
for (int i = 0; i < num_recs; i++)
|
||||
{
|
||||
st.fetch();
|
||||
assert(n2 == 1000000000LL + i);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "test odbc_db2_unsigned_long_long passed" << std::endl;
|
||||
}
|
||||
|
||||
void test_odbc_db2_long_long_vector()
|
||||
{
|
||||
const std::size_t num_recs = 100;
|
||||
session sql(backEnd, connectString);
|
||||
table_creator_bigint table(sql);
|
||||
|
||||
{
|
||||
std::vector<long long> v(num_recs);
|
||||
for (std::size_t i = 0; i < num_recs; i++)
|
||||
{
|
||||
v[i] = 1000000000LL + i;
|
||||
}
|
||||
|
||||
sql << "INSERT INTO SOCI_TEST (VAL) VALUES (:bi)", use(v);
|
||||
}
|
||||
{
|
||||
std::size_t recs = 0;
|
||||
|
||||
std::vector<long long> v(num_recs / 2 + 1);
|
||||
statement st = (sql.prepare <<
|
||||
"SELECT VAL FROM SOCI_TEST ORDER BY VAL", into(v));
|
||||
st.execute();
|
||||
while (true)
|
||||
{
|
||||
if (!st.fetch())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t vsize = v.size();
|
||||
for (std::size_t i = 0; i < vsize; i++)
|
||||
{
|
||||
assert(v[i] == 1000000000LL +
|
||||
static_cast<long long>(recs));
|
||||
recs++;
|
||||
}
|
||||
}
|
||||
assert(recs == num_recs);
|
||||
}
|
||||
|
||||
std::cout << "test odbc_db2_long_long_vector passed" << std::endl;
|
||||
}
|
||||
|
||||
void test_odbc_db2_unsigned_long_long_vector()
|
||||
{
|
||||
const std::size_t num_recs = 100;
|
||||
session sql(backEnd, connectString);
|
||||
table_creator_bigint table(sql);
|
||||
|
||||
{
|
||||
std::vector<unsigned long long> v(num_recs);
|
||||
for (std::size_t i = 0; i < num_recs; i++)
|
||||
{
|
||||
v[i] = 1000000000LL + i;
|
||||
}
|
||||
|
||||
sql << "INSERT INTO SOCI_TEST (VAL) VALUES (:bi)", use(v);
|
||||
}
|
||||
{
|
||||
std::size_t recs = 0;
|
||||
|
||||
std::vector<unsigned long long> v(num_recs / 2 + 1);
|
||||
statement st = (sql.prepare <<
|
||||
"SELECT VAL FROM SOCI_TEST ORDER BY VAL", into(v));
|
||||
st.execute();
|
||||
while (true)
|
||||
{
|
||||
if (!st.fetch())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t vsize = v.size();
|
||||
for (std::size_t i = 0; i < vsize; i++)
|
||||
{
|
||||
assert(v[i] == 1000000000LL +
|
||||
static_cast<unsigned long long>(recs));
|
||||
recs++;
|
||||
}
|
||||
}
|
||||
assert(recs == num_recs);
|
||||
}
|
||||
|
||||
std::cout << "test odbc_db2_unsigned_long_long_vector passed" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << std::endl <<
|
||||
"usage: test-odbc-db2 \"DSN=<db>;Uid=<user>;Pwd=<password>\"" <<
|
||||
std::endl << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
try
|
||||
{
|
||||
std::cout << "\nSOCI ODBC with DB2 Tests:\n\n";
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
std::cout << "\nSOCI DB2 Specific Tests:\n\n";
|
||||
test_odbc_db2_long_long();
|
||||
test_odbc_db2_unsigned_long_long();
|
||||
test_odbc_db2_long_long_vector();
|
||||
test_odbc_db2_unsigned_long_long_vector();
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (soci::odbc_soci_error const & e)
|
||||
{
|
||||
std::cout << "ODBC Error Code: " << e.odbc_error_code() << std::endl
|
||||
<< "Native Error Code: " << e.native_error_code() << std::endl
|
||||
<< "SOCI Message: " << e.what() << std::endl
|
||||
<< "ODBC Message: " << e.odbc_error_message() << std::endl;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << "STD::EXECEPTION " << e.what() << '\n';
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
148
src/backends/odbc/test/test-odbc-mssql.cpp
Normal file
148
src/backends/odbc/test/test-odbc-mssql.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-odbc.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_odbc();
|
||||
|
||||
// DDL Creation objects for common tests
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(id integer, val integer, c char, "
|
||||
"str varchar(20), sh smallint, ul numeric(20), d float, "
|
||||
"tm datetime, i1 integer, i2 integer, i3 integer, "
|
||||
"name varchar(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(num_float float, num_int integer,"
|
||||
" name varchar(20), sometime datetime, chr char)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(name varchar(100) not null, "
|
||||
"phone varchar(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(val integer)";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Support for SOCI Common Tests
|
||||
//
|
||||
|
||||
class test_context : public test_context_base
|
||||
{
|
||||
public:
|
||||
test_context(backend_factory const &backEnd,
|
||||
std::string const &connectString)
|
||||
: test_context_base(backEnd, connectString) {}
|
||||
|
||||
table_creator_base* table_creator_1(session& s) const
|
||||
{
|
||||
return new table_creator_one(s);
|
||||
}
|
||||
|
||||
table_creator_base* table_creator_2(session& s) const
|
||||
{
|
||||
return new table_creator_two(s);
|
||||
}
|
||||
|
||||
table_creator_base* table_creator_3(session& s) const
|
||||
{
|
||||
return new table_creator_three(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const &datdt_string) const
|
||||
{
|
||||
return "convert(datetime, \'" + datdt_string + "\', 120)";
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
connectString = "FILEDSN=./test-mssql.dsn";
|
||||
}
|
||||
try
|
||||
{
|
||||
std::cout << "\nSOCI ODBC with MS SQL Server Tests:\n\n";
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (soci::odbc_soci_error const & e)
|
||||
{
|
||||
std::cout << "ODBC Error Code: " << e.odbc_error_code() << std::endl
|
||||
<< "Native Error Code: " << e.native_error_code() << std::endl
|
||||
<< "SOCI Message: " << e.what() << std::endl
|
||||
<< "ODBC Message: " << e.odbc_error_message() << std::endl;
|
||||
}
|
||||
catch (soci::soci_error const & e)
|
||||
{
|
||||
std::cout << "SOCIERROR: " << e.what() << '\n';
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << "STD::EXECEPTION " << e.what() << '\n';
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
144
src/backends/odbc/test/test-odbc-mysql.cpp
Normal file
144
src/backends/odbc/test/test-odbc-mysql.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-odbc.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_odbc();
|
||||
|
||||
// DDL Creation objects for common tests
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(id integer, val integer, c char, "
|
||||
"str varchar(20), sh int2, ul numeric(20), d float8, "
|
||||
"tm datetime, i1 integer, i2 integer, i3 integer, "
|
||||
"name varchar(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(num_float float8, num_int integer,"
|
||||
" name varchar(20), sometime datetime, chr char)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(name varchar(100) not null, "
|
||||
"phone varchar(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(val integer)";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Support for SOCI Common Tests
|
||||
//
|
||||
|
||||
class test_context : public test_context_base
|
||||
{
|
||||
public:
|
||||
test_context(backend_factory const &backEnd,
|
||||
std::string const &connectString)
|
||||
: test_context_base(backEnd, connectString) {}
|
||||
|
||||
table_creator_base * table_creator_1(session& s) const
|
||||
{
|
||||
return new table_creator_one(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_2(session& s) const
|
||||
{
|
||||
return new table_creator_two(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_3(session& s) const
|
||||
{
|
||||
return new table_creator_three(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const &datdt_string) const
|
||||
{
|
||||
return "\'" + datdt_string + "\'";
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
connectString = "FILEDSN=./test-mysql.dsn";
|
||||
}
|
||||
try
|
||||
{
|
||||
std::cout << "\nSOCI ODBC with MySQL Tests:\n\n";
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (soci::odbc_soci_error const & e)
|
||||
{
|
||||
std::cout << "ODBC Error Code: " << e.odbc_error_code() << std::endl
|
||||
<< "Native Error Code: " << e.native_error_code() << std::endl
|
||||
<< "SOCI Message: " << e.what() << std::endl
|
||||
<< "ODBC Message: " << e.odbc_error_message() << std::endl;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << "STD::EXECEPTION " << e.what() << '\n';
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
146
src/backends/odbc/test/test-odbc-postgresql.cpp
Normal file
146
src/backends/odbc/test/test-odbc-postgresql.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "soci.h"
|
||||
#include "soci-odbc.h"
|
||||
#include "common-tests.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
|
||||
using namespace soci;
|
||||
using namespace soci::tests;
|
||||
|
||||
std::string connectString;
|
||||
backend_factory const &backEnd = *soci::factory_odbc();
|
||||
|
||||
// DDL Creation objects for common tests
|
||||
struct table_creator_one : public table_creator_base
|
||||
{
|
||||
table_creator_one(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(id integer, val integer, c char, "
|
||||
"str varchar(20), sh int2, ul numeric(20), d float8, "
|
||||
"tm timestamp, i1 integer, i2 integer, i3 integer, "
|
||||
"name varchar(20))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_two : public table_creator_base
|
||||
{
|
||||
table_creator_two(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(num_float float8, num_int integer,"
|
||||
" name varchar(20), sometime timestamp, chr char)";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_three : public table_creator_base
|
||||
{
|
||||
table_creator_three(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(name varchar(100) not null, "
|
||||
"phone varchar(15))";
|
||||
}
|
||||
};
|
||||
|
||||
struct table_creator_for_get_affected_rows : table_creator_base
|
||||
{
|
||||
table_creator_for_get_affected_rows(session & sql)
|
||||
: table_creator_base(sql)
|
||||
{
|
||||
sql << "create table soci_test(val integer)";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Support for SOCI Common Tests
|
||||
//
|
||||
|
||||
class test_context : public test_context_base
|
||||
{
|
||||
public:
|
||||
test_context(backend_factory const &backEnd,
|
||||
std::string const &connectString)
|
||||
: test_context_base(backEnd, connectString) {}
|
||||
|
||||
table_creator_base * table_creator_1(session& s) const
|
||||
{
|
||||
return new table_creator_one(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_2(session& s) const
|
||||
{
|
||||
return new table_creator_two(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_3(session& s) const
|
||||
{
|
||||
return new table_creator_three(s);
|
||||
}
|
||||
|
||||
table_creator_base * table_creator_4(session& s) const
|
||||
{
|
||||
return new table_creator_for_get_affected_rows(s);
|
||||
}
|
||||
|
||||
std::string to_date_time(std::string const &datdt_string) const
|
||||
{
|
||||
return "timestamptz(\'" + datdt_string + "\')";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Redirect errors, unrecoverable problems, and assert() failures to STDERR,
|
||||
// instead of debug message window.
|
||||
// This hack is required to run asser()-driven tests by Buildbot.
|
||||
// NOTE: Comment this 2 lines for debugging with Visual C++ debugger to catch assertions inside.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
#endif //_MSC_VER
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
connectString = argv[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
connectString = "FILEDSN=./test-postgresql.dsn";
|
||||
}
|
||||
try
|
||||
{
|
||||
std::cout << "\nSOCI ODBC with PostgreSQL Tests:\n\n";
|
||||
|
||||
test_context tc(backEnd, connectString);
|
||||
common_tests tests(tc);
|
||||
tests.run();
|
||||
|
||||
std::cout << "\nOK, all tests passed.\n\n";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (soci::odbc_soci_error const & e)
|
||||
{
|
||||
std::cout << "ODBC Error Code: " << e.odbc_error_code() << std::endl
|
||||
<< "Native Error Code: " << e.native_error_code() << std::endl
|
||||
<< "SOCI Message: " << e.what() << std::endl
|
||||
<< "ODBC Message: " << e.odbc_error_message() << std::endl;
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cout << "STD::EXECEPTION " << e.what() << '\n';
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
8
src/backends/odbc/test/test-postgresql.dsn
Normal file
8
src/backends/odbc/test/test-postgresql.dsn
Normal file
@@ -0,0 +1,8 @@
|
||||
[ODBC]
|
||||
Description=DSN for SOCI ODBC connection to PostgreSQL
|
||||
Driver=PostgreSQL ANSI
|
||||
Server=localhost
|
||||
Port=5432
|
||||
Database=soci_test
|
||||
UID=postgres
|
||||
PWD=
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user