Squashed 'src/soci/' content from commit 6e9312c

git-subtree-dir: src/soci
git-subtree-split: 6e9312c4bb3748907bd28d62c40feca42878cfef
This commit is contained in:
Vinnie Falco
2015-03-18 19:36:00 -07:00
commit 9708a12607
341 changed files with 54122 additions and 0 deletions

20
src/.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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.
---

0
src/TODO Normal file
View File

5
src/backends/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
*.o
*.la
*.lo
.deps
.libs

View 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 "")

View 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
View 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
View 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

View 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"

View 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()
{
// ...
}

View 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
View 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

View 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;
}
}

View 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;
}
}

View 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);
}

View 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")

View 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;
}

View 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;
}
}

View 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;
}
}

View 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)

View 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

View 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 */)
{
// ...
}

View 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"

View 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()
{
// ...
}

View 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);
}

View 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

View 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()
{
// ...
}

View 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()
{
// ...
}

View 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
View File

@@ -0,0 +1 @@
test_empty

View 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")

View 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

View 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;
}

View 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()
{
// ...
}

View 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()
{
// ...
}

View 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)

View 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

View 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;
}

View 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

View 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

View 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

View 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

View 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"

View 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()
{
}

View 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);
}

View 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

View 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);
}

View 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);
}

View 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);
}

View 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")

View 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

File diff suppressed because it is too large Load Diff

View 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);
}

View 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);
}

View 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)

View 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

View 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

View 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;
}

View 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

View 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"

View 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

View 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);
}

View 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

View 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
}

View 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;
}
}

View 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
View File

@@ -0,0 +1 @@
test_mysql

View 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")

View 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

File diff suppressed because it is too large Load Diff

View 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
}

View 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];
}
}

View 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)

View 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

View 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 */)
{
// ...
}

View 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"

View 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

View 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()
{
// ...
}

View 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_;
}

View 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

View 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;
}
}

View 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;
}
}

View 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);
}

View 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()

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,4 @@
[ODBC]
DRIVER=MySQL
DATABASE=soci_test
OPTION=0

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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