From 0d85e899232985a53f2ee29d661d5d90421ff265 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 14:57:27 +0100 Subject: [PATCH 01/79] First draft of new cmake --- CMakeLists.txt | 278 ++--------- cmake/.gitignore | 21 - cmake/CMakeLists.txt | 13 - cmake/SociBackend.cmake | 388 ---------------- cmake/SociConfig.cmake | 97 ---- cmake/SociDependencies.cmake | 101 ---- cmake/SociUtilities.cmake | 436 ------------------ cmake/SociVersion.cmake | 62 --- cmake/configs/test-access.cmake | 13 - cmake/configs/test-mysql.cmake | 4 - cmake/dependencies/Boost.cmake | 13 - cmake/dependencies/DB2.cmake | 5 - cmake/dependencies/Firebird.cmake | 9 - cmake/dependencies/MySQL.cmake | 5 - cmake/dependencies/ODBC.cmake | 5 - cmake/dependencies/Oracle.cmake | 5 - cmake/dependencies/PostgreSQL.cmake | 5 - cmake/dependencies/SQLite3.cmake | 5 - cmake/dependencies/Threads.cmake | 4 - cmake/{modules => find_modules}/FindDB2.cmake | 0 .../FindFirebird.cmake | 0 .../{modules => find_modules}/FindMySQL.cmake | 0 .../FindOracle.cmake | 0 .../{modules => find_modules}/FindSoci.cmake | 0 cmake/modules/FindDL.cmake | 21 - cmake/modules/FindODBC.cmake | 242 ---------- cmake/modules/FindPostgreSQL.cmake | 191 -------- cmake/modules/FindSQLite3.cmake | 66 --- cmake/resources/SOCIConfig.cmake.in | 3 - .../vs2010-test-cmd-args.vcxproj.user.in | 7 - cmake/soci_parse_version.cmake | 43 ++ cmake/soci_utils.cmake | 51 ++ src/CMakeLists.txt | 12 - src/backends/CMakeLists.txt | 81 ++-- src/backends/db2/CMakeLists.txt | 58 ++- src/backends/empty/CMakeLists.txt | 44 +- src/backends/firebird/CMakeLists.txt | 61 ++- src/backends/mysql/CMakeLists.txt | 60 ++- src/backends/odbc/CMakeLists.txt | 59 ++- src/backends/oracle/CMakeLists.txt | 61 ++- src/backends/postgresql/CMakeLists.txt | 71 ++- src/backends/sqlite3/CMakeLists.txt | 59 ++- src/core/CMakeLists.txt | 195 +++----- src/core/backend-loader.cpp | 2 - src/core/soci_backends_config.h.in | 12 - tests/CMakeLists.txt | 34 -- 46 files changed, 578 insertions(+), 2324 deletions(-) delete mode 100644 cmake/.gitignore delete mode 100644 cmake/CMakeLists.txt delete mode 100644 cmake/SociBackend.cmake delete mode 100644 cmake/SociConfig.cmake delete mode 100644 cmake/SociDependencies.cmake delete mode 100644 cmake/SociUtilities.cmake delete mode 100644 cmake/SociVersion.cmake delete mode 100644 cmake/configs/test-access.cmake delete mode 100644 cmake/configs/test-mysql.cmake delete mode 100644 cmake/dependencies/Boost.cmake delete mode 100644 cmake/dependencies/DB2.cmake delete mode 100644 cmake/dependencies/Firebird.cmake delete mode 100644 cmake/dependencies/MySQL.cmake delete mode 100644 cmake/dependencies/ODBC.cmake delete mode 100644 cmake/dependencies/Oracle.cmake delete mode 100644 cmake/dependencies/PostgreSQL.cmake delete mode 100644 cmake/dependencies/SQLite3.cmake delete mode 100644 cmake/dependencies/Threads.cmake rename cmake/{modules => find_modules}/FindDB2.cmake (100%) rename cmake/{modules => find_modules}/FindFirebird.cmake (100%) rename cmake/{modules => find_modules}/FindMySQL.cmake (100%) rename cmake/{modules => find_modules}/FindOracle.cmake (100%) rename cmake/{modules => find_modules}/FindSoci.cmake (100%) delete mode 100644 cmake/modules/FindDL.cmake delete mode 100644 cmake/modules/FindODBC.cmake delete mode 100644 cmake/modules/FindPostgreSQL.cmake delete mode 100644 cmake/modules/FindSQLite3.cmake delete mode 100644 cmake/resources/SOCIConfig.cmake.in delete mode 100644 cmake/resources/vs2010-test-cmd-args.vcxproj.user.in create mode 100644 cmake/soci_parse_version.cmake create mode 100644 cmake/soci_utils.cmake delete mode 100644 src/core/soci_backends_config.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 57a7c958d..3db2339c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,257 +1,71 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2009-2013 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) -# -############################################################################### -# General settings -############################################################################### -cmake_minimum_required(VERSION 2.8...3.20 FATAL_ERROR) +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) -project(SOCI) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/find_modules") -if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 14) - set(CMAKE_CXX_STANDARD 14) -endif() -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Path to additional CMake modules -set(CMAKE_MODULE_PATH ${SOCI_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) -set(CMAKE_MODULE_PATH ${SOCI_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) - -include(SociUtilities) +include(soci_parse_version) +soci_parse_version( + ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE SOCI_VERSION +) -############################################################################### -# Build features and variants -############################################################################## +project(SOCI + VERSION ${SOCI_VERSION} + DESCRIPTION "C++ database access library" + HOMEPAGE_URL "https://soci.sourceforge.net/" + LANGUAGES CXX +) -option(SOCI_SHARED "Enable build of shared libraries" ON) -option(SOCI_STATIC "Enable build of static libraries" ON) -option(SOCI_TESTS "Enable build of collection of SOCI tests" ON) -option(SOCI_ASAN "Enable address sanitizer on GCC v4.8+/Clang v 3.1+" OFF) -option(SOCI_LTO "Enable link time optimization" OFF) -option(SOCI_VISIBILITY "Enable hiding private symbol using ELF visibility if supported by the platform" ON) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) -if (SOCI_LTO) - cmake_minimum_required(VERSION 3.9) +include(CMakeDependentOption) +include(CheckIPOSupported) +include(CheckCXXCompilerFlag) - # Check and enable lto support - include(CheckIPOSupported) - check_ipo_supported(RESULT supported) +check_ipo_supported(RESULT LTO_AVAILABLE) - if (NOT supported) - message(STATUS "IPO / LTO not supported") - endif() +option(SOCI_SHARED "Enable building SOCI as a shared library" ON) +option(SOCI_STATIC "Enable building SOCI as a static library" ON) +option(SOCI_TESTS "Enable building SOCI test cases" ${PROJECT_IS_TOP_LEVEL}) +option(SOCI_ASAN "Enable building SOCI with enabled address sanitizers" OFF) +cmake_dependent_option(SOCI_LTO "Enable link time optimizations in release builds" ON "LTO_AVAILABLE" OFF) +option(SOCI_VISIBILITY "Make all functions hidden by default - this exposes only explicitly exported functions" ON) +set(SOCI_LD "" CACHE STRING "Specify a non-default linker") +option(SOCI_BOOST "Whether to enable Boost-specific bindings" ON) - if (supported AND NOT SOCI_ASAN) - message(STATUS "IPO / LTO enabled") - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # Check for lld as clang lto works best with its own linker - soci_use_ld_if_supported(lld) - endif() - elseif(supported) - message(STATUS "IPO / LTO is supported but conflicts with ASAN and not enabled") - endif() -endif() +# Configure LTO for anything but Debug builds (if enabled in the first place) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${SOCI_LTO}) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_Debug OFF) if (SOCI_VISIBILITY) - # Test whether visibility is supported - include(CheckCSourceCompiles) - check_c_source_compiles( - " - __attribute__ (( visibility(\"default\") )) int f1() { return 0; } - __attribute__ (( visibility(\"hidden\") )) int f2() { return 1; } - - int main(int argc, char* argv[]) { f1(); f2(); return 0; } - " - SOCI_HAVE_VISIBILITY_SUPPORT - ) - - if (SOCI_HAVE_VISIBILITY_SUPPORT) - message(STATUS "gcc / clang visibility enabled") - set(CMAKE_CXX_VISIBILITY_PRESET hidden) - cmake_policy(SET CMP0063 NEW) - endif() -else() - set(SOCI_HAVE_VISIBILITY_SUPPORT off) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) endif() -# Allow using alternative linker such as mold, which can be significantly -# faster than GNU ld. -option(SOCI_LD "Use non-default linker, such as 'mold'" "") -if(SOCI_LD) - # -fuse-ld works only with recent gcc (>= 12), but we don't need to support - # this with all gcc versions as this is entirely optional. I.e. if we really - # wanted to, we could use -B option as explained in mold README to make it - # work with any gcc, but for now keep the things simple. - soci_use_ld_if_supported(${SOCI_LD}) +if (SOCI_LD) + # CMake asks the compiler to do the linking so we have to pass the desired linker to the compiler + set(USE_LD_FLAG "-fuse-ld=${SOCI_LD}") + check_cxx_compiler_flag("${USE_LD_FLAG}" CAN_USE_CUSTOM_LD) + if (NOT CAN_USE_CUSTOM_LD) + message(FATAL_ERROR "Can't use custom linker '${SOCI_LD}' - compiler doesn't accept flag '${USE_LD_FLAG}'") + endif() + add_link_options("${USE_LD_FLAG}") endif() -############################################################################### -# SOCI configuration -############################################################################### -include(SociConfig) - -colormsg(_HIBLUE_ "Configuring SOCI:") - -############################################################################### -# SOCI version information -############################################################################### -include(SociVersion) - -soci_version() - -############################################################################### -# Build features and variants -############################################################################## - -boost_report_value(SOCI_SHARED) -boost_report_value(SOCI_STATIC) -boost_report_value(SOCI_TESTS) -boost_report_value(SOCI_ASAN) - -# from SociConfig.cmake -boost_report_value(LIB_SUFFIX) - -# 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. +# Ensure that libraries and executables end up at the top of the build tree 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_TARGET) -set(SOCI_CORE_TARGET_STATIC) -set(SOCI_CORE_DEPS_LIBS) - -include(SociDependencies) - -get_property(SOCI_INCLUDE_DIRS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - PROPERTY INCLUDE_DIRECTORIES) - -if(Threads_FOUND) - list(APPEND SOCI_CORE_DEPS_LIBS ${CMAKE_THREAD_LIBS_INIT}) -else() - message(FATAL_ERROR "No thread library found") -endif() - -if(NOT MSVC) - set(DL_FIND_QUIETLY TRUE) - find_package(DL) - if(DL_FOUND) - list(APPEND SOCI_CORE_DEPS_LIBS ${DL_LIBRARY}) - set_directory_properties(PROPERTIES INCLUDE_DIRECTORIES ${DL_INCLUDE_DIR}) - add_definitions(-DHAVE_DL=1) - endif() -endif() - -if(Boost_FOUND) - get_property(SOCI_COMPILE_DEFINITIONS - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - PROPERTY COMPILE_DEFINITIONS) - set(SOCI_HAVE_BOOST ON) - - list(APPEND SOCI_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB") - - if(Boost_DATE_TIME_FOUND) - list(APPEND SOCI_CORE_DEPS_LIBS ${Boost_DATE_TIME_LIBRARY}) - set(SOCI_HAVE_BOOST_DATE_TIME ON) - endif() - - list(APPEND SOCI_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) - list(APPEND SOCI_CORE_INCLUDE_DIRS ${Boost_INCLUDE_DIRS}) - - set_directory_properties(PROPERTY COMPILE_DEFINITIONS "${SOCI_COMPILE_DEFINITIONS}") - - set_property(DIRECTORY ${SOCI_SOURCE_DIR} - PROPERTY COMPILE_DEFINITIONS "${SOCI_COMPILE_DEFINITIONS}") -else() - set(SOCI_HAVE_BOOST OFF) - set(SOCI_HAVE_BOOST_DATE_TIME OFF) -endif() - -set(SOCI_HAVE_BOOST ${SOCI_HAVE_BOOST} CACHE INTERNAL "Boost library") -set(SOCI_HAVE_BOOST_DATE_TIME ${SOCI_HAVE_BOOST_DATE_TIME} CACHE INTERNAL "Boost date_time library") - -list(APPEND SOCI_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) - -set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - PROPERTY - INCLUDE_DIRECTORIES ${SOCI_INCLUDE_DIRS}) - -############################################################################### -# Installation -############################################################################### - -include(GNUInstallDirs) - -############################################################################### -# Configuration files -############################################################################### -set(CONFIG_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include) -install(DIRECTORY ${CONFIG_INCLUDE_DIR}/soci DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -set(CONFIG_FILE_IN "include/soci/soci-config.h.in") -set(CONFIG_FILE_OUT "${CONFIG_INCLUDE_DIR}/soci/soci-config.h") - - - -############################################################################### -# Build configured components -############################################################################### -include(SociBackend) - -include_directories(${SOCI_SOURCE_DIR}/include ${CONFIG_INCLUDE_DIR}) add_subdirectory(src) -if(SOCI_TESTS) - ############################################################################### - # Enable tests - ############################################################################### - enable_testing() - - file(TO_NATIVE_PATH ${PROJECT_SOURCE_DIR} TEST_ACCESS_PATH) - configure_file(${PROJECT_SOURCE_DIR}/cmake/configs/test-access.cmake ${PROJECT_SOURCE_DIR}/tests/odbc/test-access.dsn @ONLY) - set(MYSQL_DRIVER_NAME "MySQL") - if(WIN32) - set(MYSQL_DRIVER_NAME "MySQL ODBC 5.3 ANSI Driver") - endif() - configure_file(${PROJECT_SOURCE_DIR}/cmake/configs/test-mysql.cmake ${PROJECT_SOURCE_DIR}/tests/odbc/test-mysql.dsn @ONLY) - - # Define "make check" as alias for "make test" - add_custom_target(check COMMAND ctest) +if (SOCI_TESTS) + include(CTest) + enable_testing() add_subdirectory(tests) endif() - -############################################################################### -# build config file -############################################################################### - -get_cmake_property(ALL_VARIABLES CACHE_VARIABLES) -set(CONFIGURED_VARIABLES) -foreach(v ${ALL_VARIABLES}) - if (v MATCHES "^SOCI_HAVE.*") - get_property(CACHE_HELPSTRING CACHE ${v} PROPERTY HELPSTRING) - set(CONFIGURED_VARIABLES "${CONFIGURED_VARIABLES}\n// ${CACHE_HELPSTRING}\n") - if (${${v}}) - set(CONFIGURED_VARIABLES "${CONFIGURED_VARIABLES}#define ${v}\n") - else() - set(CONFIGURED_VARIABLES "${CONFIGURED_VARIABLES}/* #undef ${v} */\n") - endif() - endif() -endforeach() -configure_file("${CONFIG_FILE_IN}" "${CONFIG_FILE_OUT}") - -message(STATUS "") diff --git a/cmake/.gitignore b/cmake/.gitignore deleted file mode 100644 index 61caff875..000000000 --- a/cmake/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -*~ -*.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 - diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt deleted file mode 100644 index 317236e6a..000000000 --- a/cmake/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2009 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) -# -############################################################################### - -# install the cmake modules -file(GLOB SOCI_CMAKE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cmake") diff --git a/cmake/SociBackend.cmake b/cmake/SociBackend.cmake deleted file mode 100644 index 0a664667e..000000000 --- a/cmake/SociBackend.cmake +++ /dev/null @@ -1,388 +0,0 @@ -################################################################################ -# SociBackend.cmake - part of CMake configuration of SOCI library -################################################################################ -# Copyright (C) 2010-2013 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) -################################################################################ -# Macros in this module: -# -# soci_backend -# - defines project of a database backend for SOCI library -# -# soci_backend_test -# - defines test project of a database backend for SOCI library -################################################################################ - -macro(soci_backend_deps_found NAME DEPS SUCCESS) - #message(STATUS "DEPS=${DEPS}") - - # Determine required dependencies - set(DEPS_INCLUDE_DIRS) - set(DEPS_LIBRARIES) - set(DEPS_NOT_FOUND) - - # CMake 2.8+ syntax only: - #foreach(dep IN LISTS DEPS) - foreach(dep ${DEPS}) - soci_check_package_found(${dep} DEPEND_FOUND) - if(NOT DEPEND_FOUND) - list(APPEND DEPS_NOT_FOUND ${dep}) - else() - string(TOUPPER "${dep}" DEPU) - if( ${DEPU}_INCLUDE_DIR ) - list(APPEND DEPS_INCLUDE_DIRS ${${DEPU}_INCLUDE_DIR}) - endif() - if( ${DEPU}_INCLUDE_DIRS ) - list(APPEND DEPS_INCLUDE_DIRS ${${DEPU}_INCLUDE_DIRS}) - endif() - list(APPEND DEPS_LIBRARIES ${${DEPU}_LIBRARIES}) - endif() - endforeach() - - list(LENGTH DEPS_NOT_FOUND NOT_FOUND_COUNT) - - if (NOT_FOUND_COUNT GREATER 0) - set(${SUCCESS} False) - else() - set(${NAME}_DEPS_INCLUDE_DIRS ${DEPS_INCLUDE_DIRS}) - set(${NAME}_DEPS_LIBRARIES ${DEPS_LIBRARIES}) - set(${SUCCESS} True) - endif() - - #message(STATUS "soci_backend_deps_found: ${SUCCESS}=${${SUCCESS}}") -endmacro() - -# Defines project of a database backend for SOCI library -# -# soci_backend(backendname -# DEPENDS dependency1 dependency2 -# DESCRIPTION description -# AUTHORS author1 author2 -# MAINTAINERS maintainer1 maintainer2) -# -macro(soci_backend NAME) - parse_arguments(THIS_BACKEND - "DEPENDS;DESCRIPTION;AUTHORS;MAINTAINERS;" - "" - ${ARGN}) - - colormsg(HIGREEN "${NAME} - ${THIS_BACKEND_DESCRIPTION}") - - # Backend name variants utils - string(TOLOWER "${PROJECT_NAME}" PROJECTNAMEL) - string(TOLOWER "${NAME}" NAMEL) - string(TOUPPER "${NAME}" NAMEU) - - # Backend option available to user - set(THIS_BACKEND_OPTION SOCI_${NAMEU}) - - soci_backend_deps_found(${NAMEU} "${THIS_BACKEND_DEPENDS}" ${NAMEU}_DEPS_FOUND) - if(NOT ${NAMEU}_DEPS_FOUND) - - colormsg(_RED_ "WARNING: Some required dependencies of ${NAME} backend not found:") - - if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.8) - foreach(dep ${DEPENDS_NOT_FOUND}) - colormsg(RED " ${dep}") - endforeach() - else() - foreach(dep IN LISTS DEPENDS_NOT_FOUND) - colormsg(RED " ${dep}") - endforeach() - endif() - - # TODO: Abort or warn compilation may fail? --mloskot - colormsg(RED "Skipping") - - set(${THIS_BACKEND_OPTION} OFF) - - else() - - if(${THIS_BACKEND_OPTION}) - - get_directory_property(THIS_INCLUDE_DIRS INCLUDE_DIRECTORIES) - get_directory_property(THIS_COMPILE_DEFS COMPILE_DEFINITIONS) - - # Backend-specific depedencies - set(THIS_BACKEND_DEPENDS_INCLUDE_DIRS ${${NAMEU}_DEPS_INCLUDE_DIRS}) - set(THIS_BACKEND_DEPENDS_LIBRARIES ${${NAMEU}_DEPS_LIBRARIES}) - set(THIS_BACKEND_DEPENDS_DEFS ${${NAMEU}_DEPS_DEFS}) - - # Collect include directories - list(APPEND THIS_INCLUDE_DIRS ${SOCI_SOURCE_DIR}/include/private) - list(APPEND THIS_INCLUDE_DIRS ${THIS_BACKEND_DEPENDS_INCLUDE_DIRS}) - # Collect compile definitions - list(APPEND THIS_COMPILE_DEFS ${THIS_BACKEND_DEPENDS_DEFS}) - - set_directory_properties(PROPERTIES - INCLUDE_DIRECTORIES "${THIS_INCLUDE_DIRS}" - COMPILE_DEFINITIONS "${THIS_COMPILE_DEFS}") - - # Backend target - set(THIS_BACKEND_VAR SOCI_${NAMEU}) - set(THIS_BACKEND_TARGET ${PROJECTNAMEL}_${NAMEL}) - set(THIS_BACKEND_TARGET_VAR ${THIS_BACKEND_VAR}_TARGET) - set(${THIS_BACKEND_TARGET_VAR} ${THIS_BACKEND_TARGET}) - - soci_target_output_name(${THIS_BACKEND_TARGET} ${THIS_BACKEND_VAR}_OUTPUT_NAME) - - set(THIS_BACKEND_OUTPUT_NAME ${${THIS_BACKEND_VAR}_OUTPUT_NAME}) - set(THIS_BACKEND_OUTPUT_NAME_VAR ${THIS_BACKEND_VAR}_OUTPUT_NAME) - - set(${THIS_BACKEND_VAR}_COMPILE_DEFINITIONS ${THIS_COMPILE_DEFS}) - set(THIS_BACKEND_COMPILE_DEFINITIONS_VAR ${THIS_BACKEND_VAR}_COMPILE_DEFINITIONS) - - set(${THIS_BACKEND_VAR}_INCLUDE_DIRECTORIES ${THIS_INCLUDE_DIRS}) - set(THIS_BACKEND_INCLUDE_DIRECTORIES_VAR ${THIS_BACKEND_VAR}_INCLUDE_DIRECTORIES) - - # Backend installable headers and sources - file(GLOB THIS_BACKEND_HEADERS ${SOCI_SOURCE_DIR}/include/soci/${NAMEL}/*.h) - file(GLOB THIS_BACKEND_SOURCES *.cpp) - set(THIS_BACKEND_HEADERS_VAR SOCI_${NAMEU}_HEADERS) - set(${THIS_BACKEND_HEADERS_VAR} ${THIS_BACKEND_HEADERS}) - # Group source files for IDE source explorers (e.g. Visual Studio) - source_group("Header Files" FILES ${THIS_BACKEND_HEADERS}) - source_group("Source Files" FILES ${THIS_BACKEND_SOURCES}) - source_group("CMake Files" FILES CMakeLists.txt) - - # TODO: Extract as macros: soci_shared_lib_target and soci_static_lib_target --mloskot - # Shared library target - if (SOCI_SHARED) - add_library(${THIS_BACKEND_TARGET} - SHARED - ${THIS_BACKEND_SOURCES} - ${THIS_BACKEND_HEADERS}) - add_library(Soci::${NAMEL} ALIAS ${THIS_BACKEND_TARGET}) - - target_link_libraries(${THIS_BACKEND_TARGET} - ${SOCI_CORE_TARGET} - ${THIS_BACKEND_DEPENDS_LIBRARIES}) - - if(WIN32) - set_target_properties(${THIS_BACKEND_TARGET} - PROPERTIES - OUTPUT_NAME ${THIS_BACKEND_OUTPUT_NAME} - DEFINE_SYMBOL SOCI_DLL) - else() - set_target_properties(${THIS_BACKEND_TARGET} - PROPERTIES - SOVERSION ${${PROJECT_NAME}_SOVERSION} - INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib) - - if(APPLE) - set_target_properties(${THIS_BACKEND_TARGET} - PROPERTIES - LINK_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress") - endif() - endif() - - set_target_properties(${THIS_BACKEND_TARGET} - PROPERTIES - VERSION ${${PROJECT_NAME}_VERSION} - CLEAN_DIRECT_OUTPUT 1) - endif() - - # Static library target - if(SOCI_STATIC) - set(THIS_BACKEND_TARGET_STATIC ${THIS_BACKEND_TARGET}_static) - - add_library(${THIS_BACKEND_TARGET_STATIC} - STATIC - ${THIS_BACKEND_SOURCES} - ${THIS_BACKEND_HEADERS}) - add_library(Soci::${NAMEL}_static ALIAS ${THIS_BACKEND_TARGET_STATIC}) - - # Still need to link the libraries for tests to work - target_link_libraries (${THIS_BACKEND_TARGET_STATIC} - ${THIS_BACKEND_DEPENDS_LIBRARIES} - ) - - set_target_properties(${THIS_BACKEND_TARGET_STATIC} - PROPERTIES - OUTPUT_NAME ${THIS_BACKEND_OUTPUT_NAME} - PREFIX "lib" - CLEAN_DIRECT_OUTPUT 1) - endif() - - # Backend installation - install(FILES ${THIS_BACKEND_HEADERS} - DESTINATION - ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECTNAMEL}/${NAMEL}) - - if (SOCI_SHARED) - install(TARGETS ${THIS_BACKEND_TARGET} - EXPORT SOCI - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - endif() - - if (SOCI_STATIC) - install(TARGETS ${THIS_BACKEND_TARGET_STATIC} - EXPORT SOCI - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) - endif() - - else() - colormsg(YELLOW "${NAME} backend explicitly disabled") - endif() - - endif() - - boost_report_value(${THIS_BACKEND_OPTION}) - - if(${THIS_BACKEND_OPTION}) - boost_report_value(${THIS_BACKEND_TARGET_VAR}) - boost_report_value(${THIS_BACKEND_OUTPUT_NAME_VAR}) - boost_report_value(${THIS_BACKEND_COMPILE_DEFINITIONS_VAR}) - boost_report_value(${THIS_BACKEND_INCLUDE_DIRECTORIES_VAR}) - endif() - - # LOG - #message("soci_backend:") - #message("NAME: ${NAME}") - #message("${THIS_BACKEND_OPTION} = ${SOCI_BACKEND_SQLITE3}") - #message("DEPENDS: ${THIS_BACKEND_DEPENDS}") - #message("DESCRIPTION: ${THIS_BACKEND_DESCRIPTION}") - #message("AUTHORS: ${THIS_BACKEND_AUTHORS}") - #message("MAINTAINERS: ${THIS_BACKEND_MAINTAINERS}") - #message("SOURCES: ${THIS_BACKEND_SOURCES}") - #message("DEPENDS_LIBRARIES: ${THIS_BACKEND_DEPENDS_LIBRARIES}") - #message("DEPENDS_INCLUDE_DIRS: ${THIS_BACKEND_DEPENDS_INCLUDE_DIRS}") -endmacro() - -# Generates .vcxproj.user for target of each test. -# -# soci_backend_test_create_vcxproj_user( -# PostgreSQLTest -# "host=localhost dbname=soci_test user=mloskot") -# -function(soci_backend_test_create_vcxproj_user TARGET_NAME TEST_CMD_ARGS) - if(MSVC) - set(SYSTEM_NAME $ENV{USERDOMAIN}) - set(USER_NAME $ENV{USERNAME}) - set(SOCI_TEST_CMD_ARGS ${TEST_CMD_ARGS}) - - if(MSVC_VERSION EQUAL 1600) - configure_file( - ${SOCI_SOURCE_DIR}/cmake/resources/vs2010-test-cmd-args.vcxproj.user.in - ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.vcxproj.user - @ONLY) - endif() - endif() -endfunction(soci_backend_test_create_vcxproj_user) - -# Defines test project of a database backend for SOCI library -# -# soci_backend_test(BACKEND mybackend SOURCE mytest1.cpp -# NAME mytest1 -# CONNSTR "my test connection" -# DEPENDS library1 library2) -# -macro(soci_backend_test) - parse_arguments(THIS_TEST - "BACKEND;SOURCE;CONNSTR;NAME;DEPENDS;" - "" - ${ARGN}) - - # Test backend name - string(TOUPPER "${THIS_TEST_BACKEND}" BACKENDU) - string(TOLOWER "${THIS_TEST_BACKEND}" BACKENDL) - - if(SOCI_TESTS AND SOCI_${BACKENDU} AND NOT SOCI_${BACKENDU}_DO_NOT_TEST) - - # Test name - if(THIS_TEST_NAME) - string(TOUPPER "${THIS_TEST_NAME}" NAMEU) - set(TEST_FULL_NAME SOCI_${BACKENDU}_TEST_${NAMEU}) - else() - set(TEST_FULL_NAME SOCI_${BACKENDU}_TEST) - endif() - string(TOLOWER "${TEST_FULL_NAME}" TEST_TARGET) - string(TOUPPER "${TEST_FULL_NAME}" NAMEU) - - soci_backend_deps_found(${NAMEU} "${THIS_TEST_DEPENDS}" ${NAMEU}_DEPS_FOUND) - if(${NAMEU}_DEPS_FOUND) - get_directory_property(THIS_INCLUDE_DIRS INCLUDE_DIRECTORIES) - get_directory_property(THIS_COMPILE_DEFS COMPILE_DEFINITIONS) - - set(THIS_TEST_DEPENDS_INCLUDE_DIRS ${${NAMEU}_DEPS_INCLUDE_DIRS}) - set(THIS_TEST_DEPENDS_LIBRARIES ${${NAMEU}_DEPS_LIBRARIES}) - set(THIS_TEST_DEPENDS_DEFS ${${NAMEU}_DEPS_DEFS}) - - list(APPEND THIS_INCLUDE_DIRS ${THIS_TEST_DEPENDS_INCLUDE_DIRS}) - list(APPEND THIS_COMPILE_DEFS ${THIS_TEST_DEPENDS_DEFS}) - - set_directory_properties(PROPERTIES - INCLUDE_DIRECTORIES "${THIS_INCLUDE_DIRS}" - COMPILE_DEFINITIONS "${THIS_COMPILE_DEFS}") - else() - colormsg(_RED_ "WARNING: Some dependencies of ${THIS_TEST_BACKEND} test not found") - endif() - - set(TEST_CONNSTR_VAR ${TEST_FULL_NAME}_CONNSTR) - set(${TEST_CONNSTR_VAR} "" - CACHE STRING "Connection string for ${BACKENDU} test") - - if(NOT ${TEST_CONNSTR_VAR} AND THIS_TEST_CONNSTR) - set(${TEST_CONNSTR_VAR} ${THIS_TEST_CONNSTR}) - endif() - - boost_message_value(${TEST_CONNSTR_VAR}) - - if( SOCI_SHARED ) - # Shared libraries test - add_executable(${TEST_TARGET} ${THIS_TEST_SOURCE}) - - target_link_libraries(${TEST_TARGET} - ${SOCI_CORE_DEPS_LIBS} - ${THIS_TEST_DEPENDS_LIBRARIES} - soci_core - soci_${BACKENDL}) - - add_test(${TEST_TARGET} - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_TARGET} - ${${TEST_CONNSTR_VAR}} --invisibles) - - soci_backend_test_create_vcxproj_user(${TEST_TARGET} "\"${${TEST_CONNSTR_VAR}}\"") - - # Ask make check to try to build tests first before executing them - add_dependencies(check ${TEST_TARGET}) - endif(SOCI_SHARED) - - # Static libraries test - if(SOCI_STATIC) - set(TEST_TARGET_STATIC ${TEST_TARGET}_static) - - add_executable(${TEST_TARGET_STATIC} ${THIS_TEST_SOURCE}) - - target_link_libraries(${TEST_TARGET_STATIC} - ${SOCI_CORE_DEPS_LIBS} - ${THIS_TEST_DEPENDS_LIBRARIES} - soci_${BACKENDL}_static - soci_core_static) - - add_test(${TEST_TARGET_STATIC} - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_TARGET_STATIC} - ${${TEST_CONNSTR_VAR}} --invisibles) - - soci_backend_test_create_vcxproj_user(${TEST_TARGET_STATIC} "\"${${TEST_CONNSTR_VAR}}\"") - - # Ask make check to try to build tests first before executing them - add_dependencies(check ${TEST_TARGET_STATIC}) - endif(SOCI_STATIC) - - - - # Group source files for IDE source explorers (e.g. Visual Studio) - source_group("Source Files" FILES ${THIS_TEST_SOURCE}) - source_group("CMake Files" FILES CMakeLists.txt) - - endif() -endmacro() diff --git a/cmake/SociConfig.cmake b/cmake/SociConfig.cmake deleted file mode 100644 index 0ce5b3431..000000000 --- a/cmake/SociConfig.cmake +++ /dev/null @@ -1,97 +0,0 @@ -################################################################################ -# SociConfig.cmake - CMake build configuration of 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) -################################################################################ - -include(CheckCXXSymbolExists) - -if(WIN32) - check_cxx_symbol_exists("_M_AMD64" "" SOCI_TARGET_ARCH_X64) - if(NOT RTC_ARCH_X64) - check_cxx_symbol_exists("_M_IX86" "" SOCI_TARGET_ARCH_X86) - endif(NOT RTC_ARCH_X64) - # add check for arm here - # see http://msdn.microsoft.com/en-us/library/b0084kay.aspx -else(WIN32) - check_cxx_symbol_exists("__i386__" "" SOCI_TARGET_ARCH_X86) - check_cxx_symbol_exists("__x86_64__" "" SOCI_TARGET_ARCH_X64) - check_cxx_symbol_exists("__arm__" "" SOCI_TARGET_ARCH_ARM) -endif(WIN32) - -if(NOT DEFINED LIB_SUFFIX) - if(SOCI_TARGET_ARCH_X64) - set(_lib_suffix "64") - else() - set(_lib_suffix "") - endif() - set(LIB_SUFFIX ${_lib_suffix} CACHE STRING "Specifies suffix for the lib directory") -endif() - -# -# Force compilation flags and set desired warnings level -# - -# This is used to set the -Werror compilation flag only when explicitly -# requested, as e.g. in CI builds. -set(SOCI_WERROR_OPTION "") - -if (MSVC) - add_definitions(-D_CRT_SECURE_NO_DEPRECATE) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) - add_definitions(-D_CRT_NONSTDC_NO_WARNING) - add_definitions(-D_SCL_SECURE_NO_WARNINGS) - - if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") - string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /we4266") - endif() - - if (SOCI_ENABLE_WERROR) - set(SOCI_WERROR_OPTION "/WX") - endif (SOCI_ENABLE_WERROR) -else() - - if (SOCI_ENABLE_WERROR) - set(SOCI_WERROR_OPTION "-Werror") - endif (SOCI_ENABLE_WERROR) - - set(SOCI_GCC_CLANG_COMMON_FLAGS - "-pedantic -Wno-error=parentheses -Wall -Wextra -Wpointer-arith -Wcast-align -Wcast-qual -Wfloat-equal -Woverloaded-virtual -Wredundant-decls -Wno-long-long") - - - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER}" MATCHES "clang") - - if(NOT CMAKE_CXX_COMPILER_VERSION LESS 3.1 AND SOCI_ASAN) - set(SOCI_GCC_CLANG_COMMON_FLAGS "${SOCI_GCC_CLANG_COMMON_FLAGS} -fsanitize=address") - endif() - - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SOCI_GCC_CLANG_COMMON_FLAGS}") - - elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - - if(NOT CMAKE_CXX_COMPILER_VERSION LESS 4.8 AND SOCI_ASAN) - set(SOCI_GCC_CLANG_COMMON_FLAGS "${SOCI_GCC_CLANG_COMMON_FLAGS} -fsanitize=address") - endif() - - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SOCI_GCC_CLANG_COMMON_FLAGS} ") - if (CMAKE_COMPILER_IS_GNUCXX) - if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros") - endif() - endif() - - else() - message(WARNING "Unknown toolset - using default flags to build SOCI") - endif() - -endif() - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SOCI_WERROR_OPTION}") diff --git a/cmake/SociDependencies.cmake b/cmake/SociDependencies.cmake deleted file mode 100644 index 5f174a51f..000000000 --- a/cmake/SociDependencies.cmake +++ /dev/null @@ -1,101 +0,0 @@ -################################################################################ -# SociDependencies.cmake - part of CMake configuration of SOCI library -# -# Based on BoostExternals.cmake from CMake configuration for Boost -################################################################################ -# Copyright (C) 2010 Mateusz Loskot -# Copyright (C) 2009 Troy Straszheim -# -# 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) -################################################################################ -# Macros in this module: -# -# soci_backend - defines a database backend for SOCI library -# -################################################################################ - -# -# List of SOCI dependncies -# -set(SOCI_CORE_DEPENDENCIES - Threads - Boost) - -set(SOCI_BACKENDS_DB_DEPENDENCIES - MySQL - ODBC - Oracle - PostgreSQL - SQLite3 - Firebird - DB2) - -set(SOCI_ALL_DEPENDENCIES - ${SOCI_CORE_DEPENDENCIES} - ${SOCI_BACKENDS_DB_DEPENDENCIES}) - -# -# Perform checks -# -colormsg(_HIBLUE_ "Looking for SOCI dependencies:") - -macro(boost_external_report NAME) - - set(VARNAME ${NAME}) - string(TOUPPER ${NAME} VARNAMEU) - - set(VARNAMES ${ARGV}) - list(REMOVE_AT VARNAMES 0) - - # Test both, given original name and uppercase version too - if(NOT ${VARNAME}_FOUND AND NOT ${VARNAMEU}_FOUND) - colormsg(_RED_ "WARNING: ${NAME} libraries not found, some features will be disabled.") - endif() - - foreach(variable ${VARNAMES}) - if(${VARNAMEU}_FOUND) - boost_report_value(${VARNAMEU}_${variable}) - elseif(${VARNAME}_FOUND) - boost_report_value(${VARNAME}_${variable}) - endif() - endforeach() -endmacro() - -# -# Some externals default to OFF -# -option(WITH_VALGRIND "Run tests under valgrind" OFF) - -# -# Detect available dependencies -# -foreach(external ${SOCI_ALL_DEPENDENCIES}) - string(TOUPPER "${external}" EXTERNAL) - - # For historical reasons we use both WITH_xxx and SOCI_xxx options, and - # setting either of them to e.g. OFF should disable the corresponding - # dependency. - option(WITH_${EXTERNAL} "Attempt to find and configure ${external}" ON) - option(SOCI_${EXTERNAL} "Attempt to build ${external} backend" ON) - - if(NOT WITH_${EXTERNAL}) - set(disabled_var "WITH") - elseif(NOT SOCI_${EXTERNAL}) - set(disabled_var "SOCI") - endif() - - if(NOT DEFINED disabled_var) - colormsg(HICYAN "${external}:") - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies/${external}.cmake) - else() - set(${EXTERNAL}_FOUND FALSE CACHE BOOL "${external} found" FORCE) - colormsg(HICYAN "${external}:" YELLOW "disabled, since ${disabled_var}_${EXTERNAL}=OFF") - unset(disabled_var) - endif() - - if(NOT ${EXTERNAL}_FOUND) - set(SOCI_${EXTERNAL} OFF) - endif() -endforeach() diff --git a/cmake/SociUtilities.cmake b/cmake/SociUtilities.cmake deleted file mode 100644 index 627ecfb1f..000000000 --- a/cmake/SociUtilities.cmake +++ /dev/null @@ -1,436 +0,0 @@ -################################################################################ -# SociUtilities.cmake - part of CMake configuration of SOCI library -# -# Based on BoostUtilities.cmake from CMake configuration for Boost -################################################################################ -# Copyright (C) 2007 Douglas Gregor -# Copyright (C) 2007 Troy Straszheim -# Copyright (C) 2010-2013 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 -################################################################################ -# Macros in this module: -# -# list_contains: Determine whether a string value is in a list. -# -# car: Return the first element in a list -# -# cdr: Return all but the first element in a list -# -# parse_arguments: Parse keyword arguments for use in other macros. -# -# soci_check_package_found: Test varname-FOUND for case-insensitive varname -# -################################################################################ - -# This utility macro determines whether a particular string value -# occurs within a list of strings: -# -# list_contains(result string_to_find arg1 arg2 arg3 ... argn) -# -# This macro sets the variable named by result equal to TRUE if -# string_to_find is found anywhere in the following arguments. -macro(list_contains var value) - set(${var}) - foreach (value2 ${ARGN}) - if (${value} STREQUAL ${value2}) - set(${var} TRUE) - endif (${value} STREQUAL ${value2}) - endforeach (value2) -endmacro(list_contains) - -# This utility macro extracts the first argument from the list of -# arguments given, and places it into the variable named var. -# -# car(var arg1 arg2 ...) -macro(car var) - set(${var} ${ARGV1}) -endmacro(car) - -# This utility macro extracts all of the arguments given except the -# first, and places them into the variable named var. -# -# car(var arg1 arg2 ...) -macro(cdr var junk) - set(${var} ${ARGN}) -endmacro(cdr) - -# The parse_arguments macro will take the arguments of another macro and -# define several variables. The first argument to parse_arguments is a -# prefix to put on all variables it creates. The second argument is a -# list of names, and the third argument is a list of options. Both of -# these lists should be quoted. The rest of parse_arguments are -# arguments from another macro to be parsed. -# -# parse_arguments(prefix arg_names options arg1 arg2...) -# -# For each item in options, parse_arguments will create a variable with -# that name, prefixed with prefix_. So, for example, if prefix is -# MY_MACRO and options is OPTION1;OPTION2, then parse_arguments will -# create the variables MY_MACRO_OPTION1 and MY_MACRO_OPTION2. These -# variables will be set to true if the option exists in the command line -# or false otherwise. -# -# For each item in arg_names, parse_arguments will create a variable -# with that name, prefixed with prefix_. Each variable will be filled -# with the arguments that occur after the given arg_name is encountered -# up to the next arg_name or the end of the arguments. All options are -# removed from these lists. parse_arguments also creates a -# prefix_DEFAULT_ARGS variable containing the list of all arguments up -# to the first arg_name encountered. -macro(parse_arguments prefix arg_names option_names) - set(DEFAULT_ARGS) - foreach(arg_name ${arg_names}) - set(${prefix}_${arg_name}) - endforeach(arg_name) - foreach(option ${option_names}) - set(${prefix}_${option} FALSE) - endforeach(option) - - set(current_arg_name DEFAULT_ARGS) - set(current_arg_list) - foreach(arg ${ARGN}) - list_contains(is_arg_name ${arg} ${arg_names}) - if (is_arg_name) - set(${prefix}_${current_arg_name} ${current_arg_list}) - set(current_arg_name ${arg}) - set(current_arg_list) - else (is_arg_name) - list_contains(is_option ${arg} ${option_names}) - if (is_option) - set(${prefix}_${arg} TRUE) - else (is_option) - set(current_arg_list ${current_arg_list} ${arg}) - endif (is_option) - endif (is_arg_name) - endforeach(arg) - set(${prefix}_${current_arg_name} ${current_arg_list}) -endmacro(parse_arguments) - -# Perform a reverse topological sort on the given LIST. -# -# topological_sort(my_list "MY_" "_EDGES") -# -# LIST is the name of a variable containing a list of elements to be -# sorted in reverse topological order. Each element in the list has a -# set of outgoing edges (for example, those other list elements that -# it depends on). In the resulting reverse topological ordering -# (written back into the variable named LIST), an element will come -# later in the list than any of the elements that can be reached by -# following its outgoing edges and the outgoing edges of any vertices -# they target, recursively. Thus, if the edges represent dependencies -# on build targets, for example, the reverse topological ordering is -# the order in which one would build those targets. -# -# For each element E in this list, the edges for E are contained in -# the variable named ${PREFIX}${E}${SUFFIX}, where E is the -# upper-cased version of the element in the list. If no such variable -# exists, then it is assumed that there are no edges. For example, if -# my_list contains a, b, and c, one could provide a dependency graph -# using the following variables: -# -# MY_A_EDGES b -# MY_B_EDGES -# MY_C_EDGES a b -# -# With the involcation of topological_sort shown above and these -# variables, the resulting reverse topological ordering will be b, a, -# c. -function(topological_sort LIST PREFIX SUFFIX) - # Clear the stack and output variable - set(VERTICES "${${LIST}}") - set(STACK) - set(${LIST}) - - # Loop over all of the vertices, starting the topological sort from - # each one. - foreach(VERTEX ${VERTICES}) - string(TOUPPER ${VERTEX} UPPER_VERTEX) - - # If we haven't already processed this vertex, start a depth-first - # search from where. - if (NOT FOUND_${UPPER_VERTEX}) - # Push this vertex onto the stack with all of its outgoing edges - string(REPLACE ";" " " NEW_ELEMENT - "${VERTEX};${${PREFIX}${UPPER_VERTEX}${SUFFIX}}") - list(APPEND STACK ${NEW_ELEMENT}) - - # We've now seen this vertex - set(FOUND_${UPPER_VERTEX} TRUE) - - # While the depth-first search stack is not empty - list(LENGTH STACK STACK_LENGTH) - while(STACK_LENGTH GREATER 0) - # Remove the vertex and its remaining out-edges from the top - # of the stack - list(GET STACK -1 OUT_EDGES) - list(REMOVE_AT STACK -1) - - # Get the source vertex and the list of out-edges - separate_arguments(OUT_EDGES) - list(GET OUT_EDGES 0 SOURCE) - list(REMOVE_AT OUT_EDGES 0) - - # While there are still out-edges remaining - list(LENGTH OUT_EDGES OUT_DEGREE) - while (OUT_DEGREE GREATER 0) - # Pull off the first outgoing edge - list(GET OUT_EDGES 0 TARGET) - list(REMOVE_AT OUT_EDGES 0) - - string(TOUPPER ${TARGET} UPPER_TARGET) - if (NOT FOUND_${UPPER_TARGET}) - # We have not seen the target before, so we will traverse - # its outgoing edges before coming back to our - # source. This is the key to the depth-first traversal. - - # We've now seen this vertex - set(FOUND_${UPPER_TARGET} TRUE) - - # Push the remaining edges for the current vertex onto the - # stack - string(REPLACE ";" " " NEW_ELEMENT - "${SOURCE};${OUT_EDGES}") - list(APPEND STACK ${NEW_ELEMENT}) - - # Setup the new source and outgoing edges - set(SOURCE ${TARGET}) - string(TOUPPER ${SOURCE} UPPER_SOURCE) - set(OUT_EDGES - ${${PREFIX}${UPPER_SOURCE}${SUFFIX}}) - endif(NOT FOUND_${UPPER_TARGET}) - - list(LENGTH OUT_EDGES OUT_DEGREE) - endwhile (OUT_DEGREE GREATER 0) - - # We have finished all of the outgoing edges for - # SOURCE; add it to the resulting list. - list(APPEND ${LIST} ${SOURCE}) - - # Check the length of the stack - list(LENGTH STACK STACK_LENGTH) - endwhile(STACK_LENGTH GREATER 0) - endif (NOT FOUND_${UPPER_VERTEX}) - endforeach(VERTEX) - - set(${LIST} ${${LIST}} PARENT_SCOPE) -endfunction(topological_sort) - -# Small little hack that tweaks a component name (as used for CPack) -# to make sure to avoid certain names that cause problems. Sets the -# variable named varname to the "sanitized" name. -# -# FIXME: This is a complete hack. We probably need to fix the CPack -# generators (NSIS in particular) to get rid of the need for this. -macro(fix_cpack_component_name varname name) - if (${name} STREQUAL "foreach") - set(${varname} "boost_foreach") - else() - set(${varname} ${name}) - endif() -endmacro() - - -# -# A big shout out to the cmake gurus @ compiz -# -function (colormsg) - string (ASCII 27 _escape) - set(WHITE "29") - set(GRAY "30") - set(RED "31") - set(GREEN "32") - set(YELLOW "33") - set(BLUE "34") - set(MAG "35") - set(CYAN "36") - - foreach (color WHITE GRAY RED GREEN YELLOW BLUE MAG CYAN) - set(HI${color} "1\;${${color}}") - set(LO${color} "2\;${${color}}") - set(_${color}_ "4\;${${color}}") - set(_HI${color}_ "1\;4\;${${color}}") - set(_LO${color}_ "2\;4\;${${color}}") - endforeach() - - set(str "") - set(coloron FALSE) - foreach(arg ${ARGV}) - if (NOT ${${arg}} STREQUAL "") - if (CMAKE_COLOR_DIAGNOSTICS) - set(str "${str}${_escape}[${${arg}}m") - set(coloron TRUE) - endif() - else() - set(str "${str}${arg}") - if (coloron) - set(str "${str}${_escape}[0m") - set(coloron FALSE) - endif() - set(str "${str} ") - endif() - endforeach() - message(STATUS ${str}) -endfunction() - -# colormsg("Colors:" -# WHITE "white" GRAY "gray" GREEN "green" -# RED "red" YELLOW "yellow" BLUE "blue" MAG "mag" CYAN "cyan" -# _WHITE_ "white" _GRAY_ "gray" _GREEN_ "green" -# _RED_ "red" _YELLOW_ "yellow" _BLUE_ "blue" _MAG_ "mag" _CYAN_ "cyan" -# _HIWHITE_ "white" _HIGRAY_ "gray" _HIGREEN_ "green" -# _HIRED_ "red" _HIYELLOW_ "yellow" _HIBLUE_ "blue" _HIMAG_ "mag" _HICYAN_ "cyan" -# HIWHITE "white" HIGRAY "gray" HIGREEN "green" -# HIRED "red" HIYELLOW "yellow" HIBLUE "blue" HIMAG "mag" HICYAN "cyan" -# "right?") - -# -# pretty-prints the value of a variable so that the -# equals signs align -# -function(boost_report_value NAME) - string(LENGTH "${NAME}" varlen) - # LOG - #message(STATUS "boost_report_value: NAME=${NAME} (${varlen})") - #message(STATUS "boost_report_value: \${NAME}=${${NAME}}") - math(EXPR padding_len 40-${varlen}) - string(SUBSTRING " " - 0 ${padding_len} varpadding) - colormsg("${NAME}${varpadding} = ${${NAME}}") -endfunction() - -function(boost_message_value NAME) - string(LENGTH "${NAME}" varlen) - math(EXPR padding_len 40-${varlen}) - string(SUBSTRING " " - 0 ${padding_len} varpadding) - message(STATUS "${NAME}${varpadding} = ${${NAME}}") -endfunction() - -function(trace NAME) - if(BOOST_CMAKE_TRACE) - string(LENGTH "${NAME}" varlen) - math(EXPR padding_len 40-${varlen}) - string(SUBSTRING "........................................" - 0 ${padding_len} varpadding) - message("${NAME} ${varpadding} ${${NAME}}") - endif() -endfunction() - -# -# pretty-prints the value of a variable so that the -# equals signs align -# -function(boost_report_pretty PRETTYNAME VARNAME) - string(LENGTH "${PRETTYNAME}" varlen) - math(EXPR padding_len 30-${varlen}) - string(SUBSTRING " " - 0 ${padding_len} varpadding) - message(STATUS "${PRETTYNAME}${varpadding} = ${${VARNAME}}") -endfunction() - -# -# assert that ARG is actually a library target -# -macro(dependency_check ARG) - trace(ARG) - if (NOT "${ARG}" STREQUAL "") - get_target_property(deptype ${ARG} TYPE) - if(NOT deptype MATCHES ".*_LIBRARY$") - set(DEPENDENCY_OKAY FALSE) - list(APPEND DEPENDENCY_FAILURES ${ARG}) - endif() - endif() -endmacro() - -# -# Tests package-FOUND for varname in three cases as given, lowercase and -# uppercase. -# -macro(soci_check_package_found NAME SUCCESS) - - set(${SUCCESS} FALSE) - set(VARNAME ${NAME}) - set(VARNAME_SUCCESS ${${VARNAME}_FOUND}) - - # Test both, given original name and uppercase version too - if(VARNAME_SUCCESS) - set(${SUCCESS} TRUE) - else() - string(TOUPPER ${NAME} VARNAME) - set(VARNAME_SUCCESS ${${VARNAME}_FOUND}) - if(VARNAME_SUCCESS) - set(${SUCCESS} TRUE) - endif() - endif() - - #message(STATUS "soci_check_package_found: ${SUCCESS}=${${SUCCESS}}") -endmacro() - -# -# Pretty-print of given property of current directory. -# -function(soci_report_directory_property PROPNAME) - get_directory_property(${PROPNAME} ${PROPNAME}) - boost_report_value(${PROPNAME}) -endfunction() - -# -# Scans the current directory and returns a list of subdirectories. -# Author: Robert Fleming -# Source: http://www.cmake.org/pipermail/cmake/2008-February/020114.html -# -# Third parameter is 1 if you want relative paths returned. -# Usage: list_subdirectories(the_list_is_returned_here /path/to/project TRUE) -# -macro(list_subdirectories retval curdir return_relative) - file(GLOB sub-dir RELATIVE ${curdir} *) - set(list_of_dirs "") - foreach(dir ${sub-dir}) - if(IS_DIRECTORY ${curdir}/${dir}) - if (${return_relative}) - set(list_of_dirs ${list_of_dirs} ${dir}) - else() - set(list_of_dirs ${list_of_dirs} ${curdir}/${dir}) - endif() - endif() - endforeach() - set(${retval} ${list_of_dirs}) -endmacro() - -# -# Generates output name for given target depending on platform and version. -# For instance, on Windows, libraries get ABI version suffix soci_coreXY.{dll|lib}. -# -function(soci_target_output_name TARGET_NAME OUTPUT_NAME) - if(NOT DEFINED TARGET_NAME) - message(SEND_ERROR "Error, the variable TARGET_NAME is not defined!") - endif() - - if(NOT DEFINED ${PROJECT_NAME}_VERSION) - message(SEND_ERROR "Error, the variable ${${PROJECT_NAME}_VERSION} is not defined!") - endif() - - # On Windows, ABI version is specified using binary file name suffix. - # On Unix, suffix is empty and SOVERSION is used instead. - if (WIN32) - string(LENGTH "${${PROJECT_NAME}_ABI_VERSION}" abilen) - if(abilen GREATER 0) - set(SUFFIX "_${${PROJECT_NAME}_ABI_VERSION}") - endif() - endif() - - set(${OUTPUT_NAME} ${TARGET_NAME}${SUFFIX} PARENT_SCOPE) -endfunction() - -# Check if the given linker is supported and use it if it is. -function(soci_use_ld_if_supported ld) - include(CheckCXXCompilerFlag) - set(ld_flag "-fuse-ld=${ld}") - check_cxx_compiler_flag(${ld_flag} can_use_ld) - if (can_use_ld) - add_link_options(${ld_flag}) - endif() -endfunction() diff --git a/cmake/SociVersion.cmake b/cmake/SociVersion.cmake deleted file mode 100644 index 5c8b5d75a..000000000 --- a/cmake/SociVersion.cmake +++ /dev/null @@ -1,62 +0,0 @@ -################################################################################ -# SociVersion.cmake - part of CMake configuration of 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) -################################################################################ -# Macros in this module: -# -# soci_version - defines version information for SOCI library -# -################################################################################ - -# Defines version information for SOCI library -# -# soci_version(MAJOR major_version MINOR minor_version PATCH patch_level) -# -# MAJOR.MINOR version is used to set SOVERSION -# -macro(soci_version) - # get version from soci/version.h - file( - STRINGS - "${PROJECT_SOURCE_DIR}/include/soci/version.h" - _VERSION - REGEX - "#define SOCI_VERSION ([0-9]+)" - ) - string(REGEX MATCH "([0-9]+)" _VERSION "${_VERSION}") - - math(EXPR ${PROJECT_NAME}_VERSION_MAJOR "${_VERSION} / 100000") - math(EXPR ${PROJECT_NAME}_VERSION_MINOR "${_VERSION} / 100 % 1000") - math(EXPR ${PROJECT_NAME}_VERSION_PATCH "${_VERSION} % 100") - - # Set VERSION string - set(${PROJECT_NAME}_VERSION - "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}") - - # Set SOVERSION based on major and minor - set(${PROJECT_NAME}_SOVERSION - "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}") - - # Set ABI version string used to name binary output and, by SOCI loader, to find binaries. - # On Windows, ABI version is specified using binary file name suffix. - # On Unix, suffix ix empty and SOVERSION is used instead. - if (UNIX) - set(${PROJECT_NAME}_ABI_VERSION ${${PROJECT_NAME}_SOVERSION}) - elseif(WIN32) - set(${PROJECT_NAME}_ABI_VERSION - "${${PROJECT_NAME}_VERSION_MAJOR}_${${PROJECT_NAME}_VERSION_MINOR}") - else() - message(FATAL_ERROR "Ambiguous target platform with unknown ABI version scheme. Giving up.") - endif() - - boost_report_value(${PROJECT_NAME}_VERSION) - boost_report_value(${PROJECT_NAME}_ABI_VERSION) - - add_definitions(-DSOCI_ABI_VERSION="${${PROJECT_NAME}_ABI_VERSION}") - -endmacro() diff --git a/cmake/configs/test-access.cmake b/cmake/configs/test-access.cmake deleted file mode 100644 index abc44deb0..000000000 --- a/cmake/configs/test-access.cmake +++ /dev/null @@ -1,13 +0,0 @@ -[ODBC] -DRIVER=Microsoft Access Driver (*.mdb, *.accdb) -UID=admin -UserCommitSync=Yes -Threads=3 -SafeTransactions=0 -PageTimeout=5 -MaxScanRows=8 -MaxBufferSize=2048 -FIL=MS Access -DriverId=25 -DefaultDir=@TEST_ACCESS_PATH@\tests\odbc -DBQ=@TEST_ACCESS_PATH@\tests\odbc\soci_test.mdb diff --git a/cmake/configs/test-mysql.cmake b/cmake/configs/test-mysql.cmake deleted file mode 100644 index 48e54033d..000000000 --- a/cmake/configs/test-mysql.cmake +++ /dev/null @@ -1,4 +0,0 @@ -[ODBC] -DRIVER=@MYSQL_DRIVER_NAME@ -DATABASE=soci_test -OPTION=0 diff --git a/cmake/dependencies/Boost.cmake b/cmake/dependencies/Boost.cmake deleted file mode 100644 index 366d32b74..000000000 --- a/cmake/dependencies/Boost.cmake +++ /dev/null @@ -1,13 +0,0 @@ -set(Boost_FIND_QUIETLY TRUE) - -set(Boost_USE_MULTITHREADED ON) -find_package(Boost 1.33.1 COMPONENTS date_time) - -if (NOT Boost_DATE_TIME_FOUND) - find_package(Boost 1.33.1) -endif() - -set(Boost_RELEASE_VERSION - "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - -boost_external_report(Boost RELEASE_VERSION INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/DB2.cmake b/cmake/dependencies/DB2.cmake deleted file mode 100644 index 063f1a17d..000000000 --- a/cmake/dependencies/DB2.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(DB2_FIND_QUIETLY TRUE) - -find_package(DB2) - -boost_external_report(DB2 INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/Firebird.cmake b/cmake/dependencies/Firebird.cmake deleted file mode 100644 index b072702b0..000000000 --- a/cmake/dependencies/Firebird.cmake +++ /dev/null @@ -1,9 +0,0 @@ -option(SOCI_FIREBIRD_EMBEDDED "Use embedded library in Firebird backend" OFF) -boost_report_value(SOCI_FIREBIRD_EMBEDDED) - -set(Firebird_FIND_QUIETLY TRUE) - -find_package(Firebird) - -boost_external_report(Firebird INCLUDE_DIR LIBRARIES VERSION) - diff --git a/cmake/dependencies/MySQL.cmake b/cmake/dependencies/MySQL.cmake deleted file mode 100644 index 5599b0885..000000000 --- a/cmake/dependencies/MySQL.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(MySQL_FIND_QUIETLY TRUE) - -find_package(MySQL) - -boost_external_report(MySQL INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/ODBC.cmake b/cmake/dependencies/ODBC.cmake deleted file mode 100644 index 88b85a1c5..000000000 --- a/cmake/dependencies/ODBC.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(ODBC_FIND_QUIETLY TRUE) - -find_package(ODBC) - -boost_external_report(ODBC INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/Oracle.cmake b/cmake/dependencies/Oracle.cmake deleted file mode 100644 index 32cc649ea..000000000 --- a/cmake/dependencies/Oracle.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(Oracle_FIND_QUIETLY TRUE) - -find_package(Oracle) - -boost_external_report(Oracle INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/PostgreSQL.cmake b/cmake/dependencies/PostgreSQL.cmake deleted file mode 100644 index c6f215425..000000000 --- a/cmake/dependencies/PostgreSQL.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(PostgreSQL_FIND_QUIETLY TRUE) - -find_package(PostgreSQL) - -boost_external_report(PostgreSQL INCLUDE_DIRS LIBRARIES VERSION) diff --git a/cmake/dependencies/SQLite3.cmake b/cmake/dependencies/SQLite3.cmake deleted file mode 100644 index 37f57f614..000000000 --- a/cmake/dependencies/SQLite3.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(SQLite3_FIND_QUIETLY TRUE) - -find_package(SQLite3) - -boost_external_report(SQLite3 INCLUDE_DIR LIBRARIES) diff --git a/cmake/dependencies/Threads.cmake b/cmake/dependencies/Threads.cmake deleted file mode 100644 index 6a5487554..000000000 --- a/cmake/dependencies/Threads.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(Threads_FIND_QUIETLY TRUE) - -find_package(Threads) -boost_report_value(CMAKE_THREAD_LIBS_INIT) diff --git a/cmake/modules/FindDB2.cmake b/cmake/find_modules/FindDB2.cmake similarity index 100% rename from cmake/modules/FindDB2.cmake rename to cmake/find_modules/FindDB2.cmake diff --git a/cmake/modules/FindFirebird.cmake b/cmake/find_modules/FindFirebird.cmake similarity index 100% rename from cmake/modules/FindFirebird.cmake rename to cmake/find_modules/FindFirebird.cmake diff --git a/cmake/modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake similarity index 100% rename from cmake/modules/FindMySQL.cmake rename to cmake/find_modules/FindMySQL.cmake diff --git a/cmake/modules/FindOracle.cmake b/cmake/find_modules/FindOracle.cmake similarity index 100% rename from cmake/modules/FindOracle.cmake rename to cmake/find_modules/FindOracle.cmake diff --git a/cmake/modules/FindSoci.cmake b/cmake/find_modules/FindSoci.cmake similarity index 100% rename from cmake/modules/FindSoci.cmake rename to cmake/find_modules/FindSoci.cmake diff --git a/cmake/modules/FindDL.cmake b/cmake/modules/FindDL.cmake deleted file mode 100644 index 3f7f889f3..000000000 --- a/cmake/modules/FindDL.cmake +++ /dev/null @@ -1,21 +0,0 @@ -if(DL_INCLUDE_DIR) - set(DL_FIND_QUIETLY TRUE) -endif() - -find_path(DL_INCLUDE_DIR dlfcn.h) -find_library(DL_LIBRARY NAMES dl) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(DL DEFAULT_MSG DL_LIBRARY DL_INCLUDE_DIR) - -if(NOT DL_FOUND) - # if dlopen can be found without linking in dl then, - # dlopen is part of libc, so don't need to link extra libs. - include(CheckFunctionExists) - check_function_exists(dlopen DL_FOUND) - set(DL_LIBRARY "") -endif() - -set(DL_LIBRARIES ${DL_LIBRARY}) - -mark_as_advanced(DL_LIBRARY DL_INCLUDE_DIR) diff --git a/cmake/modules/FindODBC.cmake b/cmake/modules/FindODBC.cmake deleted file mode 100644 index 67e6237c7..000000000 --- a/cmake/modules/FindODBC.cmake +++ /dev/null @@ -1,242 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindODBC --------- - -Find the ODBC include directory and library. - -Use this module by invoking find_package with the form:: - -.. code-block:: cmake - - find_package(ODBC - [REQUIRED] # Fail with error if ODBC is not found - ) - -On Windows, when building with Visual Studio, this module assumes the ODBC -library is provided by the available Windows SDK. - -On Unix, this module allows to search for ODBC library provided by -unixODBC or iODBC implementations of ODBC API. -This module reads hint about location of the config program: - -.. variable:: ODBC_CONFIG - - Location of odbc_config or iodbc-config program - -Otherwise, this module tries to find the config program, -first from unixODBC, then from iODBC. -If no config program found, this module searches for ODBC header -and library in list of known locations. - -Imported targets -^^^^^^^^^^^^^^^^ - -This module defines the following :prop_tgt:`IMPORTED` targets: - -.. variable:: ODBC::ODBC - - Imported target for using the ODBC library, if found. - -Result variables -^^^^^^^^^^^^^^^^ - -.. variable:: ODBC_FOUND - - Set to true if ODBC library found, otherwise false or undefined. - -.. variable:: ODBC_INCLUDE_DIRS - - Paths to include directories listed in one variable for use by ODBC client. - May be empty on Windows, where the include directory corresponding to the - expected Windows SDK is already available in the compilation environment. - -.. variable:: ODBC_LIBRARIES - - Paths to libraries to linked against to use ODBC. - May just a library name on Windows, where the library directory corresponding - to the expected Windows SDK is already available in the compilation environment. - -.. variable:: ODBC_CONFIG - - Path to unixODBC or iODBC config program, if found or specified. - -Cache variables -^^^^^^^^^^^^^^^ - -For users who wish to edit and control the module behavior, this module -reads hints about search locations from the following variables:: - -.. variable:: ODBC_INCLUDE_DIR - - Path to ODBC include directory with ``sql.h`` header. - -.. variable:: ODBC_LIBRARY - - Path to ODBC library to be linked. - -NOTE: The variables above should not usually be used in CMakeLists.txt files! - -Limitations -^^^^^^^^^^^ - -On Windows, this module does not search for iODBC. -On Unix, there is no way to prefer unixODBC over iODBC, or vice versa, -other than providing the config program location using the ``ODBC_CONFIG``. -This module does not allow to search for a specific ODBC driver. - -#]=======================================================================] - -### Try Windows Kits ########################################################## -if(WIN32) - # List names of ODBC libraries on Windows - set(ODBC_LIBRARY odbc32.lib) - set(_odbc_lib_names odbc32;) - - # List additional libraries required to use ODBC library - if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(_odbc_required_libs_names odbccp32;ws2_32) - elseif(MINGW) - set(_odbc_required_libs_names odbccp32) - endif() -endif() - -### Try unixODBC or iODBC config program ###################################### -if (UNIX AND NOT ODBC_CONFIG) - find_program(ODBC_CONFIG - NAMES odbc_config iodbc-config - DOC "Path to unixODBC or iODBC config program") -endif() - -if (UNIX AND ODBC_CONFIG) - # unixODBC and iODBC accept unified command line options - execute_process(COMMAND ${ODBC_CONFIG} --cflags - OUTPUT_VARIABLE _cflags OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${ODBC_CONFIG} --libs - OUTPUT_VARIABLE _libs OUTPUT_STRIP_TRAILING_WHITESPACE) - - # Collect paths of include directories from CFLAGS - separate_arguments(_cflags NATIVE_COMMAND "${_cflags}") - foreach(arg IN LISTS _cflags) - if("${arg}" MATCHES "^-I(.*)$") - list(APPEND _odbc_include_paths "${CMAKE_MATCH_1}") - endif() - endforeach() - unset(_cflags) - - # Collect paths of library names and directories from LIBS - separate_arguments(_libs NATIVE_COMMAND "${_libs}") - foreach(arg IN LISTS _libs) - if("${arg}" MATCHES "^-L(.*)$") - list(APPEND _odbc_lib_paths "${CMAKE_MATCH_1}") - elseif("${arg}" MATCHES "^-l(.*)$") - set(_lib_name ${CMAKE_MATCH_1}) - string(REGEX MATCH "odbc" _is_odbc ${_lib_name}) - if(_is_odbc) - list(APPEND _odbc_lib_names ${_lib_name}) - else() - list(APPEND _odbc_required_libs_names ${_lib_name}) - endif() - unset(_lib_name) - endif() - endforeach() - unset(_libs) -endif() - -### Try unixODBC or iODBC in include/lib filesystems ########################## -if (UNIX AND NOT ODBC_CONFIG) - # List names of both ODBC libraries, unixODBC and iODBC - set(_odbc_lib_names odbc;iodbc;unixodbc;) - - set(_odbc_include_paths - /usr/local/odbc/include) - - set(_odbc_lib_paths - /usr/local/odbc/lib) -endif() - -# DEBUG -#message("ODBC_CONFIG=${ODBC_CONFIG}") -#message("_odbc_include_hints=${_odbc_include_hints}") -#message("_odbc_include_paths=${_odbc_include_paths}") -#message("_odbc_lib_paths=${_odbc_lib_paths}") -#message("_odbc_lib_names=${_odbc_lib_names}") - -### Find include directories ################################################## -find_path(ODBC_INCLUDE_DIR - NAMES sql.h - HINTS ${_odbc_include_hints} - PATHS ${_odbc_include_paths}) - -if(NOT ODBC_INCLUDE_DIR AND WIN32) - set(ODBC_INCLUDE_DIR "") -endif() - -### Find libraries ############################################################ -if(NOT ODBC_LIBRARY) - find_library(ODBC_LIBRARY - NAMES ${_odbc_lib_names} - PATHS ${_odbc_lib_paths} - PATH_SUFFIXES odbc) - - foreach(_lib IN LISTS _odbc_required_libs_names) - find_library(_lib_path - NAMES ${_lib} - PATHS ${_odbc_lib_paths} # system parths or collected from ODBC_CONFIG - PATH_SUFFIXES odbc) - if (_lib_path) - list(APPEND _odbc_required_libs_paths ${_lib_path}) - endif() - unset(_lib_path CACHE) - endforeach() - - unset(_odbc_lib_names) - unset(_odbc_lib_paths) - unset(_odbc_required_libs_names) -endif() - -### Set result variables ###################################################### -set(REQUIRED_VARS ODBC_LIBRARY) -if(NOT WIN32) - list(APPEND REQUIRED_VARS ODBC_INCLUDE_DIR) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(ODBC DEFAULT_MSG ${REQUIRED_VARS}) - -mark_as_advanced(FORCE ODBC_LIBRARY ODBC_INCLUDE_DIR) - -if(ODBC_CONFIG) - mark_as_advanced(FORCE ODBC_CONFIG) -endif() - -set(ODBC_INCLUDE_DIRS ${ODBC_INCLUDE_DIR}) -list(APPEND ODBC_LIBRARIES ${ODBC_LIBRARY}) -list(APPEND ODBC_LIBRARIES ${_odbc_required_libs_paths}) - -### Import targets ############################################################ -if(ODBC_FOUND) - if(NOT TARGET ODBC::ODBC) - if(IS_ABSOLUTE "${ODBC_LIBRARY}") - add_library(ODBC::ODBC UNKNOWN IMPORTED) - set_target_properties(ODBC::ODBC PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${ODBC_LIBRARY}") - else() - add_library(ODBC::ODBC INTERFACE IMPORTED) - set_target_properties(ODBC::ODBC PROPERTIES - IMPORTED_LIBNAME "${ODBC_LIBRARY}") - endif() - set_target_properties(ODBC::ODBC PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${ODBC_INCLUDE_DIR}") - - if(_odbc_required_libs_paths) - set_property(TARGET ODBC::ODBC APPEND PROPERTY - INTERFACE_LINK_LIBRARIES "${_odbc_required_libs_paths}") - endif() - endif() -endif() - -unset(_odbc_required_libs_paths) diff --git a/cmake/modules/FindPostgreSQL.cmake b/cmake/modules/FindPostgreSQL.cmake deleted file mode 100644 index e795f2e7a..000000000 --- a/cmake/modules/FindPostgreSQL.cmake +++ /dev/null @@ -1,191 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#.rst: -# FindPostgreSQL -# -------------- -# -# Find the PostgreSQL installation. -# -# This module defines -# -# :: -# -# POSTGRESQL_LIBRARIES - the PostgreSQL libraries needed for linking -# POSTGRESQL_INCLUDE_DIRS - the directories of the PostgreSQL headers -# POSTGRESQL_LIBRARY_DIRS - the link directories for PostgreSQL libraries -# POSTGRESQL_VERSION_STRING - the version of PostgreSQL found (since CMake 2.8.8) - -# ---------------------------------------------------------------------------- -# History: -# This module is derived from the module originally found in the VTK source tree. -# -# ---------------------------------------------------------------------------- -# Note: -# POSTGRESQL_ADDITIONAL_VERSIONS is a variable that can be used to set the -# version mumber of the implementation of PostgreSQL. -# In Windows the default installation of PostgreSQL uses that as part of the path. -# E.g C:\Program Files\PostgreSQL\8.4. -# Currently, the following version numbers are known to this module: -# "14" "13" "12" "11" "10" "9.6" "9.5" "9.4" "9.3" "9.2" "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0" -# -# To use this variable just do something like this: -# set(POSTGRESQL_ADDITIONAL_VERSIONS "9.2" "8.4.4") -# before calling find_package(PostgreSQL) in your CMakeLists.txt file. -# This will mean that the versions you set here will be found first in the order -# specified before the default ones are searched. -# -# ---------------------------------------------------------------------------- -# You may need to manually set: -# POSTGRESQL_INCLUDE_DIR - the path to where the PostgreSQL include files are. -# POSTGRESQL_LIBRARY_DIR - The path to where the PostgreSQL library files are. -# If FindPostgreSQL.cmake cannot find the include files or the library files. -# -# ---------------------------------------------------------------------------- -# The following variables are set if PostgreSQL is found: -# POSTGRESQL_FOUND - Set to true when PostgreSQL is found. -# POSTGRESQL_INCLUDE_DIRS - Include directories for PostgreSQL -# POSTGRESQL_LIBRARY_DIRS - Link directories for PostgreSQL libraries -# POSTGRESQL_LIBRARIES - The PostgreSQL libraries. -# -# ---------------------------------------------------------------------------- -# If you have installed PostgreSQL in a non-standard location. -# (Please note that in the following comments, it is assumed that -# points to the root directory of the include directory of PostgreSQL.) -# Then you have three options. -# 1) After CMake runs, set POSTGRESQL_INCLUDE_DIR to /include and -# POSTGRESQL_LIBRARY_DIR to wherever the library pq (or libpq in windows) is -# 2) Use CMAKE_INCLUDE_PATH to set a path to /PostgreSQL<-version>. This will allow find_path() -# to locate POSTGRESQL_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g. In your CMakeLists.txt file -# set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/include") -# 3) Set an environment variable called ${POSTGRESQL_ROOT} that points to the root of where you have -# installed PostgreSQL, e.g. . -# -# ---------------------------------------------------------------------------- - -set(POSTGRESQL_INCLUDE_PATH_DESCRIPTION "top-level directory containing the PostgreSQL include directories. E.g /usr/local/include/PostgreSQL/8.4 or C:/Program Files/PostgreSQL/8.4/include") -set(POSTGRESQL_INCLUDE_DIR_MESSAGE "Set the POSTGRESQL_INCLUDE_DIR cmake cache entry to the ${POSTGRESQL_INCLUDE_PATH_DESCRIPTION}") -set(POSTGRESQL_LIBRARY_PATH_DESCRIPTION "top-level directory containing the PostgreSQL libraries.") -set(POSTGRESQL_LIBRARY_DIR_MESSAGE "Set the POSTGRESQL_LIBRARY_DIR cmake cache entry to the ${POSTGRESQL_LIBRARY_PATH_DESCRIPTION}") -set(POSTGRESQL_ROOT_DIR_MESSAGE "Set the POSTGRESQL_ROOT system variable to where PostgreSQL is found on the machine E.g C:/Program Files/PostgreSQL/8.4") - - -set(POSTGRESQL_KNOWN_VERSIONS ${POSTGRESQL_ADDITIONAL_VERSIONS} - "14" "13" "12" "11" "10" "9.6" "9.5" "9.4" "9.3" "9.2" "9.1" "9.0" "8.4" "8.3" "8.2" "8.1" "8.0") - -# Define additional search paths for root directories. -set( POSTGRESQL_ROOT_DIRECTORIES - ENV POSTGRESQL_ROOT - ${POSTGRESQL_ROOT} -) -foreach(suffix ${POSTGRESQL_KNOWN_VERSIONS}) - if(WIN32) - list(APPEND POSTGRESQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES - "PostgreSQL/${suffix}/lib") - list(APPEND POSTGRESQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES - "PostgreSQL/${suffix}/include") - list(APPEND POSTGRESQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES - "PostgreSQL/${suffix}/include/server") - endif() - if(UNIX) - list(APPEND POSTGRESQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES - "pgsql-${suffix}/lib") - list(APPEND POSTGRESQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES - "pgsql-${suffix}/include") - list(APPEND POSTGRESQL_TYPE_ADDITIONAL_SEARCH_SUFFIXES - "postgresql/${suffix}/server" - "pgsql-${suffix}/include/server") - endif() - if(APPLE) - list(APPEND POSTGRESQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES - "postgresql@${suffix}") - list(APPEND POSTGRESQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES - "postgresql@${suffix}") - endif() -endforeach() - -# -# Look for an installation. -# -find_path(POSTGRESQL_INCLUDE_DIR - NAMES libpq-fe.h - PATHS - # Look in other places. - ${POSTGRESQL_ROOT_DIRECTORIES} - PATH_SUFFIXES - pgsql - postgresql - include - ${POSTGRESQL_INCLUDE_ADDITIONAL_SEARCH_SUFFIXES} - # Help the user find it if we cannot. - DOC "The ${POSTGRESQL_INCLUDE_DIR_MESSAGE}" -) - -# The PostgreSQL library. -set (POSTGRESQL_LIBRARY_TO_FIND pq) -# Setting some more prefixes for the library -set (POSTGRESQL_LIB_PREFIX "") -if ( WIN32 ) - set (POSTGRESQL_LIB_PREFIX ${POSTGRESQL_LIB_PREFIX} "lib") - set (POSTGRESQL_LIBRARY_TO_FIND ${POSTGRESQL_LIB_PREFIX}${POSTGRESQL_LIBRARY_TO_FIND}) -endif() - -function(__postgresql_find_library _name) - find_library(${_name} - NAMES ${ARGN} - PATHS - ${POSTGRESQL_ROOT_DIRECTORIES} - PATH_SUFFIXES - lib - ${POSTGRESQL_LIBRARY_ADDITIONAL_SEARCH_SUFFIXES} - # Help the user find it if we cannot. - DOC "The ${POSTGRESQL_LIBRARY_DIR_MESSAGE}" - ) -endfunction() - -__postgresql_find_library(POSTGRESQL_LIBRARY ${POSTGRESQL_LIBRARY_TO_FIND}) -__postgresql_find_library(POSTGRESQL_LIBRARY_DEBUG ${POSTGRESQL_LIBRARY_TO_FIND}d) -get_filename_component(POSTGRESQL_LIBRARY_DIR ${POSTGRESQL_LIBRARY} PATH) - -if (POSTGRESQL_INCLUDE_DIR) - # Some platforms include multiple pg_config.hs for multi-lib configurations - # This is a temporary workaround. A better solution would be to compile - # a dummy c file and extract the value of the symbol. - file(GLOB _PG_CONFIG_HEADERS "${POSTGRESQL_INCLUDE_DIR}/pg_config*.h") - foreach(_PG_CONFIG_HEADER ${_PG_CONFIG_HEADERS}) - if(EXISTS "${_PG_CONFIG_HEADER}") - file(STRINGS "${_PG_CONFIG_HEADER}" pgsql_version_str - REGEX "^#define[\t ]+PG_VERSION[\t ]+\".*\"") - if(pgsql_version_str) - string(REGEX REPLACE "^#define[\t ]+PG_VERSION[\t ]+\"([^\"]*)\".*" - "\\1" POSTGRESQL_VERSION_STRING "${pgsql_version_str}") - break() - endif() - endif() - endforeach() - unset(pgsql_version_str) -endif() - -# Did we find anything? -# -#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -include(FindPackageHandleStandardArgs) -# -find_package_handle_standard_args(PostgreSQL - REQUIRED_VARS POSTGRESQL_LIBRARY POSTGRESQL_INCLUDE_DIR - VERSION_VAR POSTGRESQL_VERSION_STRING) -set(POSTGRESQL_FOUND ${POSTGRESQL_FOUND}) - -# Now try to get the include and library path. -if(POSTGRESQL_FOUND) - set(POSTGRESQL_INCLUDE_DIRS ${POSTGRESQL_INCLUDE_DIR}) - set(POSTGRESQL_LIBRARY_DIRS ${POSTGRESQL_LIBRARY_DIR}) - if(POSTGRESQL_LIBRARY AND POSTGRESQL_LIBRARY_DEBUG) - set(POSTGRESQL_LIBRARIES optimized ${POSTGRESQL_LIBRARY} debug ${POSTGRESQL_LIBRARY_DEBUG}) - else() - set(POSTGRESQL_LIBRARIES ${POSTGRESQL_LIBRARY}) - endif() - set(POSTGRESQL_VERSION ${POSTGRESQL_VERSION_STRING}) -endif() - -mark_as_advanced(POSTGRESQL_INCLUDE_DIR POSTGRESQL_LIBRARY) diff --git a/cmake/modules/FindSQLite3.cmake b/cmake/modules/FindSQLite3.cmake deleted file mode 100644 index 885a6569c..000000000 --- a/cmake/modules/FindSQLite3.cmake +++ /dev/null @@ -1,66 +0,0 @@ -############################################################################### -# CMake module to search for SQLite 3 library -# -# On success, the macro sets the following variables: -# SQLITE3_FOUND = if the library found -# SQLITE3_LIBRARY = full path to the library -# SQLITE3_LIBRARIES = full path to the library -# SQLITE3_INCLUDE_DIR = where to find the library headers -# -# Copyright (c) 2009 Mateusz Loskot -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -############################################################################### - -find_path(SQLITE3_INCLUDE_DIR - NAMES sqlite3.h - PATH_PREFIXES sqlite sqlite3 - PATHS - /usr/include - /usr/local/include - $ENV{LIB_DIR}/include - $ENV{LIB_DIR}/include/sqlite - $ENV{LIB_DIR}/include/sqlite3 - $ENV{ProgramFiles}/SQLite/*/include - $ENV{ProgramFiles}/SQLite3/*/include - $ENV{SystemDrive}/SQLite/*/include - $ENV{SystemDrive}/SQLite3/*/include - $ENV{SQLITE_ROOT}/include - ${SQLITE_ROOT_DIR}/include - $ENV{OSGEO4W_ROOT}/include) - -set(SQLITE3_NAMES sqlite3_i sqlite3 sqlite3-static) -find_library(SQLITE3_LIBRARY - NAMES ${SQLITE3_NAMES} - PATHS - /usr/lib - /usr/local/lib - $ENV{LIB_DIR}/lib - $ENV{ProgramFiles}/SQLite/*/lib - $ENV{ProgramFiles}/SQLite3/*/lib - $ENV{SystemDrive}/SQLite/*/lib - $ENV{SystemDrive}/SQLite3/*/lib - $ENV{SQLITE_ROOT}/lib - ${SQLITE_ROOT_DIR}/lib - $ENV{OSGEO4W_ROOT}/lib) - -set(SQLITE3_LIBRARIES - ${SQLITE3_LIBRARIES} - ${SQLITE3_LIBRARY}) - -#message(STATUS ${SQLITE3_LIBRARY}) -# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE -# if all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SQLite3 - DEFAULT_MSG - SQLITE3_LIBRARIES - SQLITE3_INCLUDE_DIR) - -if(NOT SQLITE3_FOUND) - message(STATUS "SQLite3 not found (SQLITE3_INCLUDE_DIR=${SQLITE3_INCLUDE_DIR}, SQLITE3_LIBRARY=${SQLITE3_LIBRARY}.") -endif() - -mark_as_advanced(SQLITE3_LIBRARY SQLITE3_INCLUDE_DIR SQLITE3_LIBRARIES) diff --git a/cmake/resources/SOCIConfig.cmake.in b/cmake/resources/SOCIConfig.cmake.in deleted file mode 100644 index 8096a3c25..000000000 --- a/cmake/resources/SOCIConfig.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -@PACKAGE_INIT@ - -include(${CMAKE_CURRENT_LIST_DIR}/SOCITargets.cmake) diff --git a/cmake/resources/vs2010-test-cmd-args.vcxproj.user.in b/cmake/resources/vs2010-test-cmd-args.vcxproj.user.in deleted file mode 100644 index f41ec9b27..000000000 --- a/cmake/resources/vs2010-test-cmd-args.vcxproj.user.in +++ /dev/null @@ -1,7 +0,0 @@ - - - - @SOCI_TEST_CMD_ARGS@ - WindowsLocalDebugger - - diff --git a/cmake/soci_parse_version.cmake b/cmake/soci_parse_version.cmake new file mode 100644 index 000000000..d9d01cf54 --- /dev/null +++ b/cmake/soci_parse_version.cmake @@ -0,0 +1,43 @@ +include(soci_utils) + +# This function extracts the current SOCI version from the version header file +# +# Use as +# soci_parse_version( +# ROOT_DIR +# OUTPUT_VARIABLE +# ) +# where +# - is the path to SOCI's source tree root directory +# - is the name of the variable used to make the parsed version available. This variable +# will contain the version in a cmake-compatible format +function(soci_parse_version) + set(FLAGS "") + set(ONE_VAL_OPTIONS "OUTPUT_VARIABLE" "ROOT_DIR") + set(MULTI_VAL_OPTIONS "") + cmake_parse_arguments(PARSE_VERSION "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) + soci_verify_parsed_arguments( + PREFIX "PARSE_VERSION" + FUNCTION_NAME "soci_parse_version" + REQUIRED "OUTPUT_VARIABLE" "ROOT_DIR" + ) + + set(VERSION_REGEX "^#define[ \\t]*SOCI_VERSION[ \\t]*([0-9]+)") + + file(STRINGS "${PARSE_VERSION_ROOT_DIR}/include/soci/version.h" VERSION_LINE + REGEX ${VERSION_REGEX} + LIMIT_COUNT 1 + ) + + if (NOT VERSION_LINE MATCHES "${VERSION_REGEX}") + message(STATUS "${PARSE_VERSION_ROOT_DIR}") + message(FATAL_ERROR "Failed at parsing version from header file") + endif() + + set(RAW_VERSION "${CMAKE_MATCH_1}") + math(EXPR MAJOR_VERSION "${RAW_VERSION} / 100000") + math(EXPR MINOR_VERSION "(${RAW_VERSION} / 100) % 1000") + math(EXPR PATCH_VERSION "${RAW_VERSION} % 100") + + set("${PARSE_VERSION_OUTPUT_VARIABLE}" "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}" PARENT_SCOPE) +endfunction() diff --git a/cmake/soci_utils.cmake b/cmake/soci_utils.cmake new file mode 100644 index 000000000..0b44f00a9 --- /dev/null +++ b/cmake/soci_utils.cmake @@ -0,0 +1,51 @@ +# This function is meant to be called by other cmake functions that use the cmake_parse_arguments function. +# It implements the boilerplate that is associated with validating the parsed arguments (e.g. no unknown arguments used; +# ensuring all required options have been set). +# +# Use as +# soci_verify_parsed_arguments( +# PREFIX +# FUNCTION_NAME +# [REQUIRED …] +# ) +# where +# - is the prefix used in the cmake_parse_arguments call that shall be validated +# - is the name of the calling function or macro - this will be part of error mesages +# - are names of options **without** the prefix +function(soci_verify_parsed_arguments) + set(FLAGS "") + set(ONE_VAL_OPTIONS "PREFIX" "FUNCTION_NAME") + set(MULTI_VAL_OPTIONS "REQUIRED") + cmake_parse_arguments(VERIFY_PARSED_ARGS "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) + + # First, verify own arguments + if (DEFINED VERIFY_PARSED_ARGS_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "verify_parsed_arguments - Received unrecognized options: ${VERIFY_PARSED_ARGS_UNPARSED_ARGUMENTS}") + endif() + if (DEFINED VERIFY_PARSED_ARGS_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "verify_parsed_arguments - The following options are missing argumens: ${VERIFY_PARSED_ARGS_KEYWORDS_MISSING_VALUES}") + endif() + if (NOT DEFINED VERIFY_PARSED_ARGS_PREFIX) + message(FATAL_ERROR "verify_parsed_arguments - Missing required option 'PREFIX'") + endif() + if (NOT DEFINED VERIFY_PARSED_ARGS_FUNCTION_NAME) + message(FATAL_ERROR "verify_parsed_arguments - Missing required option 'FUNCTION_NAME'") + endif() + + # Now start the function's actual job: Verifying a top-level function's call to cmake_parse_arguments + if (DEFINED ${VERIFY_PARSED_ARGS_PREFIX}_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "${VERIFY_PARSED_ARGS_FUNCTION_NAME} - Received unrecognized options: ${${PREFIX}_UNPARSED_ARGUMENTS}") + endif() + if (DEFINED ${VERIFY_PARSED_ARGS_PREFIX}_KEYWORDS_MISSING_VALUES) + message(FATAL_ERROR "${VERIFY_PARSED_ARGS_FUNCTION_NAME} - The following options are missing arguments: ${${PREFIX}_KEYWORDS_MISSING_VALUES}") + endif() + + if (DEFINED VERIFY_PARSED_ARGS_REQUIRED) + foreach(CURRENT_ARG IN LISTS VERIFY_PARSED_ARGS_REQUIRED) + set(CURRENT_ARG "${VERIFY_PARSED_ARGS_PREFIX}_${CURRENT_ARG}") + if (NOT DEFINED ${CURRENT_ARG}) + message(FATAL_ERROR "${VERIFY_PARSED_ARGS_FUNCTION_NAME - Missing required option '${CURRENT_ARG}'") + endif() + endforeach() + endif() +endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 472ae864c..1c5fd47ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,2 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2013 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) -# -############################################################################### -include_directories(${SOCI_SOURCE_DIR}/include/private) - add_subdirectory(core) add_subdirectory(backends) diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 871e151ef..a5b4e5489 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,50 +1,41 @@ -############################################################################### -# -# 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 backend libraries:") +set(SOCI_EMPTY ${PROJECT_IS_TOP_LEVEL} CACHE STRING "Include the 'empty' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") -# 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() - set(SOCI_HAVE_${depUP} OFF CACHE INTERNAL "${depUP} backend") -endforeach() +set(SOCI_DB2 "AsAvailable" CACHE STRING "Include the 'DB2' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_FIREBIRD "AsAvailable" CACHE STRING "Include the 'Firebird' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_MYSQL "AsAvailable" CACHE STRING "Include the 'MySQL' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_ODBC "AsAvailable" CACHE STRING "Include the 'ODBC' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_ORACLE "AsAvailable" CACHE STRING "Include the 'Oracle' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_POSTGRESQL "AsAvailable" CACHE STRING "Include the 'Postgresql' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +set(SOCI_SQLITE3 "AsAvailable" CACHE STRING "Include the 'SQLite3' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") -# 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() +set(BACKENDS "empty" "db2" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") +foreach(CURRENT IN LISTS BACKENDS) + string(TOUPPER "${CURRENT}" CURRENT_UPPER) -# 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}) - set(SOCI_HAVE_${dirUP} ON CACHE INTERNAL "${dirUP} backend") - endif() - endif() - endif() -endforeach() + # Backwards compatibility with the old cmake setup that used WITH_* variables + # These are now only defined if the user specified them. + if (DEFINED WITH_${CURRENT_UPPER}) + message(DEPRECATION "Use of the WITH_${CURRENT_UPPER} option is deprecated - use the new SOCI_${CURRENT_UPPER} instead") + set(SOCI_${CURRENT_UPPER} "${WITH_${CURRENT_UPPER}}" CACHE STRING "") + # Delete the legacy variable from cache + unset(WITH_${CURRENT_UPPER} CACHE) + endif() -message(STATUS "") + if (SOCI_${CURRENT_UPPER} STREQUAL "Enabled") + set(SOCI_${CURRENT_UPPER} ON) + elseif(SOCI_${CURRENT_UPPER} STREQUAL "Disabled") + set(SOCI_${CURRENT_UPPER} OFF) + elseif(SOCI_${CURRENT_UPPER} STREQUAL "AsAvailable") + set(SOCI_${CURRENT_UPPER} ON) + set(SOCI_${CURRENT_UPPER}_AS_AVAILABLE ON CACHE INTERNAL "") + elseif(SOCI_${CURRENT_UPPER} MATCHES "[Oo][Nn]|[Oo][Ff][Ff]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Yy][Ee][Ss]|[Nn][Oo]|[YyNn01]") + # Do nothing - these are already booleans + else() + message(FATAL_ERROR "Unsupported value '${SOCI_${CURRENT_UPPER}}' for option SOCI_${CURRENT_UPPER}") + endif() + + if (SOCI_${CURRENT_UPPER}) + add_subdirectory(${CURRENT}) + endif() +endforeach() diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index 7913d7920..f2694968f 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -1,14 +1,44 @@ -############################################################################### -# -# 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 - DESCRIPTION "SOCI backend for DB2" AUTHORS "Denis Chapligin" - MAINTAINERS "Denis Chapligin") +if (SOCI_DB2_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(DB2 ${REQUIRED_FLAG}) + +if (NOT DB2_FOUND) + message(STATUS "Disabling DB2 backend as the required dependencies were not found") + return() +endif() + +add_library(soci_db2_objects OBJECT + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_db2_objects PUBLIC DB2::db2) +target_link_libraries(soci_db2_objects PUBLIC soci_core_objects) + +target_include_directories(soci_db2_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_db2) + target_link_libraries(soci_db2 PUBLIC soci_db2_objects) + add_library(SOCI::shared::DB2 ALIAS soci_db2) +endif() +if (SOCI_STATIC) + add_library(soci_db2_static) + target_link_libraries(soci_db2_static PUBLIC soci_db2_objects) + add_library(SOCI::static::DB2 ALIAS soci_db2_static) +endif() diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index b09185b58..5e4f3dc83 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -1,14 +1,30 @@ -############################################################################### -# -# 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 - DESCRIPTION "SOCI backend skeleton for new backends development" - AUTHORS "Maciej Sobczak, Stephen Hutton" - MAINTAINERS "Maciej Sobczak") +add_library(soci_empty_objects OBJECT + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_empty_objects PUBLIC soci_core_objects) + +target_include_directories(soci_empty_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_empty) + target_link_libraries(soci_empty PUBLIC soci_empty_objects) + add_library(SOCI::shared::Empty ALIAS soci_empty) +endif() +if (SOCI_STATIC) + add_library(soci_empty_static) + target_link_libraries(soci_empty_static PUBLIC soci_empty_objects) + add_library(SOCI::static::Empty ALIAS soci_empty_static) +endif() diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index cbf702813..2f004466a 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -1,16 +1,47 @@ -############################################################################### -# -# 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) -# -############################################################################### +option(SOCI_FIREBIRD_EMBEDDED "Use the embedded version of Firebird" OFF) -soci_backend(Firebird - DEPENDS Firebird - DESCRIPTION "SOCI backend for Firebird" - AUTHORS "RafaÅ‚ Bobrowski" - MAINTAINERS "Viacheslav Naydenov") +if (SOCI_FIREBIRD_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(Firebird ${REQUIRED_FLAG}) + +if (NOT Firebird_FOUND) + message(STATUS "Disabling Firebird backend as the required dependencies were not found") + return() +endif() + +add_library(soci_firebird_objects OBJECT + "blob.cpp" + "common.cpp" + "error-firebird.cpp" + "factory.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_firebird_objects PUBLIC Firebird::firebird) +target_link_libraries(soci_firebird_objects PUBLIC soci_core_objects) + +target_include_directories(soci_firebird_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_firebird) + target_link_libraries(soci_firebird PUBLIC soci_firebird_objects) + add_library(SOCI::shared::Firebird ALIAS soci_firebird) +endif() +if (SOCI_STATIC) + add_library(soci_firebird_static) + target_link_libraries(soci_firebird_static PUBLIC soci_firebird_objects) + add_library(SOCI::static::Firebird ALIAS soci_firebird_static) +endif() diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index b2dcc9ceb..7c7c6449f 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -1,15 +1,45 @@ -############################################################################### -# -# 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 - DESCRIPTION "SOCI backend for MySQL" - AUTHORS "Pawel Aleksander Fedorynski" - MAINTAINERS "Pawel Aleksander Fedorynski") +if (SOCI_MYSQL_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(MySQL ${REQUIRED_FLAG}) + +if (NOT MySQL_FOUND) + message(STATUS "Disabling MySQL backend as the required dependencies were not found") + return() +endif() + +add_library(soci_mysql_objects OBJECT + "blob.cpp" + "common.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_mysql_objects PUBLIC MySQL::mysql) +target_link_libraries(soci_mysql_objects PUBLIC soci_core_objects) + +target_include_directories(soci_mysql_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_mysql) + target_link_libraries(soci_mysql PUBLIC soci_mysql_objects) + add_library(SOCI::shared::MySQL ALIAS soci_mysql) +endif() +if (SOCI_STATIC) + add_library(soci_mysql_static) + target_link_libraries(soci_mysql_static PUBLIC soci_mysql_objects) + add_library(SOCI::static::MySQL ALIAS soci_mysql_static) +endif() diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index ce0195393..8e7257b08 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -1,15 +1,44 @@ -############################################################################### -# -# 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 - DESCRIPTION "SOCI backend for ODBC" - AUTHORS "Maciej Sobczak, Stephen Hutton, David Courtney" - MAINTAINERS "Vadim Zeitlin, Mateusz Loskot, Maciej Sobczak") +if (SOCI_ODBC_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(ODBC ${REQUIRED_FLAG}) + +if (NOT ODBC_FOUND) + message(STATUS "Disabling ODBC backend as the required dependencies were not found") + return() +endif() + +add_library(soci_odbc_objects OBJECT + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_odbc_objects PUBLIC ODBC::ODBC) +target_link_libraries(soci_odbc_objects PUBLIC soci_core_objects) + +target_include_directories(soci_odbc_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_odbc) + target_link_libraries(soci_odbc PUBLIC soci_odbc_objects) + add_library(SOCI::shared::ODBC ALIAS soci_odbc) +endif() +if (SOCI_STATIC) + add_library(soci_odbc_static) + target_link_libraries(soci_odbc_static PUBLIC soci_odbc_objects) + add_library(SOCI::static::ODBC ALIAS soci_odbc_static) +endif() diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 632ca6179..faaa82faf 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -1,15 +1,46 @@ -############################################################################### -# -# 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(Oracle - DEPENDS Oracle - DESCRIPTION "SOCI backend for Oracle 10+" - AUTHORS "Maciej Sobczak, Stephen Hutton" - MAINTAINERS "Maciej Sobczak") +if (SOCI_ORACLE_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(Oracle ${REQUIRED_FLAG}) + +if (NOT Oracle_FOUND) + message(STATUS "Disabling Oracle backend as the required dependencies were not found") + return() +endif() + +add_library(soci_oracle_objects OBJECT + "blob.cpp" + "clob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_oracle_objects PUBLIC Oracle::oracle) +target_link_libraries(soci_oracle_objects PUBLIC soci_core_objects) + +target_include_directories(soci_oracle_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_oracle) + target_link_libraries(soci_oracle PUBLIC soci_oracle_objects) + add_library(SOCI::shared::Oracle ALIAS soci_oracle) +endif() +if (SOCI_STATIC) + add_library(soci_oracle_static) + target_link_libraries(soci_oracle_static PUBLIC soci_oracle_objects) + add_library(SOCI::static::Oracle ALIAS soci_oracle_static) +endif() diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 3debd3aa0..afe20b0b5 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -1,32 +1,59 @@ -############################################################################### -# -# 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) -# -############################################################################### +if (SOCI_POSTGRESQL_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() + +find_package(PostgreSQL ${REQUIRED_FLAG}) -include(CMakeDependentOption) +if (NOT PostgreSQL_FOUND) + message(STATUS "Disabling PostgreSQL backend as the required dependencies were not found") + return() +endif() option(SOCI_POSTGRESQL_NO_LO64 "Do not use lo_xxx64() functions for compatibility with PostgreSQL < 9.3" - OFF) - + OFF +) if (POSTGRESQL_VERSION VERSION_LESS "9.3.0") set(SOCI_POSTGRESQL_NO_LO64 ON CACHE BOOL "Avoid using lo_xxx64() functions" FORCE) endif() -if(SOCI_POSTGRESQL_NO_LO64) - add_definitions(-DSOCI_POSTGRESQL_NO_LO64=1) -endif() +add_library(soci_postgresql_objects OBJECT + "blob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_postgresql_objects PUBLIC PostgreSQL::PostgreSQL) +target_link_libraries(soci_postgresql_objects PUBLIC soci_core_objects) -soci_backend(PostgreSQL - DEPENDS PostgreSQL - DESCRIPTION "SOCI backend for PostgreSQL" - AUTHORS "Maciej Sobczak, Stephen Hutton" - MAINTAINERS "Mateusz Loskot") +target_include_directories(soci_postgresql_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) -boost_report_value(SOCI_POSTGRESQL_NO_LO64) +if (SOCI_SHARED) + add_library(soci_postgresql) + target_link_libraries(soci_postgresql PUBLIC soci_postgresql_objects) + add_library(SOCI::shared::PostgreSQL ALIAS soci_postgresql) + if (SOCI_POSTGRESQL_NO_LO64) + target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) + endif() +endif() +if (SOCI_STATIC) + add_library(soci_postgresql_static) + target_link_libraries(soci_postgresql_static PUBLIC soci_postgresql_objects) + add_library(SOCI::static::PostgreSQL ALIAS soci_postgresql_static) + if (SOCI_POSTGRESQL_NO_LO64) + target_compile_definitions(soci_postgresql_static INTERFACE SOCI_POSTGRESQL_NO_LO64) + endif() +endif() diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index cd90b7ae2..264455c84 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -1,16 +1,45 @@ -############################################################################### -# -# 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) -# -############################################################################### +if (SOCI_SQLITE3_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") +endif() -soci_backend(SQLite3 - DEPENDS SQLite3 - DESCRIPTION "SOCI backend for SQLite 3" - AUTHORS "Maciej Sobczak, Stephen Hutton, David Courtney" - MAINTAINERS "Maciej Sobczak, Mateusz Loskot") +find_package(SQLite3 ${REQUIRED_FLAG}) + +if (NOT SQLite3_FOUND) + message(STATUS "Disabling SQLite3 backend as the required dependencies were not found") + return() +endif() + +add_library(soci_sqlite3_objects OBJECT + "blob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) + +target_link_libraries(soci_sqlite3_objects PUBLIC SQLite::SQLite3) +target_link_libraries(soci_sqlite3_objects PUBLIC soci_core_objects) + +target_include_directories(soci_sqlite3_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/private" + "${PROJECT_SOURCE_DIR}/include/soci" +) + +if (SOCI_SHARED) + add_library(soci_sqlite3) + target_link_libraries(soci_sqlite3 PUBLIC soci_sqlite3_objects) + add_library(SOCI::shared::SQLite3 ALIAS soci_sqlite3) +endif() +if (SOCI_STATIC) + add_library(soci_sqlite3_static) + target_link_libraries(soci_sqlite3_static PUBLIC soci_sqlite3_objects) + add_library(SOCI::static::SQLite3 ALIAS soci_sqlite3_static) +endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0158da227..c30973bf4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,151 +1,64 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2009-2013 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 core library:") - -include(CMakePackageConfigHelpers) - -# Set INCLUDE_DIRECTORIES -get_directory_property(SOCI_CORE_INCLUDE_DIRS INCLUDE_DIRECTORIES) -list(APPEND SOCI_CORE_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) -set_directory_properties(PROPERTIES - INCLUDE_DIRECTORIES "${SOCI_CORE_INCLUDE_DIRS}") - -# Configure backend loader to also use default install directory. -configure_file(soci_backends_config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/soci_backends_config.h) - -# Core source files -file(GLOB SOCI_CORE_HEADERS ${SOCI_SOURCE_DIR}/include/soci/*.h) -file(GLOB SOCI_CORE_SOURCES *.cpp) - -# Group source files for IDE source explorers (e.g. Visual Studio) -source_group("Header Files" FILES ${SOCI_CORE_HEADERS}) -source_group("Source Files" FILES ${SOCI_CORE_SOURCES}) -source_group("CMake Files" FILES CMakeLists.txt) - -# Core targets configuration -string(TOLOWER "${PROJECT_NAME}" PROJECTNAMEL) -#this command will update parent scope variable -set(SOCI_CORE_TARGET ${PROJECTNAMEL}_core PARENT_SCOPE) -set(SOCI_CORE_TARGET ${PROJECTNAMEL}_core) - -soci_target_output_name(${SOCI_CORE_TARGET} SOCI_CORE_TARGET_OUTPUT_NAME) - -# -# Core shared library -# -if (SOCI_SHARED) - add_library(${SOCI_CORE_TARGET} SHARED ${SOCI_CORE_HEADERS} ${SOCI_CORE_SOURCES}) - add_library(Soci::core ALIAS ${SOCI_CORE_TARGET}) - - target_link_libraries(${SOCI_CORE_TARGET} ${SOCI_CORE_DEPS_LIBS}) - - if(WIN32) - set_target_properties(${SOCI_CORE_TARGET} - PROPERTIES - DEFINE_SYMBOL SOCI_DLL - OUTPUT_NAME "${SOCI_CORE_TARGET_OUTPUT_NAME}" - VERSION ${SOCI_VERSION} - CLEAN_DIRECT_OUTPUT 1) - else() - set_target_properties(${SOCI_CORE_TARGET} - PROPERTIES - VERSION ${SOCI_VERSION} - SOVERSION ${SOCI_SOVERSION} - INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib - CLEAN_DIRECT_OUTPUT 1) +find_package(Threads REQUIRED) + +add_library(soci_core_objects OBJECT + "backend-loader.cpp" + "blob.cpp" + "common.cpp" + "connection-parameters.cpp" + "connection-pool.cpp" + "error.cpp" + "into-type.cpp" + "logger.cpp" + "once-temp-type.cpp" + "prepare-temp-type.cpp" + "procedure.cpp" + "ref-counted-statement.cpp" + "row.cpp" + "rowid.cpp" + "session.cpp" + "soci-simple.cpp" + "statement.cpp" + "transaction.cpp" + "use-type.cpp" + "values.cpp" +) + +target_link_libraries(soci_core_objects PRIVATE Threads::Threads) + +if (SOCI_BOOST) + find_package(Boost REQUIRED) + target_link_libraries(soci_core_objects PUBLIC Boost::boost) + target_compile_definitions(soci_core_objects PUBLIC SOCI_HAVE_BOOST) + + if (TARGET Boost::date_time) + target_link_libraries(soci_core_objects PUBLIC Boost::date_time) + target_compile_definitions(soci_core_objects PUBLIC SOCI_HAVE_BOOST_DATE_TIME) endif() - - target_include_directories(${SOCI_CORE_TARGET} - PUBLIC - $ - $ - $ - ) - endif() -# This adds definitions to all build configurations. SOCI_DEBUG_POSTFIX is passed to soci library -add_definitions(-DSOCI_LIB_PREFIX="${CMAKE_SHARED_LIBRARY_PREFIX}soci_" - -DSOCI_LIB_SUFFIX="${CMAKE_SHARED_LIBRARY_SUFFIX}" - -DSOCI_DEBUG_POSTFIX="${CMAKE_DEBUG_POSTFIX}") +target_include_directories(soci_core_objects PUBLIC "${PROJECT_SOURCE_DIR}/include") +target_include_directories(soci_core_objects + PRIVATE + "${PROJECT_SOURCE_DIR}/include/soci" + "${PROJECT_SOURCE_DIR}/include/private" +) -# -# Core static library -# -if (SOCI_STATIC) - set(SOCI_CORE_TARGET_STATIC ${SOCI_CORE_TARGET}_static) +target_compile_definitions(soci_core_objects + PRIVATE + DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" + # TODO: Configure prefix and suffix properly + SOCI_LIB_PREFIX="" + SOCI_LIB_SUFFIX="" +) - add_library(${SOCI_CORE_TARGET_STATIC} STATIC - ${SOCI_CORE_HEADERS} ${SOCI_CORE_SOURCES}) - add_library(Soci::core_static ALIAS ${SOCI_CORE_TARGET_STATIC}) - - # we still need to link against dl if we have it - target_link_libraries (${SOCI_CORE_TARGET_STATIC} - ${SOCI_CORE_DEPS_LIBS} - ) - - set_target_properties(${SOCI_CORE_TARGET_STATIC} - PROPERTIES - OUTPUT_NAME ${SOCI_CORE_TARGET_OUTPUT_NAME} - PREFIX "lib" - CLEAN_DIRECT_OUTPUT 1) - - target_include_directories(${SOCI_CORE_TARGET_STATIC} - PUBLIC - $ - $ - $ - ) - -endif() - - - - -# -# Core installation -# -install(FILES ${SOCI_CORE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECTNAMEL}) if (SOCI_SHARED) - install(TARGETS ${SOCI_CORE_TARGET} - EXPORT SOCI - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + add_library(soci_core SHARED) + target_link_libraries(soci_core PUBLIC soci_core_objects) + add_library(SOCI::shared::core ALIAS soci_core) endif() - if (SOCI_STATIC) - install(TARGETS ${SOCI_CORE_TARGET_STATIC} - EXPORT SOCI - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + add_library(soci_core_static STATIC) + target_link_libraries(soci_core_static PUBLIC soci_core_objects) + add_library(SOCI::static::core ALIAS soci_core_static) endif() - -install(EXPORT SOCI NAMESPACE SOCI:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SOCI FILE SOCITargets.cmake) -configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/resources/SOCIConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/SOCIConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SOCI) -write_basic_package_version_file(SOCIConfigVersion.cmake VERSION ${SOCI_VERSION} COMPATIBILITY SameMajorVersion) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SOCIConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/SOCIConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/SOCI) - -# -# Core configuration summary -# -boost_report_value(SOCI_CORE_TARGET) -boost_report_value(SOCI_CORE_TARGET_OUTPUT_NAME) -boost_report_value(SOCI_CORE_DEPS_LIBS) -boost_report_value(SOCI_CORE_INCLUDE_DIRS) -boost_report_value(WITH_BOOST) -soci_report_directory_property(COMPILE_DEFINITIONS) - -message(STATUS "") diff --git a/src/core/backend-loader.cpp b/src/core/backend-loader.cpp index 00c5bec06..9bc308ea4 100644 --- a/src/core/backend-loader.cpp +++ b/src/core/backend-loader.cpp @@ -17,8 +17,6 @@ #include #endif -#include "soci_backends_config.h" - using namespace soci; using namespace soci::dynamic_backends; diff --git a/src/core/soci_backends_config.h.in b/src/core/soci_backends_config.h.in deleted file mode 100644 index b6c1d9f29..000000000 --- a/src/core/soci_backends_config.h.in +++ /dev/null @@ -1,12 +0,0 @@ -// -// Copyright (C) 2011 Alex Ott -// 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_BACKENDS_CONFIG_H -#define SOCI_BACKENDS_CONFIG_H - -#define DEFAULT_BACKENDS_PATH "@CMAKE_INSTALL_FULL_LIBDIR@" - -#endif // SOCI_BACKENDS_CONFIG_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c8397b4ee..e69de29bb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,34 +0,0 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2013 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 tests:") - -# This works around a problem when building in C++11 mode with clang (see #984). -add_definitions(-DCATCH_CONFIG_CPP11_NO_SHUFFLE) - -if(MSVC) - add_compile_options(/bigobj) -endif() - -include_directories( - ${SOCI_SOURCE_DIR}/include/private - ${CMAKE_CURRENT_SOURCE_DIR}) - -file(GLOB SOCI_TESTS_COMMON common-tests.h) - -add_subdirectory(empty) -add_subdirectory(db2) -add_subdirectory(firebird) -add_subdirectory(mysql) -add_subdirectory(odbc) -add_subdirectory(oracle) -add_subdirectory(postgresql) -add_subdirectory(sqlite3) From 9e7c5dbc24f5d6e962484e991b0e681a97eed419 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 14:57:37 +0100 Subject: [PATCH 02/79] Fix typo --- include/soci/version.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/soci/version.h b/include/soci/version.h index b13caa3dd..1a3ac7315 100644 --- a/include/soci/version.h +++ b/include/soci/version.h @@ -10,10 +10,8 @@ #ifndef SOCI_VERSION_HPP #define SOCI_VERSION_HPP -// When updating the version here, don't forget to update it in CMakeLists.txt! - // -// Caution, this is the only SOCI header that is guarenteed +// Caution, this is the only SOCI header that is guaranteed // to change with every SOCI release, including this header // will cause a recompile every time a new SOCI version is // released. From 579a099536c3449f7f54cb975dfac3aa888d645a Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:03:11 +0100 Subject: [PATCH 03/79] Delete FindSoci.cmake - will be replaced by SociConfig --- cmake/find_modules/FindSoci.cmake | 140 ------------------------------ 1 file changed, 140 deletions(-) delete mode 100644 cmake/find_modules/FindSoci.cmake diff --git a/cmake/find_modules/FindSoci.cmake b/cmake/find_modules/FindSoci.cmake deleted file mode 100644 index ece836e06..000000000 --- a/cmake/find_modules/FindSoci.cmake +++ /dev/null @@ -1,140 +0,0 @@ -############################################################################### -# CMake module to search for SOCI library -# -# This module defines: -# Soci_INCLUDE_DIRS = include dirs to be used when using the soci library -# Soci_LIBRARY = full path to the soci library -# Soci_VERSION = the soci version found -# Soci_FOUND = true if soci was found -# -# This module respects: -# LIB_SUFFIX = (64|32|"") Specifies the suffix for the lib directory -# -# For each component you specify in find_package(), the following variables are set. -# -# Soci_${COMPONENT}_PLUGIN = full path to the soci plugin (not set for the "core" component) -# Soci_${COMPONENT}_FOUND -# -# This module provides the following imported targets, if found: -# -# Soci::core = target for the core library and include directories -# Soci::${COMPONENT} = target for each plugin -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -############################################################################### -# -### Global Configuration Section -# -set(_SOCI_ALL_PLUGINS mysql odbc postgresql sqlite3) -set(_SOCI_REQUIRED_VARS Soci_INCLUDE_DIR Soci_LIBRARY) - -# -### FIRST STEP: Find the soci headers. -# -find_path( - Soci_INCLUDE_DIR soci.h - HINTS "/usr/local" - PATH_SUFFIXES "" "soci" - DOC "Soci (http://soci.sourceforge.net) include directory") -mark_as_advanced(Soci_INCLUDE_DIR) - -set(Soci_INCLUDE_DIRS ${Soci_INCLUDE_DIR} CACHE STRING "") - -# -### SECOND STEP: Find the soci core library. Respect LIB_SUFFIX -# -find_library( - Soci_LIBRARY - NAMES soci_core - HINTS ${Soci_INCLUDE_DIR}/.. - PATH_SUFFIXES lib${LIB_SUFFIX}) -mark_as_advanced(Soci_LIBRARY) - -get_filename_component(Soci_LIBRARY_DIR ${Soci_LIBRARY} PATH) -mark_as_advanced(Soci_LIBRARY_DIR) - -# -### THIRD STEP: Find all installed plugins if the library was found -# -if(Soci_INCLUDE_DIR AND Soci_LIBRARY) - set(Soci_core_FOUND TRUE CACHE BOOL "") - - add_library(Soci::core UNKNOWN IMPORTED) - set_target_properties( - Soci::core - PROPERTIES IMPORTED_LOCATION "${Soci_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${Soci_INCLUDE_DIR}" - ) - - # - ### FOURTH STEP: Obtain SOCI version - # - set(Soci_VERSION_FILE "${Soci_INCLUDE_DIR}/version.h") - if(EXISTS "${Soci_VERSION_FILE}") - file(READ "${Soci_VERSION_FILE}" VERSION_CONTENT) - string(REGEX MATCH "#define[ \t]*SOCI_VERSION[ \t]*[0-9]+" VERSION_MATCH "${VERSION_CONTENT}") - string(REGEX REPLACE "#define[ \t]*SOCI_VERSION[ \t]*" "" VERSION_MATCH "${VERSION_MATCH}") - - if(NOT VERSION_MATCH) - message(WARNING "Failed to extract SOCI version") - else() - math(EXPR MAJOR "${VERSION_MATCH} / 100000" OUTPUT_FORMAT DECIMAL) - math(EXPR MINOR "${VERSION_MATCH} / 100 % 1000" OUTPUT_FORMAT DECIMAL) - math(EXPR PATCH "${VERSION_MATCH} % 100" OUTPUT_FORMAT DECIMAL) - - set(Soci_VERSION "${MAJOR}.${MINOR}.${PATCH}" CACHE STRING "") - endif() - else() - message(WARNING "Unable to check SOCI version") - endif() - - foreach(plugin IN LISTS _SOCI_ALL_PLUGINS) - - find_library( - Soci_${plugin}_PLUGIN - NAMES soci_${plugin} - HINTS ${Soci_INCLUDE_DIR}/.. - PATH_SUFFIXES lib${LIB_SUFFIX}) - mark_as_advanced(Soci_${plugin}_PLUGIN) - - if(Soci_${plugin}_PLUGIN) - set(Soci_${plugin}_FOUND TRUE CACHE BOOL "") - add_library(Soci::${plugin} UNKNOWN IMPORTED) - set_target_properties( - Soci::${plugin} - PROPERTIES IMPORTED_LOCATION "${Soci_${plugin}_PLUGIN}" - ) - target_link_libraries(Soci::${plugin} INTERFACE Soci::core) - else() - set(Soci_${plugin}_FOUND FALSE CACHE BOOL "") - endif() - - endforeach() -endif() - -# -### ADHERE TO STANDARDS -# -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Soci - REQUIRED_VARS ${_SOCI_REQUIRED_VARS} - VERSION_VAR Soci_VERSION - HANDLE_COMPONENTS -) - -# For compatibility with previous versions of this script -# DO NOT USE THESE VARIABLES IN NEW PROJECTS! -set(SOCI_FOUND ${Soci_FOUND}) -set(SOCI_INCLUDE_DIRS ${Soci_INCLUDE_DIRS}) -set(SOCI_LIBRARY ${Soci_LIBRARY}) -set(SOCI_VERSION ${Soci_VERSION}) -set(SOCI_mysql_FOUND ${Soci_mysql_FOUND}) -set(SOCI_odbc_FOUND ${Soci_odbc_FOUND}) -set(SOCI_postgresql_FOUND ${Soci_postgresql_PLUGIN}) -set(SOCI_sqlite3_FOUND ${Soci_sqlite3_PLUGIN}) -set(SOCI_mysql_PLUGIN ${Soci_mysql_PLUGIN}) -set(SOCI_odbc_PLUGIN ${Soci_odbc_PLUGIN}) -set(SOCI_postgresql_PLUGIN ${Soci_postgresql_PLUGIN}) -set(SOCI_sqlite3_PLUGIN ${Soci_sqlite3_PLUGIN}) From c2dcce7564951544e416dd6827bb940214a6621c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:03:33 +0100 Subject: [PATCH 04/79] Updated find modules to define interface targets --- cmake/find_modules/FindDB2.cmake | 30 +++----- cmake/find_modules/FindFirebird.cmake | 31 ++++---- cmake/find_modules/FindMySQL.cmake | 68 ++++++++--------- cmake/find_modules/FindOracle.cmake | 102 ++++++++++++++------------ src/backends/firebird/CMakeLists.txt | 6 +- src/backends/mysql/CMakeLists.txt | 2 +- src/backends/oracle/CMakeLists.txt | 2 +- 7 files changed, 123 insertions(+), 118 deletions(-) diff --git a/cmake/find_modules/FindDB2.cmake b/cmake/find_modules/FindDB2.cmake index a752f1110..70222565a 100644 --- a/cmake/find_modules/FindDB2.cmake +++ b/cmake/find_modules/FindDB2.cmake @@ -3,9 +3,8 @@ # # On success, the macro sets the following variables: # DB2_FOUND = if the library found -# DB2_LIBRARY = full path to the library # DB2_LIBRARIES = full path to the library -# DB2_INCLUDE_DIR = where to find the library headers +# DB2_INCLUDE_DIRS = where to find the library headers # # Copyright (c) 2013 Denis Chapligin # @@ -70,14 +69,15 @@ elseif(WIN32) endif() endif() -find_path(DB2_INCLUDE_DIR sqlcli1.h +find_path(DB2_INCLUDE_DIRS sqlcli1.h $ENV{DB2_INCLUDE_DIR} + $ENV{DB2_INCLUDE_DIRS} $ENV{DB2_DIR}/include $ENV{DB2_HOME} $ENV{IBM_DB_INCLUDE} ${DB2_FIND_INCLUDE_PATHS}) -find_library(DB2_LIBRARY +find_library(DB2_LIBRARIES NAMES db2 db2api PATHS $ENV{DB2LIB} @@ -85,22 +85,16 @@ find_library(DB2_LIBRARY ${DB2_FIND_LIB_PATHS} ${DB2_FIND_LIB_NO_LIB}) -if(DB2_LIBRARY) - get_filename_component(DB2_LIBRARY_DIR ${DB2_LIBRARY} PATH) +if(DB2_LIBRARIES) + get_filename_component(DB2_LIBRARY_DIR ${DB2_LIBRARIES} PATH) endif() -if(DB2_INCLUDE_DIR AND DB2_LIBRARY_DIR) - set(DB2_FOUND TRUE) -endif() - -set(DB2_LIBRARIES ${DB2_LIBRARY}) - -# Handle the QUIETLY and REQUIRED arguments and set DB2_FOUND to TRUE -# if all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(DB2 - DEFAULT_MSG - DB2_INCLUDE_DIR - DB2_LIBRARIES) + REQUIRED_VARS DB2_INCLUDE_DIRS DB2_LIBRARIES +) -mark_as_advanced(DB2_INCLUDE_DIR DB2_LIBRARIES) +add_library(DB2 INTERFACE) +target_link_libraries(DB2 INTERFACE ${DB2_LIBRARIES}) +target_include_directories(DB2 INTERFACE ${DB2_INCLUDE_DIRS}) +add_library(DB2::DB2 ALIAS DB2) diff --git a/cmake/find_modules/FindFirebird.cmake b/cmake/find_modules/FindFirebird.cmake index 0e9b6784b..594b69908 100644 --- a/cmake/find_modules/FindFirebird.cmake +++ b/cmake/find_modules/FindFirebird.cmake @@ -5,34 +5,35 @@ ############################################################## # This module defines -# FIREBIRD_INCLUDE_DIR - where to find ibase.h -# FIREBIRD_LIBRARIES - the libraries to link against to use FIREBIRD -# FIREBIRD_FOUND - true if FIREBIRD was found +# Firebird_INCLUDE_DIRS - where to find ibase.h +# Firebird_LIBRARIES - the libraries to link against to use Firebird +# Firebird_FOUND - true if Firebird was found -find_path(FIREBIRD_INCLUDE_DIR ibase.h +find_path(Firebird_INCLUDE_DIRS ibase.h /usr/include $ENV{ProgramFiles}/Firebird/*/include ) -if(SOCI_FIREBIRD_EMBEDDED) - set(FIREBIRD_LIB_NAMES fbembed) +if (Firebird_SEARCH_EMBEDDED) + set(Firebird_LIB_NAMES fbembed) else() - set(FIREBIRD_LIB_NAMES fbclient fbclient_ms) + set(Firebird_LIB_NAMES fbclient fbclient_ms) endif() -find_library(FIREBIRD_LIBRARIES +find_library(Firebird_LIBRARIES NAMES - ${FIREBIRD_LIB_NAMES} + ${Firebird_LIB_NAMES} PATHS /usr/lib $ENV{ProgramFiles}/Firebird/*/lib ) -# fbembed ? - include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Firebird - DEFAULT_MSG FIREBIRD_LIBRARIES FIREBIRD_INCLUDE_DIR) - -mark_as_advanced(FIREBIRD_INCLUDE_DIR FIREBIRD_LIBRARIES) +find_package_handle_standard_args(Firebird + REQUIRED_VARS Firebird_LIBRARIES Firebird_INCLUDE_DIRS +) +add_library(Firebird INTERFACE) +target_link_libraries(Firebird INTERFACE ${Firebird_LIBRARIES}) +target_include_directories(Firebird INTERFACE ${Firebird_INCLUDE_DIRS}) +add_library(Firebird::Firebird ALIAS Firebird) diff --git a/cmake/find_modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake index 73efa8809..a3beb3ecc 100644 --- a/cmake/find_modules/FindMySQL.cmake +++ b/cmake/find_modules/FindMySQL.cmake @@ -1,10 +1,10 @@ # - Try to find MariaDB / MySQL library # Find the MySQL includes and client library # This module defines -# MYSQL_INCLUDE_DIR, where to find mysql.h -# MYSQL_LIBRARIES, the libraries needed to use MySQL. -# MYSQL_LIB_DIR, path to the MYSQL_LIBRARIES -# MYSQL_FOUND, If false, do not try to use MySQL. +# MySQL_INCLUDE_DIRS +# MySQL_LIBRARIES, the libraries needed to use MySQL. +# MySQL_LIB_DIR, path to the MySQL_LIBRARIES +# MySQL_FOUND, If false, do not try to use MySQL. # Copyright (c) 2006-2008, JarosÅ‚aw Staniek # Copyright (c) 2023 Vadim Zeitline (MariaDB support) @@ -15,24 +15,28 @@ include(CheckCXXSourceCompiles) if(WIN32) - find_path(MYSQL_INCLUDE_DIR mysql.h + find_path(MySQL_INCLUDE_DIRS mysql.h PATHS $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_INCLUDE_DIRS} $ENV{MYSQL_DIR}/include + $ENV{MYSQL_DIRS}/include $ENV{ProgramFiles}/MySQL/*/include $ENV{SystemDrive}/MySQL/*/include $ENV{ProgramW6432}/MySQL/*/include ) -else(WIN32) - find_path(MYSQL_INCLUDE_DIR mysql.h +else() + find_path(MySQL_INCLUDE_DIRS mysql.h PATHS $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_INCLUDE_DIRS} $ENV{MYSQL_DIR}/include + $ENV{MYSQL_DIRS}/include PATH_SUFFIXES mariadb mysql ) -endif(WIN32) +endif() if(WIN32) if (${CMAKE_BUILD_TYPE}) @@ -45,14 +49,13 @@ if(WIN32) if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") set(binary_dist debug) set(build_dist Debug) - else(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + else() ADD_DEFINITIONS(-DDBUG_OFF) set(binary_dist opt) set(build_dist Release) - endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + endif() -# find_library(MYSQL_LIBRARIES NAMES mysqlclient - set(MYSQL_LIB_PATHS + set(MySQL_LIB_PATHS $ENV{MYSQL_DIR}/lib/${binary_dist} $ENV{MYSQL_DIR}/libmysql/${build_dist} $ENV{MYSQL_DIR}/client/${build_dist} @@ -64,36 +67,33 @@ if(WIN32) $ENV{SystemDrive}/MySQL/*/lib/opt $ENV{ProgramW6432}/MySQL/*/lib ) - find_library(MYSQL_LIBRARIES NAMES libmysql + find_library(MySQL_LIBRARIES NAMES libmysql PATHS - ${MYSQL_LIB_PATHS} + ${MySQL_LIB_PATHS} ) -else(WIN32) -# find_library(MYSQL_LIBRARIES NAMES mysqlclient - set(MYSQL_LIB_PATHS - $ENV{MYSQL_DIR}/lib +else() + set(MySQL_LIB_PATHS + $ENV{MySQL_DIR}/lib PATH_SUFFIXES mariadb mysql ) - find_library(MYSQL_LIBRARIES NAMES mariadbclient mysqlclient + find_library(MySQL_LIBRARIES NAMES mariadbclient mysqlclient PATHS - ${MYSQL_LIB_PATHS} + ${MySQL_LIB_PATHS} ) -endif(WIN32) - -if(MYSQL_LIBRARIES) - get_filename_component(MYSQL_LIB_DIR ${MYSQL_LIBRARIES} PATH) -endif(MYSQL_LIBRARIES) +endif() -set( CMAKE_REQUIRED_INCLUDES ${MYSQL_INCLUDE_DIR} ) +if(MySQL_LIBRARIES) + get_filename_component(MySQL_LIB_DIR ${MySQL_LIBRARIES} PATH) +endif() -if(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) - set(MYSQL_FOUND TRUE) - message(STATUS "Found MySQL: ${MYSQL_INCLUDE_DIR}, ${MYSQL_LIBRARIES}") -else(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) - set(MYSQL_FOUND FALSE) - message(STATUS "MySQL not found.") -endif(MYSQL_INCLUDE_DIR AND MYSQL_LIBRARIES) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MySQL + REQUIRED_VARS MySQL_LIBRARIES MySQL_INCLUDE_DIRS +) -mark_as_advanced(MYSQL_INCLUDE_DIR MYSQL_LIBRARIES) +add_library(MySQL INTERFACE) +target_link_libraries(MySQL INTERFACE ${MySQL_LIBRARIES}) +target_include_directories(MySQL INTERFACE ${MySQL_INCLUDE_DIRS}) +add_library(MySQL::MySQL ALIAS MySQL) diff --git a/cmake/find_modules/FindOracle.cmake b/cmake/find_modules/FindOracle.cmake index 37106f761..c68ef7767 100644 --- a/cmake/find_modules/FindOracle.cmake +++ b/cmake/find_modules/FindOracle.cmake @@ -3,12 +3,12 @@ # CMake module to search for Oracle client library (OCI) # # On success, the macro sets the following variables: -# ORACLE_FOUND = if the library found -# ORACLE_LIBRARY = full path to the library -# ORACLE_LIBRARIES = full path to the library -# ORACLE_INCLUDE_DIR = where to find the library headers also defined, +# Oracle_FOUND = if the library found +# Oracle_LIBRARY = full path to the library +# Oracle_LIBRARIES = full path to the library +# Oracle_INCLUDE_DIR = where to find the library headers also defined, # but not for general use are -# ORACLE_VERSION = version of library which was found, e.g. "1.2.5" +# Oracle_VERSION = version of library which was found, e.g. "1.2.5" # # Copyright (c) 2009-2013 Mateusz Loskot # @@ -23,68 +23,74 @@ ############################################################################### # First check for CMAKE variable -if(NOT ORACLE_HOME) - # If ORACLE_HOME is not defined check for env var and if exists set from env var +if(NOT ORACLE_HOME AND NOT Oracle_HOME) if(EXISTS $ENV{ORACLE_HOME}) - set(ORACLE_HOME $ENV{ORACLE_HOME}) + set(Oracle_HOME $ENV{ORACLE_HOME}) + endif() + if(EXISTS $ENV{Oracle_HOME}) + set(Oracle_HOME $ENV{Oracle_HOME}) endif() endif() +if (ORACLE_HOME) + set(Oracle_HOME "${ORACLE_HOME}") +endif() -message(STATUS "ORACLE_HOME=${ORACLE_HOME}") - -find_path(ORACLE_INCLUDE_DIR +find_path(Oracle_INCLUDE_DIRS NAMES oci.h PATHS - ${ORACLE_HOME}/rdbms/public - ${ORACLE_HOME}/include - ${ORACLE_HOME}/sdk/include # Oracle SDK - ${ORACLE_HOME}/OCI/include # Oracle XE on Windows + ${Oracle_HOME}/rdbms/public + ${Oracle_HOME}/include + ${Oracle_HOME}/sdk/include # Oracle SDK + ${Oracle_HOME}/OCI/include # Oracle XE on Windows # instant client from rpm /usr/include/oracle/*/client${LIB_SUFFIX}) -set(ORACLE_VERSIONS 21 20 19 18 12 11 10) -set(ORACLE_OCI_NAMES clntsh libclntsh oci) # Dirty trick might help on OSX, see issues/89 -set(ORACLE_OCCI_NAMES libocci occi) -set(ORACLE_NNZ_NAMES ociw32) -foreach(loop_var IN LISTS ORACLE_VERSIONS) - set(ORACLE_OCCI_NAMES ${ORACLE_OCCI_NAMES} oraocci${loop_var}) - set(ORACLE_NNZ_NAMES ${ORACLE_NNZ_NAMES} nnz${loop_var} libnnz${loop_var}) +set(Oracle_VERSIONS 21 20 19 18 12 11 10) +set(Oracle_OCI_NAMES clntsh libclntsh oci) # Dirty trick might help on OSX, see issues/89 +set(Oracle_OCCI_NAMES libocci occi) +set(Oracle_NNZ_NAMES ociw32) + +foreach(loop_var IN LISTS Oracle_VERSIONS) + set(Oracle_OCCI_NAMES ${Oracle_OCCI_NAMES} oraocci${loop_var}) + set(Oracle_NNZ_NAMES ${Oracle_NNZ_NAMES} nnz${loop_var} libnnz${loop_var}) endforeach(loop_var) -set(ORACLE_LIB_DIR - ${ORACLE_HOME} - ${ORACLE_HOME}/lib - ${ORACLE_HOME}/sdk/lib # Oracle SDK - ${ORACLE_HOME}/sdk/lib/msvc - ${ORACLE_HOME}/OCI/lib/msvc # Oracle XE on Windows +set(Oracle_LIB_DIR + ${Oracle_HOME} + ${Oracle_HOME}/lib + ${Oracle_HOME}/sdk/lib # Oracle SDK + ${Oracle_HOME}/sdk/lib/msvc + ${Oracle_HOME}/OCI/lib/msvc # Oracle XE on Windows # Instant client from rpm /usr/lib/oracle/*/client${LIB_SUFFIX}/lib) -find_library(ORACLE_OCI_LIBRARY - NAMES ${ORACLE_OCI_NAMES} PATHS ${ORACLE_LIB_DIR}) -find_library(ORACLE_OCCI_LIBRARY - NAMES ${ORACLE_OCCI_NAMES} PATHS ${ORACLE_LIB_DIR}) -find_library(ORACLE_NNZ_LIBRARY - NAMES ${ORACLE_NNZ_NAMES} PATHS ${ORACLE_LIB_DIR}) +find_library(Oracle_OCI_LIBRARY + NAMES ${Oracle_OCI_NAMES} PATHS ${Oracle_LIB_DIR}) +find_library(Oracle_OCCI_LIBRARY + NAMES ${Oracle_OCCI_NAMES} PATHS ${Oracle_LIB_DIR}) +find_library(Oracle_NNZ_LIBRARY + NAMES ${Oracle_NNZ_NAMES} PATHS ${Oracle_LIB_DIR}) -set(ORACLE_LIBRARY - ${ORACLE_OCI_LIBRARY} - ${ORACLE_OCCI_LIBRARY} - ${ORACLE_NNZ_LIBRARY}) +if (Oracle_OCI_LIBRARY AND Oracle_OCCI_LIBRARY AND Oracle_NNZ_LIBRARY) + set(Oracle_LIBRARIES + ${Oracle_OCI_LIBRARY} + ${Oracle_OCCI_LIBRARY} + ${Oracle_NNZ_LIBRARY}) +endif() -if(NOT WIN32) - set(ORACLE_LIBRARY ${ORACLE_LIBRARY} ${ORACLE_CLNTSH_LIBRARY}) +if(NOT WIN32 AND Oracle_CLNTSH_LIBRARY) + list(APPEND Oracle_LIBRARIES ${Oracle_CLNTSH_LIBRARY}) endif(NOT WIN32) -set(ORACLE_LIBRARIES ${ORACLE_LIBRARY}) - -# Handle the QUIETLY and REQUIRED arguments and set ORACLE_FOUND to TRUE -# if all listed variables are TRUE include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Oracle DEFAULT_MSG ORACLE_LIBRARY ORACLE_INCLUDE_DIR) +find_package_handle_standard_args(Oracle + REQUIRED_VARS Oracle_LIBRARIES Oracle_INCLUDE_DIRS) -if(NOT ORACLE_FOUND) - message(STATUS "None of the supported Oracle versions (${ORACLE_VERSIONS}) could be found, consider updating ORACLE_VERSIONS if the version you use is not among them.") +if(NOT Oracle_FOUND) + message(STATUS "None of the supported Oracle versions (${Oracle_VERSIONS}) could be found, consider updating Oracle_VERSIONS if the version you use is not among them.") endif() -mark_as_advanced(ORACLE_INCLUDE_DIR ORACLE_LIBRARY) +add_library(Oracle INTERFACE) +target_link_libraries(Oracle INTERFACE ${Oracle_LIBRARIES}) +target_include_directories(Oracle INTERFACE ${Oracle_INCLUDE_DIRS}) +add_library(Oracle::Oracle ALIAS Oracle) diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index 2f004466a..164c00222 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -6,6 +6,10 @@ else() set(REQUIRED_FLAG "REQUIRED") endif() +if (SOCI_FIREBIRD_EMBEDDED) + set(Firebird_SEARCH_EMBEDDED ON) +endif() + find_package(Firebird ${REQUIRED_FLAG}) if (NOT Firebird_FOUND) @@ -26,7 +30,7 @@ add_library(soci_firebird_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_firebird_objects PUBLIC Firebird::firebird) +target_link_libraries(soci_firebird_objects PUBLIC Firebird::Firebird) target_link_libraries(soci_firebird_objects PUBLIC soci_core_objects) target_include_directories(soci_firebird_objects diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 7c7c6449f..c2ae8cefd 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -24,7 +24,7 @@ add_library(soci_mysql_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_mysql_objects PUBLIC MySQL::mysql) +target_link_libraries(soci_mysql_objects PUBLIC MySQL::MySQL) target_link_libraries(soci_mysql_objects PUBLIC soci_core_objects) target_include_directories(soci_mysql_objects diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index faaa82faf..5b0eff67e 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -25,7 +25,7 @@ add_library(soci_oracle_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_oracle_objects PUBLIC Oracle::oracle) +target_link_libraries(soci_oracle_objects PUBLIC Oracle::Oracle) target_link_libraries(soci_oracle_objects PUBLIC soci_core_objects) target_include_directories(soci_oracle_objects From 4a378edae9764ca18a7a759e842552e6213ed78c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:17:53 +0100 Subject: [PATCH 05/79] Remove redundant condition in endif() --- cmake/find_modules/FindOracle.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/find_modules/FindOracle.cmake b/cmake/find_modules/FindOracle.cmake index c68ef7767..1319eed88 100644 --- a/cmake/find_modules/FindOracle.cmake +++ b/cmake/find_modules/FindOracle.cmake @@ -80,7 +80,7 @@ endif() if(NOT WIN32 AND Oracle_CLNTSH_LIBRARY) list(APPEND Oracle_LIBRARIES ${Oracle_CLNTSH_LIBRARY}) -endif(NOT WIN32) +endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Oracle From 926bfbba34bd4dc53c78caf858a0c39c0e26dedf Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:21:12 +0100 Subject: [PATCH 06/79] SOCI_HAVE_CXX11 doesn't exist anymore --- include/soci/soci-platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/soci/soci-platform.h b/include/soci/soci-platform.h index bb2b8eeff..e662565b8 100644 --- a/include/soci/soci-platform.h +++ b/include/soci/soci-platform.h @@ -21,7 +21,7 @@ #include #include -#include "soci/soci-config.h" // for SOCI_HAVE_CXX11 +#include "soci/soci-config.h" #if defined(_MSC_VER) #define LL_FMT_FLAGS "I64" From 1ccc2188b4513c04f1c2b82122b704cb87caca26 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:22:37 +0100 Subject: [PATCH 07/79] Configure soci-config.h (empty for now) --- src/core/CMakeLists.txt | 4 ++++ {include/soci => src/core}/soci-config.h.in | 0 2 files changed, 4 insertions(+) rename {include/soci => src/core}/soci-config.h.in (100%) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c30973bf4..18feb06be 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,6 +23,10 @@ add_library(soci_core_objects OBJECT "values.cpp" ) +# TODO: Actually populate this config file with something useful +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/soci-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/soci/soci-config.h") +target_include_directories(soci_core_objects PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include") + target_link_libraries(soci_core_objects PRIVATE Threads::Threads) if (SOCI_BOOST) diff --git a/include/soci/soci-config.h.in b/src/core/soci-config.h.in similarity index 100% rename from include/soci/soci-config.h.in rename to src/core/soci-config.h.in From ec64b18f4bbeca798a92f30e57285775f1fc276a Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:24:49 +0100 Subject: [PATCH 08/79] Disable Boost support by default --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3db2339c0..a55419198 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,8 @@ option(SOCI_ASAN "Enable building SOCI with enabled address sanitizers" OFF) cmake_dependent_option(SOCI_LTO "Enable link time optimizations in release builds" ON "LTO_AVAILABLE" OFF) option(SOCI_VISIBILITY "Make all functions hidden by default - this exposes only explicitly exported functions" ON) set(SOCI_LD "" CACHE STRING "Specify a non-default linker") -option(SOCI_BOOST "Whether to enable Boost-specific bindings" ON) +# Default-enable only if Boost has been found before (in a superproject) +option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${Boost_FOUND}) # Configure LTO for anything but Debug builds (if enabled in the first place) From 1489077d813819d55822968384befb39936d70f7 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 3 Jan 2024 17:28:52 +0100 Subject: [PATCH 09/79] Oracle: Remove non-existing CPP source --- src/backends/oracle/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 5b0eff67e..610160731 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -13,7 +13,6 @@ endif() add_library(soci_oracle_objects OBJECT "blob.cpp" - "clob.cpp" "error.cpp" "factory.cpp" "row-id.cpp" From 074bdf25f6ccfc3ca309c54c6dfd2fb411d1f62f Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 16 Feb 2024 20:21:25 +0100 Subject: [PATCH 10/79] Improved target setup --- CMakeLists.txt | 7 ++- src/CMakeLists.txt | 22 ++++++++ src/backends/CMakeLists.txt | 51 ++++++++++++++++-- src/backends/db2/CMakeLists.txt | 25 ++++----- src/backends/empty/CMakeLists.txt | 23 +++++---- src/backends/firebird/CMakeLists.txt | 25 ++++----- src/backends/mysql/CMakeLists.txt | 25 ++++----- src/backends/odbc/CMakeLists.txt | 25 ++++----- src/backends/oracle/CMakeLists.txt | 25 ++++----- src/backends/postgresql/CMakeLists.txt | 33 ++++++------ src/backends/sqlite3/CMakeLists.txt | 25 ++++----- src/core/CMakeLists.txt | 71 +++++++++++++++++++------- 12 files changed, 234 insertions(+), 123 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a55419198..05242b810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,10 +61,15 @@ 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) +# We need a dummy source file to add as sources for libs that otherwise +# only consist of object libraries (otherwise some toolchains may complain) +set(SOCI_CXX_DUMMY_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp") +if (NOT EXISTS "${SOCI_CXX_DUMMY_SOURCE}") + file(TOUCH "${SOCI_CXX_DUMMY_SOURCE}") +endif() add_subdirectory(src) - if (SOCI_TESTS) include(CTest) enable_testing() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c5fd47ef..54acfd04c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,2 +1,24 @@ +add_library(soci_shared_interface INTERFACE) +add_library(SOCI::shared::soci ALIAS soci_shared_interface) + +add_library(soci_static_interface INTERFACE) +add_library(SOCI::static::soci ALIAS soci_static_interface) + +add_library(soci_interface INTERFACE) +add_library(SOCI::soci ALIAS soci_interface) + +if (SOCI_STATIC AND SOCI_SHARED) + if (BUILD_SHARED_LIBS) + target_link_libraries(soci_interface INTERFACE SOCI::shared::soci) + else() + target_link_libraries(soci_interface INTERFACE SOCI::static::soci) + endif() +elseif(SOCI_STATIC) + target_link_libraries(soci_interface INTERFACE SOCI::static::soci) +else() + target_link_libraries(soci_interface INTERFACE SOCI::shared::soci) +endif() + + add_subdirectory(core) add_subdirectory(backends) diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index a5b4e5489..acb05fe7c 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,3 +1,5 @@ +include(soci_utils) + set(SOCI_EMPTY ${PROJECT_IS_TOP_LEVEL} CACHE STRING "Include the 'empty' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") set(SOCI_DB2 "AsAvailable" CACHE STRING "Include the 'DB2' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") @@ -9,6 +11,45 @@ set(SOCI_POSTGRESQL "AsAvailable" CACHE STRING "Include the 'Postgresql' backend set(SOCI_SQLITE3 "AsAvailable" CACHE STRING "Include the 'SQLite3' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") +function(soci_backend_objects_to_lib) + set(FLAGS "") + set(ONE_VAL_OPTIONS "OBJECT_LIB" "SHARED_TARGET_NAME" "STATIC_TARGET_NAME" "ALIAS_NAME") + set(MULTI_VAL_OPTIONS "SHARED_DEPS" "STATIC_DEPS") + cmake_parse_arguments(SOCI_BACKEND_LIB "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) + soci_verify_parsed_arguments( + PREFIX "SOCI_BACKEND_LIB" + FUNCTION_NAME "soci_backend_objects_to_lib" + REQUIRED "OBJECT_LIB" "SHARED_TARGET_NAME" "STATIC_TARGET_NAME" "ALIAS_NAME" + ) + + if (SOCI_SHARED) + add_library(${SOCI_BACKEND_LIB_SHARED_TARGET_NAME} SHARED "${SOCI_CXX_DUMMY_SOURCE}") + target_link_libraries(${SOCI_BACKEND_LIB_SHARED_TARGET_NAME} + PUBLIC + ${SOCI_BACKEND_LIB_OBJECT_LIB} + ${SOCI_BACKEND_LIB_SHARED_DEPS} + ) + + add_library(SOCI::shared::${SOCI_BACKEND_LIB_ALIAS_NAME} ALIAS ${SOCI_BACKEND_LIB_SHARED_TARGET_NAME}) + + target_link_libraries(soci_shared_interface INTERFACE SOCI::shared::${SOCI_BACKEND_LIB_ALIAS_NAME}) + endif() + + if (SOCI_STATIC) + add_library(${SOCI_BACKEND_LIB_STATIC_TARGET_NAME} STATIC "${SOCI_CXX_DUMMY_SOURCE}") + target_link_libraries(${SOCI_BACKEND_LIB_STATIC_TARGET_NAME} + PUBLIC + ${SOCI_BACKEND_LIB_OBJECT_LIB} + ${SOCI_BACKEND_LIB_STATIC_DEPS} + ) + + add_library(SOCI::static::${SOCI_BACKEND_LIB_ALIAS_NAME} ALIAS ${SOCI_BACKEND_LIB_STATIC_TARGET_NAME}) + + target_link_libraries(soci_static_interface INTERFACE SOCI::static::${SOCI_BACKEND_LIB_ALIAS_NAME}) + endif() +endfunction() + + set(BACKENDS "empty" "db2" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") foreach(CURRENT IN LISTS BACKENDS) string(TOUPPER "${CURRENT}" CURRENT_UPPER) @@ -22,13 +63,15 @@ foreach(CURRENT IN LISTS BACKENDS) unset(WITH_${CURRENT_UPPER} CACHE) endif() + set(SOCI_${CURRENT_UPPER}_AS_AVAILABLE OFF CACHE INTERNAL "" FORCE) + if (SOCI_${CURRENT_UPPER} STREQUAL "Enabled") - set(SOCI_${CURRENT_UPPER} ON) + set(SOCI_${CURRENT_UPPER} ON CACHE BOOL "" FORCE) elseif(SOCI_${CURRENT_UPPER} STREQUAL "Disabled") - set(SOCI_${CURRENT_UPPER} OFF) + set(SOCI_${CURRENT_UPPER} OFF CACHE BOOL "" FORCE) elseif(SOCI_${CURRENT_UPPER} STREQUAL "AsAvailable") - set(SOCI_${CURRENT_UPPER} ON) - set(SOCI_${CURRENT_UPPER}_AS_AVAILABLE ON CACHE INTERNAL "") + set(SOCI_${CURRENT_UPPER} ON CACHE BOOL "" FORCE) + set(SOCI_${CURRENT_UPPER}_AS_AVAILABLE ON CACHE INTERNAL "" FORCE) elseif(SOCI_${CURRENT_UPPER} MATCHES "[Oo][Nn]|[Oo][Ff][Ff]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Yy][Ee][Ss]|[Nn][Oo]|[YyNn01]") # Do nothing - these are already booleans else() diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index f2694968f..0b5bdeeac 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(DB2 ${REQUIRED_FLAG}) if (NOT DB2_FOUND) message(STATUS "Disabling DB2 backend as the required dependencies were not found") + set(SOCI_DB2 OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_db2_interface INTERFACE) +target_link_libraries(soci_db2_interface INTERFACE DB2::DB2 soci_core_interface) + add_library(soci_db2_objects OBJECT "blob.cpp" "factory.cpp" @@ -23,8 +27,7 @@ add_library(soci_db2_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_db2_objects PUBLIC DB2::db2) -target_link_libraries(soci_db2_objects PUBLIC soci_core_objects) +target_link_libraries(soci_db2_objects PUBLIC soci_db2_interface) target_include_directories(soci_db2_objects PRIVATE @@ -32,13 +35,11 @@ target_include_directories(soci_db2_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_db2) - target_link_libraries(soci_db2 PUBLIC soci_db2_objects) - add_library(SOCI::shared::DB2 ALIAS soci_db2) -endif() -if (SOCI_STATIC) - add_library(soci_db2_static) - target_link_libraries(soci_db2_static PUBLIC soci_db2_objects) - add_library(SOCI::static::DB2 ALIAS soci_db2_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_db2_objects + SHARED_TARGET_NAME soci_db2 + STATIC_TARGET_NAME soci_db2_static + ALIAS_NAME DB2 + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index 5e4f3dc83..c2bf3966f 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -1,3 +1,6 @@ +add_library(soci_empty_interface INTERFACE) +target_link_libraries(soci_empty_interface INTERFACE soci_core_interface) + add_library(soci_empty_objects OBJECT "blob.cpp" "factory.cpp" @@ -10,7 +13,7 @@ add_library(soci_empty_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_empty_objects PUBLIC soci_core_objects) +target_link_libraries(soci_empty_objects PUBLIC soci_empty_interface) target_include_directories(soci_empty_objects PRIVATE @@ -18,13 +21,11 @@ target_include_directories(soci_empty_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_empty) - target_link_libraries(soci_empty PUBLIC soci_empty_objects) - add_library(SOCI::shared::Empty ALIAS soci_empty) -endif() -if (SOCI_STATIC) - add_library(soci_empty_static) - target_link_libraries(soci_empty_static PUBLIC soci_empty_objects) - add_library(SOCI::static::Empty ALIAS soci_empty_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_empty_objects + SHARED_TARGET_NAME soci_empty + STATIC_TARGET_NAME soci_empty_static + ALIAS_NAME Empty + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index 164c00222..e97ab1c92 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -14,9 +14,13 @@ find_package(Firebird ${REQUIRED_FLAG}) if (NOT Firebird_FOUND) message(STATUS "Disabling Firebird backend as the required dependencies were not found") + set(SOCI_FIREBIRD OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_firebird_interface INTERFACE) +target_link_libraries(soci_firebird_interface INTERFACE Firebird::Firebird soci_core_interface) + add_library(soci_firebird_objects OBJECT "blob.cpp" "common.cpp" @@ -30,8 +34,7 @@ add_library(soci_firebird_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_firebird_objects PUBLIC Firebird::Firebird) -target_link_libraries(soci_firebird_objects PUBLIC soci_core_objects) +target_link_libraries(soci_firebird_objects PUBLIC Firebird::Firebird soci_firebird_interface) target_include_directories(soci_firebird_objects PRIVATE @@ -39,13 +42,11 @@ target_include_directories(soci_firebird_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_firebird) - target_link_libraries(soci_firebird PUBLIC soci_firebird_objects) - add_library(SOCI::shared::Firebird ALIAS soci_firebird) -endif() -if (SOCI_STATIC) - add_library(soci_firebird_static) - target_link_libraries(soci_firebird_static PUBLIC soci_firebird_objects) - add_library(SOCI::static::Firebird ALIAS soci_firebird_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_firebird_objects + SHARED_TARGET_NAME soci_firebird + STATIC_TARGET_NAME soci_firebird_static + ALIAS_NAME Firebird + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index c2ae8cefd..c6ce7f3ac 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(MySQL ${REQUIRED_FLAG}) if (NOT MySQL_FOUND) message(STATUS "Disabling MySQL backend as the required dependencies were not found") + set(SOCI_MYSQL OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_mysql_interface INTERFACE) +target_link_libraries(soci_mysql_interface INTERFACE MySQL::MySQL soci_core_interface) + add_library(soci_mysql_objects OBJECT "blob.cpp" "common.cpp" @@ -24,8 +28,7 @@ add_library(soci_mysql_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_mysql_objects PUBLIC MySQL::MySQL) -target_link_libraries(soci_mysql_objects PUBLIC soci_core_objects) +target_link_libraries(soci_mysql_objects PUBLIC soci_mysql_interface) target_include_directories(soci_mysql_objects PRIVATE @@ -33,13 +36,11 @@ target_include_directories(soci_mysql_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_mysql) - target_link_libraries(soci_mysql PUBLIC soci_mysql_objects) - add_library(SOCI::shared::MySQL ALIAS soci_mysql) -endif() -if (SOCI_STATIC) - add_library(soci_mysql_static) - target_link_libraries(soci_mysql_static PUBLIC soci_mysql_objects) - add_library(SOCI::static::MySQL ALIAS soci_mysql_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_mysql_objects + SHARED_TARGET_NAME soci_mysql + STATIC_TARGET_NAME soci_mysql_static + ALIAS_NAME MySQL + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index 8e7257b08..0b2856e58 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(ODBC ${REQUIRED_FLAG}) if (NOT ODBC_FOUND) message(STATUS "Disabling ODBC backend as the required dependencies were not found") + set(SOCI_ODBC OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_odbc_interface INTERFACE) +target_link_libraries(soci_odbc_interface INTERFACE ODBC::ODBC soci_core_interface) + add_library(soci_odbc_objects OBJECT "blob.cpp" "factory.cpp" @@ -23,8 +27,7 @@ add_library(soci_odbc_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_odbc_objects PUBLIC ODBC::ODBC) -target_link_libraries(soci_odbc_objects PUBLIC soci_core_objects) +target_link_libraries(soci_odbc_objects PUBLIC soci_odbc_interface) target_include_directories(soci_odbc_objects PRIVATE @@ -32,13 +35,11 @@ target_include_directories(soci_odbc_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_odbc) - target_link_libraries(soci_odbc PUBLIC soci_odbc_objects) - add_library(SOCI::shared::ODBC ALIAS soci_odbc) -endif() -if (SOCI_STATIC) - add_library(soci_odbc_static) - target_link_libraries(soci_odbc_static PUBLIC soci_odbc_objects) - add_library(SOCI::static::ODBC ALIAS soci_odbc_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_odbc_objects + SHARED_TARGET_NAME soci_odbc + STATIC_TARGET_NAME soci_odbc_static + ALIAS_NAME ODBC + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 610160731..eca72db1d 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(Oracle ${REQUIRED_FLAG}) if (NOT Oracle_FOUND) message(STATUS "Disabling Oracle backend as the required dependencies were not found") + set(SOCI_ORACLE OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_oracle_interface INTERFACE) +target_link_libraries(soci_oracle_interface INTERFACE Oracle::Oracle soci_core_interface) + add_library(soci_oracle_objects OBJECT "blob.cpp" "error.cpp" @@ -24,8 +28,7 @@ add_library(soci_oracle_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_oracle_objects PUBLIC Oracle::Oracle) -target_link_libraries(soci_oracle_objects PUBLIC soci_core_objects) +target_link_libraries(soci_oracle_objects PUBLIC soci_oracle_interface) target_include_directories(soci_oracle_objects PRIVATE @@ -33,13 +36,11 @@ target_include_directories(soci_oracle_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_oracle) - target_link_libraries(soci_oracle PUBLIC soci_oracle_objects) - add_library(SOCI::shared::Oracle ALIAS soci_oracle) -endif() -if (SOCI_STATIC) - add_library(soci_oracle_static) - target_link_libraries(soci_oracle_static PUBLIC soci_oracle_objects) - add_library(SOCI::static::Oracle ALIAS soci_oracle_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_oracle_objects + SHARED_TARGET_NAME soci_oracle + STATIC_TARGET_NAME soci_oracle_static + ALIAS_NAME Oracle + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index afe20b0b5..93675be0b 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(PostgreSQL ${REQUIRED_FLAG}) if (NOT PostgreSQL_FOUND) message(STATUS "Disabling PostgreSQL backend as the required dependencies were not found") + set(SOCI_POSTGRESQL OFF CACHE BOOL "" FORCE) return() endif() @@ -19,6 +20,9 @@ if (POSTGRESQL_VERSION VERSION_LESS "9.3.0") set(SOCI_POSTGRESQL_NO_LO64 ON CACHE BOOL "Avoid using lo_xxx64() functions" FORCE) endif() +add_library(soci_postgresql_interface INTERFACE) +target_link_libraries(soci_postgresql_interface INTERFACE PostgreSQL::PostgreSQL soci_core_interface) + add_library(soci_postgresql_objects OBJECT "blob.cpp" "error.cpp" @@ -32,8 +36,7 @@ add_library(soci_postgresql_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_postgresql_objects PUBLIC PostgreSQL::PostgreSQL) -target_link_libraries(soci_postgresql_objects PUBLIC soci_core_objects) +target_link_libraries(soci_postgresql_objects PUBLIC soci_postgresql_interface) target_include_directories(soci_postgresql_objects PRIVATE @@ -41,19 +44,15 @@ target_include_directories(soci_postgresql_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_postgresql) - target_link_libraries(soci_postgresql PUBLIC soci_postgresql_objects) - add_library(SOCI::shared::PostgreSQL ALIAS soci_postgresql) - if (SOCI_POSTGRESQL_NO_LO64) - target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) - endif() -endif() -if (SOCI_STATIC) - add_library(soci_postgresql_static) - target_link_libraries(soci_postgresql_static PUBLIC soci_postgresql_objects) - add_library(SOCI::static::PostgreSQL ALIAS soci_postgresql_static) - if (SOCI_POSTGRESQL_NO_LO64) - target_compile_definitions(soci_postgresql_static INTERFACE SOCI_POSTGRESQL_NO_LO64) - endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_postgresql_objects + SHARED_TARGET_NAME soci_postgresql + STATIC_TARGET_NAME soci_postgresql_static + ALIAS_NAME PostgreSQL + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) +if (SOCI_POSTGRESQL_NO_LO64) + target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) + target_compile_definitions(soci_postgresql_static INTERFACE SOCI_POSTGRESQL_NO_LO64) endif() diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 264455c84..1339bd697 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(SQLite3 ${REQUIRED_FLAG}) if (NOT SQLite3_FOUND) message(STATUS "Disabling SQLite3 backend as the required dependencies were not found") + set(SOCI_SQLITE3 OFF CACHE BOOL "" FORCE) return() endif() +add_library(soci_sqlite3_interface INTERFACE) +target_link_libraries(soci_sqlite3_interface INTERFACE SQLite::SQLite3 soci_core_interface) + add_library(soci_sqlite3_objects OBJECT "blob.cpp" "error.cpp" @@ -24,8 +28,7 @@ add_library(soci_sqlite3_objects OBJECT "vector-use-type.cpp" ) -target_link_libraries(soci_sqlite3_objects PUBLIC SQLite::SQLite3) -target_link_libraries(soci_sqlite3_objects PUBLIC soci_core_objects) +target_link_libraries(soci_sqlite3_objects PUBLIC soci_sqlite3_interface) target_include_directories(soci_sqlite3_objects PRIVATE @@ -33,13 +36,11 @@ target_include_directories(soci_sqlite3_objects "${PROJECT_SOURCE_DIR}/include/soci" ) -if (SOCI_SHARED) - add_library(soci_sqlite3) - target_link_libraries(soci_sqlite3 PUBLIC soci_sqlite3_objects) - add_library(SOCI::shared::SQLite3 ALIAS soci_sqlite3) -endif() -if (SOCI_STATIC) - add_library(soci_sqlite3_static) - target_link_libraries(soci_sqlite3_static PUBLIC soci_sqlite3_objects) - add_library(SOCI::static::SQLite3 ALIAS soci_sqlite3_static) -endif() +soci_backend_objects_to_lib( + OBJECT_LIB soci_sqlite3_objects + SHARED_TARGET_NAME soci_sqlite3 + STATIC_TARGET_NAME soci_sqlite3_static + ALIAS_NAME SQLite3 + SHARED_DEPS SOCI::shared::core + STATIC_DEPS SOCI::static::core +) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 18feb06be..4c02467a3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,49 @@ find_package(Threads REQUIRED) +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + " + __attribute__ (( visibility(\"default\") )) int f1() { return 0; } + __attribute__ (( visibility(\"hidden\") )) int f2() { return 1; } + + int main(int argc, char* argv[]) { f1(); f2(); return 0; } + " + SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED +) + +# TODO: Actually populate this config file with something useful +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/soci-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/soci/soci-config.h") + +add_library(soci_core_interface INTERFACE) +target_include_directories(soci_core_interface + INTERFACE + "${CMAKE_CURRENT_BINARY_DIR}/include" + "${PROJECT_SOURCE_DIR}/include" +) +target_compile_definitions(soci_core_interface + INTERFACE + # Define the macro SOCI_DLL on Windows + $,SOCI_DLL,> +) + +if (SOCI_VISIBILITY AND SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED) + target_compile_definitions(soci_core_interface + INTERFACE + SOCI_HAVE_VISIBILITY_SUPPORT + ) +endif() + +if (SOCI_BOOST) + find_package(Boost REQUIRED) + target_link_libraries(soci_core_interface INTERFACE Boost::boost) + target_compile_definitions(soci_core_interface INTERFACE SOCI_HAVE_BOOST) + + if (TARGET Boost::date_time) + target_link_libraries(soci_core_interface INTERFACE Boost::date_time) + target_compile_definitions(soci_core_interface INTERFACE SOCI_HAVE_BOOST_DATE_TIME) + endif() +endif() + add_library(soci_core_objects OBJECT "backend-loader.cpp" "blob.cpp" @@ -12,6 +56,7 @@ add_library(soci_core_objects OBJECT "once-temp-type.cpp" "prepare-temp-type.cpp" "procedure.cpp" + "ref-counted-prepare-info.cpp" "ref-counted-statement.cpp" "row.cpp" "rowid.cpp" @@ -23,24 +68,13 @@ add_library(soci_core_objects OBJECT "values.cpp" ) -# TODO: Actually populate this config file with something useful -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/soci-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/soci/soci-config.h") -target_include_directories(soci_core_objects PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include") - -target_link_libraries(soci_core_objects PRIVATE Threads::Threads) - -if (SOCI_BOOST) - find_package(Boost REQUIRED) - target_link_libraries(soci_core_objects PUBLIC Boost::boost) - target_compile_definitions(soci_core_objects PUBLIC SOCI_HAVE_BOOST) - - if (TARGET Boost::date_time) - target_link_libraries(soci_core_objects PUBLIC Boost::date_time) - target_compile_definitions(soci_core_objects PUBLIC SOCI_HAVE_BOOST_DATE_TIME) - endif() -endif() +target_link_libraries(soci_core_objects + PUBLIC + soci_core_interface + PRIVATE + Threads::Threads +) -target_include_directories(soci_core_objects PUBLIC "${PROJECT_SOURCE_DIR}/include") target_include_directories(soci_core_objects PRIVATE "${PROJECT_SOURCE_DIR}/include/soci" @@ -55,14 +89,15 @@ target_compile_definitions(soci_core_objects SOCI_LIB_SUFFIX="" ) - if (SOCI_SHARED) add_library(soci_core SHARED) target_link_libraries(soci_core PUBLIC soci_core_objects) add_library(SOCI::shared::core ALIAS soci_core) + target_link_libraries(soci_shared_interface INTERFACE SOCI::shared::core) endif() if (SOCI_STATIC) add_library(soci_core_static STATIC) target_link_libraries(soci_core_static PUBLIC soci_core_objects) add_library(SOCI::static::core ALIAS soci_core_static) + target_link_libraries(soci_static_interface INTERFACE SOCI::static::core) endif() From 456a8cc6aeff2e01cca58e9570e9f63f0aa15ed9 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 15:25:50 +0100 Subject: [PATCH 11/79] Include building tests --- src/backends/CMakeLists.txt | 3 +- src/backends/db2/CMakeLists.txt | 4 +- src/backends/empty/CMakeLists.txt | 4 +- src/backends/firebird/CMakeLists.txt | 4 +- src/backends/mysql/CMakeLists.txt | 4 +- src/backends/odbc/CMakeLists.txt | 4 +- src/backends/oracle/CMakeLists.txt | 4 +- src/backends/postgresql/CMakeLists.txt | 4 +- src/backends/sqlite3/CMakeLists.txt | 4 +- src/core/CMakeLists.txt | 4 +- tests/CMakeLists.txt | 59 + tests/common-tests.cpp | 6211 +++++++++++++++ tests/common-tests.h | 6895 +---------------- tests/db2/CMakeLists.txt | 24 +- tests/db2/{test-db2.cpp => db2_tests.cpp} | 49 +- tests/empty/CMakeLists.txt | 24 +- .../empty/{test-empty.cpp => empty_tests.cpp} | 6 +- tests/firebird/CMakeLists.txt | 24 +- .../{test-firebird.cpp => firebird_tests.cpp} | 52 +- tests/mysql/CMakeLists.txt | 24 +- .../mysql/{test-mysql.cpp => mysql_tests.cpp} | 41 +- tests/mysql/{test-mysql.h => mysql_tests.h} | 0 tests/oracle/CMakeLists.txt | 24 +- .../{test-oracle.cpp => oracle_tests.cpp} | 55 +- tests/postgresql/CMakeLists.txt | 24 +- ...st-postgresql.cpp => postgresql_tests.cpp} | 50 +- tests/sqlite3/CMakeLists.txt | 25 +- .../{test-sqlite3.cpp => sqlite3_tests.cpp} | 49 +- 28 files changed, 6656 insertions(+), 7019 deletions(-) create mode 100644 tests/common-tests.cpp rename tests/db2/{test-db2.cpp => db2_tests.cpp} (92%) rename tests/empty/{test-empty.cpp => empty_tests.cpp} (100%) rename tests/firebird/{test-firebird.cpp => firebird_tests.cpp} (96%) rename tests/mysql/{test-mysql.cpp => mysql_tests.cpp} (97%) rename tests/mysql/{test-mysql.h => mysql_tests.h} (100%) rename tests/oracle/{test-oracle.cpp => oracle_tests.cpp} (97%) rename tests/postgresql/{test-postgresql.cpp => postgresql_tests.cpp} (97%) rename tests/sqlite3/{test-sqlite3.cpp => sqlite3_tests.cpp} (96%) diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index acb05fe7c..052393683 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -50,8 +50,7 @@ function(soci_backend_objects_to_lib) endfunction() -set(BACKENDS "empty" "db2" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") -foreach(CURRENT IN LISTS BACKENDS) +foreach(CURRENT IN ITEMS "db2" "empty" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") string(TOUPPER "${CURRENT}" CURRENT_UPPER) # Backwards compatibility with the old cmake setup that used WITH_* variables diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index 0b5bdeeac..fe3ac0503 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -40,6 +40,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_db2 STATIC_TARGET_NAME soci_db2_static ALIAS_NAME DB2 - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index c2bf3966f..daf0a5f21 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -26,6 +26,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_empty STATIC_TARGET_NAME soci_empty_static ALIAS_NAME Empty - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index e97ab1c92..a7a93d04b 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -47,6 +47,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_firebird STATIC_TARGET_NAME soci_firebird_static ALIAS_NAME Firebird - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index c6ce7f3ac..1aefac598 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -41,6 +41,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_mysql STATIC_TARGET_NAME soci_mysql_static ALIAS_NAME MySQL - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index 0b2856e58..7310f0347 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -40,6 +40,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_odbc STATIC_TARGET_NAME soci_odbc_static ALIAS_NAME ODBC - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index eca72db1d..7dbd0c31e 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -41,6 +41,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_oracle STATIC_TARGET_NAME soci_oracle_static ALIAS_NAME Oracle - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 93675be0b..d927eca57 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -49,8 +49,8 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_postgresql STATIC_TARGET_NAME soci_postgresql_static ALIAS_NAME PostgreSQL - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) if (SOCI_POSTGRESQL_NO_LO64) target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 1339bd697..31852f92d 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -41,6 +41,6 @@ soci_backend_objects_to_lib( SHARED_TARGET_NAME soci_sqlite3 STATIC_TARGET_NAME soci_sqlite3_static ALIAS_NAME SQLite3 - SHARED_DEPS SOCI::shared::core - STATIC_DEPS SOCI::static::core + SHARED_DEPS SOCI::shared::Core + STATIC_DEPS SOCI::static::Core ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4c02467a3..d1aa7ff22 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -92,12 +92,12 @@ target_compile_definitions(soci_core_objects if (SOCI_SHARED) add_library(soci_core SHARED) target_link_libraries(soci_core PUBLIC soci_core_objects) - add_library(SOCI::shared::core ALIAS soci_core) + add_library(SOCI::shared::Core ALIAS soci_core) target_link_libraries(soci_shared_interface INTERFACE SOCI::shared::core) endif() if (SOCI_STATIC) add_library(soci_core_static STATIC) target_link_libraries(soci_core_static PUBLIC soci_core_objects) - add_library(SOCI::static::core ALIAS soci_core_static) + add_library(SOCI::static::Core ALIAS soci_core_static) target_link_libraries(soci_static_interface INTERFACE SOCI::static::core) endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e69de29bb..b7fe2e797 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -0,0 +1,59 @@ +include(soci_utils) + +add_library(soci_common_tests STATIC common-tests.cpp) +target_include_directories(soci_common_tests + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) +target_link_libraries(soci_common_tests PUBLIC soci_core_interface) +target_include_directories(soci_common_tests PUBLIC "${PROJECT_SOURCE_DIR}/include/private/") + + +function(soci_make_tests) + set(FLAGS "") + set(ONE_VAL_OPTIONS "CONNECTION_STRING" "OBJECT_LIB" "SOCI_DEP_ALIAS" "SHARED_NAME" "STATIC_NAME") + set(MULTI_VAL_OPTIONS "") + cmake_parse_arguments(SOCI_MAKE_TESTS "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) + soci_verify_parsed_arguments( + PREFIX "SOCI_MAKE_TESTS" + FUNCTION_NAME "soci_make_tests" + REQUIRED "CONNECTION_STRING" "OBJECT_LIB" "SHARED_NAME" "STATIC_NAME" + ) + + set(CREATED_TESTS "") + + if (SOCI_SHARED) + add_executable(${SOCI_MAKE_TESTS_SHARED_NAME} "${SOCI_CXX_DUMMY_SOURCE}") + target_link_libraries(${SOCI_MAKE_TESTS_SHARED_NAME} PRIVATE ${SOCI_MAKE_TESTS_OBJECT_LIB}) + if (SOCI_MAKE_TESTS_SOCI_DEP_ALIAS) + target_link_libraries(${SOCI_MAKE_TESTS_SHARED_NAME} PRIVATE SOCI::shared::${SOCI_MAKE_TESTS_SOCI_DEP_ALIAS}) + endif() + + list(APPEND CREATED_TESTS "${SOCI_MAKE_TESTS_SHARED_NAME}") + endif() + + if (SOCI_STATIC) + add_executable(${SOCI_MAKE_TESTS_STATIC_NAME} "${SOCI_CXX_DUMMY_SOURCE}") + target_link_libraries(${SOCI_MAKE_TESTS_STATIC_NAME} PRIVATE ${SOCI_MAKE_TESTS_OBJECT_LIB}) + if (SOCI_MAKE_TESTS_SOCI_DEP_ALIAS) + target_link_libraries(${SOCI_MAKE_TESTS_STATIC_NAME} PRIVATE SOCI::static::${SOCI_MAKE_TESTS_SOCI_DEP_ALIAS}) + endif() + + list(APPEND CREATED_TESTS "${SOCI_MAKE_TESTS_STATIC_NAME}") + endif() + + foreach (CURRENT IN LISTS CREATED_TESTS) + add_test( + NAME "${CURRENT}" + COMMAND "${CURRENT}" "${SOCI_MAKE_TESTS_CONNECTION_STRING}" "--invisibles" + ) + endforeach() +endfunction() + +foreach (CURRENT_BACKEND IN ITEMS "db2" "empty" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") + string(TOUPPER "${CURRENT_BACKEND}" CURRENT_BACKEND_UPPER) + + if (SOCI_${CURRENT_BACKEND_UPPER} AND NOT SOCI_${CURRENT_BACKEND_UPPER}_DO_NOT_TEST) + add_subdirectory(${CURRENT_BACKEND}) + endif() +endforeach() diff --git a/tests/common-tests.cpp b/tests/common-tests.cpp new file mode 100644 index 000000000..23eee449f --- /dev/null +++ b/tests/common-tests.cpp @@ -0,0 +1,6211 @@ +// +// Copyright (C) 2004-2008 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) +// + +// Important: we have to include catch before common.tests.h in order to +// make the CATCH_CONFIG_RUNNER definition have an effect +#define CATCH_CONFIG_RUNNER +#include + +#include "common-tests.h" + +#include "soci/soci.h" + +#include "soci-compiler.h" + +#include "soci/callbacks.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace soci +{ +namespace tests +{ + +test_context_base* test_context_base::the_test_context_ = nullptr; + +class common_tests +{ +public: + common_tests() + : tc_(test_context_base::get_instance()), + backEndFactory_(tc_.get_backend_factory()), + connectString_(tc_.get_connect_string()) + {} + +protected: + test_context_base const & tc_; + backend_factory const &backEndFactory_; + std::string const connectString_; + + SOCI_NOT_COPYABLE(common_tests) +}; + +using auto_table_creator = std::unique_ptr; + +// Define the test cases in their own namespace to avoid clashes with the test +// cases defined in individual backend tests: as only line number is used for +// building the name of the "anonymous" function by the TEST_CASE macro, we +// could have a conflict between a test defined here and in some backend if +// they happened to start on the same line. +namespace test_cases +{ + +inline bool operator== ( const std::tm& a, const std::tm& b ) +{ + return a.tm_sec == b.tm_sec && a.tm_min == b.tm_min && a.tm_hour == b.tm_hour && a.tm_mday == b.tm_mday && a.tm_mon == b.tm_mon && + a.tm_year == b.tm_year && a.tm_wday == b.tm_wday && a.tm_yday == b.tm_yday && a.tm_isdst == b.tm_isdst; +} + +TEST_CASE_METHOD ( common_tests, "timegm implementation", "[core][timegm]" ) +{ + std::tm t1; + t1.tm_year = 105; + t1.tm_mon = 13; // + 1 year + t1.tm_mday = 15; + t1.tm_hour = 28; // + 1 day + t1.tm_min = 14; + t1.tm_sec = 17; + + std::tm t2 = t1; + + const auto timegm_result = timegm (&t1); + const auto timegm_soci_result = details::timegm_impl_soci (&t2); + CHECK ( timegm_result == timegm_soci_result ); + CHECK ( t1.tm_year == 106 ); + CHECK ( t1.tm_mon == 1 ); + CHECK ( t1.tm_mday == 16 ); + CHECK ( t1.tm_hour == 4 ); + CHECK ( ( t1 == t2 ) ); +} + +TEST_CASE_METHOD(common_tests, "Exception on not connected", "[core][exception]") +{ + soci::session sql; // no connection + + // ensure connection is checked, no crash occurs + CHECK_THROWS_AS(sql.begin(), soci_error); + CHECK_THROWS_AS(sql.commit(), soci_error); + CHECK_THROWS_AS(sql.rollback(), soci_error); + CHECK_THROWS_AS(sql.get_backend_name(), soci_error); + CHECK_THROWS_AS(sql.make_statement_backend(), soci_error); + CHECK_THROWS_AS(sql.make_rowid_backend(), soci_error); + CHECK_THROWS_AS(sql.make_blob_backend(), soci_error); + + std::string s; + long long l; + CHECK_THROWS_AS(sql.get_next_sequence_value(s, l), soci_error); + CHECK_THROWS_AS(sql.get_last_insert_id(s, l), soci_error); +} + +TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + CHECK_THROWS_AS(sql << "drop table soci_test_nosuchtable", soci_error); + + sql << "insert into soci_test (id) values (" << 123 << ")"; + int id; + sql << "select id from soci_test", into(id); + CHECK(id == 123); + + sql << "insert into soci_test (id) values (" << 234 << ")"; + sql << "insert into soci_test (id) values (" << 345 << ")"; + // Test prepare, execute, fetch correctness + statement st = (sql.prepare << "select id from soci_test", into(id)); + st.execute(); + int count = 0; + while(st.fetch()) + count++; + CHECK(count == 3 ); + bool fetchEnd = st.fetch(); // All the data has been read here so additional fetch must return false + CHECK(fetchEnd == false); +} + +// "into" tests, type conversions, etc. +TEST_CASE_METHOD(common_tests, "Use and into", "[core][into]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + SECTION("Round trip works for char") + { + char c('a'); + sql << "insert into soci_test(c) values(:c)", use(c); + sql << "select c from soci_test", into(c); + CHECK(c == 'a'); + } + + SECTION("Round trip works for string") + { + std::string helloSOCI("Hello, SOCI!"); + sql << "insert into soci_test(str) values(:s)", use(helloSOCI); + std::string str; + sql << "select str from soci_test", into(str); + CHECK(str == "Hello, SOCI!"); + } + + SECTION("Round trip works for short") + { + short three(3); + sql << "insert into soci_test(sh) values(:id)", use(three); + short sh(0); + sql << "select sh from soci_test", into(sh); + CHECK(sh == 3); + } + + SECTION("Round trip works for int") + { + int five(5); + sql << "insert into soci_test(id) values(:id)", use(five); + int i(0); + sql << "select id from soci_test", into(i); + CHECK(i == 5); + } + + SECTION("Round trip works for unsigned long") + { + unsigned long seven(7); + sql << "insert into soci_test(ul) values(:ul)", use(seven); + unsigned long ul(0); + sql << "select ul from soci_test", into(ul); + CHECK(ul == 7); + } + + SECTION("Round trip works for double") + { + double pi(3.14159265); + sql << "insert into soci_test(d) values(:d)", use(pi); + double d(0.0); + sql << "select d from soci_test", into(d); + ASSERT_EQUAL(d, pi); + } + + SECTION("Round trip works for date without time") + { + std::tm nov15 = std::tm(); + nov15.tm_year = 105; + nov15.tm_mon = 10; + nov15.tm_mday = 15; + nov15.tm_hour = 0; + nov15.tm_min = 0; + nov15.tm_sec = 0; + + sql << "insert into soci_test(tm) values(:tm)", use(nov15); + + std::tm t = std::tm(); + sql << "select tm from soci_test", into(t); + CHECK(t.tm_year == 105); + CHECK(t.tm_mon == 10); + CHECK(t.tm_mday == 15); + CHECK(t.tm_hour == 0); + CHECK(t.tm_min == 0); + CHECK(t.tm_sec == 0); + } + + SECTION("Round trip works for date with time") + { + std::tm nov15 = std::tm(); + nov15.tm_year = 105; + nov15.tm_mon = 10; + nov15.tm_mday = 15; + nov15.tm_hour = 22; + nov15.tm_min = 14; + nov15.tm_sec = 17; + + sql << "insert into soci_test(tm) values(:tm)", use(nov15); + + std::tm t = std::tm(); + sql << "select tm from soci_test", into(t); + CHECK(t.tm_year == 105); + CHECK(t.tm_mon == 10); + CHECK(t.tm_mday == 15); + CHECK(t.tm_hour == 22); + CHECK(t.tm_min == 14); + CHECK(t.tm_sec == 17); + } + + SECTION("Indicator is filled correctly in the simplest case") + { + int id(1); + std::string str("Hello"); + sql << "insert into soci_test(id, str) values(:id, :str)", + use(id), use(str); + + int i; + indicator ind; + sql << "select id from soci_test", into(i, ind); + CHECK(ind == i_ok); + } + + SECTION("Indicators work correctly more generally") + { + sql << "insert into soci_test(id,tm) values(NULL,NULL)"; + int i; + indicator ind; + sql << "select id from soci_test", into(i, ind); + CHECK(ind == i_null); + + // additional test for NULL with std::tm + std::tm t = std::tm(); + sql << "select tm from soci_test", into(t, ind); + CHECK(ind == i_null); + + // indicator should be initialized even when nothing is found + ind = i_ok; + sql << "select id from soci_test where str='NO SUCH ROW'", + into(i, ind); + CHECK(ind == i_null); + + try + { + // expect error + sql << "select id from soci_test", into(i); + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "Null value fetched and no indicator defined."); + CHECK_THAT(e.what(), + Catch::Contains("for the parameter number 1")); + } + + sql << "select id from soci_test where id = 1000", into(i, ind); + CHECK(sql.got_data() == false); + + // no data expected + sql << "select id from soci_test where id = 1000", into(i); + CHECK(sql.got_data() == false); + + // no data expected, test correct behaviour with use + int id = 1000; + sql << "select id from soci_test where id = :id", use(id), into(i); + CHECK(sql.got_data() == false); + } +} + +// repeated fetch and bulk fetch +TEST_CASE_METHOD(common_tests, "Repeated and bulk fetch", "[core][bulk]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + SECTION("char") + { + char c; + for (c = 'a'; c <= 'z'; ++c) + { + sql << "insert into soci_test(c) values(\'" << c << "\')"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == 'z' - 'a' + 1); + + { + char c2 = 'a'; + + statement st = (sql.prepare << + "select c from soci_test order by c", into(c)); + + st.execute(); + while (st.fetch()) + { + CHECK(c == c2); + ++c2; + } + CHECK(c2 == 'a' + count); + } + { + char c2 = 'a'; + + std::vector vec(10); + statement st = (sql.prepare << + "select c from soci_test order by c", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != vec.size(); ++i) + { + CHECK(c2 == vec[i]); + ++c2; + } + + vec.resize(10); + } + CHECK(c2 == 'a' + count); + } + + { + // verify an exception is thrown when empty vector is used + std::vector vec; + try + { + sql << "select c from soci_test", into(vec); + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "Vectors of size 0 are not allowed."); + } + } + + } + + // repeated fetch and bulk fetch of std::string + SECTION("std::string") + { + int const rowsToTest = 10; + for (int i = 0; i != rowsToTest; ++i) + { + std::ostringstream ss; + ss << "Hello_" << i; + + sql << "insert into soci_test(str) values(\'" + << ss.str() << "\')"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == rowsToTest); + + { + int i = 0; + std::string s; + statement st = (sql.prepare << + "select str from soci_test order by str", into(s)); + + st.execute(); + while (st.fetch()) + { + std::ostringstream ss; + ss << "Hello_" << i; + CHECK(s == ss.str()); + ++i; + } + CHECK(i == rowsToTest); + } + { + int i = 0; + + std::vector vec(4); + statement st = (sql.prepare << + "select str from soci_test order by str", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t j = 0; j != vec.size(); ++j) + { + std::ostringstream ss; + ss << "Hello_" << i; + CHECK(ss.str() == vec[j]); + ++i; + } + + vec.resize(4); + } + CHECK(i == rowsToTest); + } + } + + SECTION("short") + { + short const rowsToTest = 100; + short sh; + for (sh = 0; sh != rowsToTest; ++sh) + { + sql << "insert into soci_test(sh) values(" << sh << ")"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == rowsToTest); + + { + short sh2 = 0; + + statement st = (sql.prepare << + "select sh from soci_test order by sh", into(sh)); + + st.execute(); + while (st.fetch()) + { + CHECK(sh == sh2); + ++sh2; + } + CHECK(sh2 == rowsToTest); + } + { + short sh2 = 0; + + std::vector vec(8); + statement st = (sql.prepare << + "select sh from soci_test order by sh", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != vec.size(); ++i) + { + CHECK(sh2 == vec[i]); + ++sh2; + } + + vec.resize(8); + } + CHECK(sh2 == rowsToTest); + } + } + + SECTION("int") + { + int const rowsToTest = 100; + int i; + for (i = 0; i != rowsToTest; ++i) + { + sql << "insert into soci_test(id) values(" << i << ")"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == rowsToTest); + + { + int i2 = 0; + + statement st = (sql.prepare << + "select id from soci_test order by id", into(i)); + + st.execute(); + while (st.fetch()) + { + CHECK(i == i2); + ++i2; + } + CHECK(i2 == rowsToTest); + } + { + // additional test with the use element + + int i2 = 0; + int cond = 0; // this condition is always true + + statement st = (sql.prepare << + "select id from soci_test where id >= :cond order by id", + use(cond), into(i)); + + st.execute(); + while (st.fetch()) + { + CHECK(i == i2); + ++i2; + } + CHECK(i2 == rowsToTest); + } + { + int i2 = 0; + + std::vector vec(8); + statement st = (sql.prepare << + "select id from soci_test order by id", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t n = 0; n != vec.size(); ++n) + { + CHECK(i2 == vec[n]); + ++i2; + } + + vec.resize(8); + } + CHECK(i2 == rowsToTest); + } + } + + SECTION("unsigned int") + { + unsigned int const rowsToTest = 100; + unsigned int ul; + for (ul = 0; ul != rowsToTest; ++ul) + { + sql << "insert into soci_test(ul) values(" << ul << ")"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == static_cast(rowsToTest)); + + { + unsigned int ul2 = 0; + + statement st = (sql.prepare << + "select ul from soci_test order by ul", into(ul)); + + st.execute(); + while (st.fetch()) + { + CHECK(ul == ul2); + ++ul2; + } + CHECK(ul2 == rowsToTest); + } + { + unsigned int ul2 = 0; + + std::vector vec(8); + statement st = (sql.prepare << + "select ul from soci_test order by ul", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != vec.size(); ++i) + { + CHECK(ul2 == vec[i]); + ++ul2; + } + + vec.resize(8); + } + CHECK(ul2 == rowsToTest); + } + } + + SECTION("unsigned long long") + { + unsigned int const rowsToTest = 100; + unsigned long long ul; + for (ul = 0; ul != rowsToTest; ++ul) + { + sql << "insert into soci_test(ul) values(" << ul << ")"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == static_cast(rowsToTest)); + + { + unsigned long long ul2 = 0; + + statement st = (sql.prepare << + "select ul from soci_test order by ul", into(ul)); + + st.execute(); + while (st.fetch()) + { + CHECK(ul == ul2); + ++ul2; + } + CHECK(ul2 == rowsToTest); + } + { + unsigned long long ul2 = 0; + + std::vector vec(8); + statement st = (sql.prepare << + "select ul from soci_test order by ul", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != vec.size(); ++i) + { + CHECK(ul2 == vec[i]); + ++ul2; + } + + vec.resize(8); + } + CHECK(ul2 == rowsToTest); + } + } + + SECTION("double") + { + int const rowsToTest = 100; + double d = 0.0; + + statement sti = (sql.prepare << + "insert into soci_test(d) values(:d)", use(d)); + for (int i = 0; i != rowsToTest; ++i) + { + sti.execute(true); + d += 0.6; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == rowsToTest); + + { + double d2 = 0.0; + int i = 0; + + statement st = (sql.prepare << + "select d from soci_test order by d", into(d)); + + st.execute(); + while (st.fetch()) + { + ASSERT_EQUAL(d, d2); + d2 += 0.6; + ++i; + } + CHECK(i == rowsToTest); + } + { + double d2 = 0.0; + int i = 0; + + std::vector vec(8); + statement st = (sql.prepare << + "select d from soci_test order by d", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t j = 0; j != vec.size(); ++j) + { + ASSERT_EQUAL(d2, vec[j]); + d2 += 0.6; + ++i; + } + + vec.resize(8); + } + CHECK(i == rowsToTest); + } + } + + SECTION("std::tm") + { + int const rowsToTest = 8; + for (int i = 0; i != rowsToTest; ++i) + { + std::ostringstream ss; + ss << 2000 + i << "-0" << 1 + i << '-' << 20 - i << ' ' + << 15 + i << ':' << 50 - i << ':' << 40 + i; + + sql << "insert into soci_test(id, tm) values(" << i + << ", " << tc_.to_date_time(ss.str()) << ")"; + } + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == rowsToTest); + + { + std::tm t = std::tm(); + int i = 0; + + statement st = (sql.prepare << + "select tm from soci_test order by id", into(t)); + + st.execute(); + while (st.fetch()) + { + CHECK(t.tm_year == 2000 - 1900 + i); + CHECK(t.tm_mon == i); + CHECK(t.tm_mday == 20 - i); + CHECK(t.tm_hour == 15 + i); + CHECK(t.tm_min == 50 - i); + CHECK(t.tm_sec == 40 + i); + + ++i; + } + CHECK(i == rowsToTest); + } + { + int i = 0; + + std::vector vec(3); + statement st = (sql.prepare << + "select tm from soci_test order by id", into(vec)); + st.execute(); + while (st.fetch()) + { + for (std::size_t j = 0; j != vec.size(); ++j) + { + CHECK(vec[j].tm_year == 2000 - 1900 + i); + CHECK(vec[j].tm_mon == i); + CHECK(vec[j].tm_mday == 20 - i); + CHECK(vec[j].tm_hour == 15 + i); + CHECK(vec[j].tm_min == 50 - i); + CHECK(vec[j].tm_sec == 40 + i); + + ++i; + } + + vec.resize(3); + } + CHECK(i == rowsToTest); + } + } +} + +// test for indicators (repeated fetch and bulk) +TEST_CASE_METHOD(common_tests, "Indicators", "[core][indicator]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(id, val) values(1, 10)"; + sql << "insert into soci_test(id, val) values(2, 11)"; + sql << "insert into soci_test(id, val) values(3, NULL)"; + sql << "insert into soci_test(id, val) values(4, NULL)"; + sql << "insert into soci_test(id, val) values(5, 12)"; + + { + int val; + indicator ind; + + statement st = (sql.prepare << + "select val from soci_test order by id", into(val, ind)); + + st.execute(); + bool gotData = st.fetch(); + CHECK(gotData); + CHECK(ind == i_ok); + CHECK(val == 10); + gotData = st.fetch(); + CHECK(gotData); + CHECK(ind == i_ok); + CHECK(val == 11); + gotData = st.fetch(); + CHECK(gotData); + CHECK(ind == i_null); + gotData = st.fetch(); + CHECK(gotData); + CHECK(ind == i_null); + gotData = st.fetch(); + CHECK(gotData); + CHECK(ind == i_ok); + CHECK(val == 12); + gotData = st.fetch(); + CHECK(gotData == false); + } + { + std::vector vals(3); + std::vector inds(3); + + statement st = (sql.prepare << + "select val from soci_test order by id", into(vals, inds)); + + st.execute(); + bool gotData = st.fetch(); + CHECK(gotData); + CHECK(vals.size() == 3); + CHECK(inds.size() == 3); + CHECK(inds[0] == i_ok); + CHECK(vals[0] == 10); + CHECK(inds[1] == i_ok); + CHECK(vals[1] == 11); + CHECK(inds[2] == i_null); + gotData = st.fetch(); + CHECK(gotData); + CHECK(vals.size() == 2); + CHECK(inds[0] == i_null); + CHECK(inds[1] == i_ok); + CHECK(vals[1] == 12); + gotData = st.fetch(); + CHECK(gotData == false); + } + + // additional test for "no data" condition + { + std::vector vals(3); + std::vector inds(3); + + statement st = (sql.prepare << + "select val from soci_test where 0 = 1", into(vals, inds)); + + bool gotData = st.execute(true); + CHECK(gotData == false); + + // for convenience, vectors should be truncated + CHECK(vals.empty()); + CHECK(inds.empty()); + + // for even more convenience, fetch should not fail + // but just report end of rowset + // (and vectors should be truncated) + + vals.resize(1); + inds.resize(1); + + gotData = st.fetch(); + CHECK(gotData == false); + CHECK(vals.empty()); + CHECK(inds.empty()); + } + + // additional test for "no data" without prepared statement + { + std::vector vals(3); + std::vector inds(3); + + sql << "select val from soci_test where 0 = 1", + into(vals, inds); + + // vectors should be truncated + CHECK(vals.empty()); + CHECK(inds.empty()); + } + } + +} + +// test for different sizes of data vector and indicators vector +// (library should force ind. vector to have same size as data vector) +TEST_CASE_METHOD(common_tests, "Indicators vector", "[core][indicator][vector]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(id, str, val) values(1, 'ten', 10)"; + sql << "insert into soci_test(id, str, val) values(2, 'elf', 11)"; + sql << "insert into soci_test(id, str, val) values(3, NULL, NULL)"; + sql << "insert into soci_test(id, str, val) values(4, NULL, NULL)"; + sql << "insert into soci_test(id, str, val) values(5, 'xii', 12)"; + + { + std::vector vals(4); + std::vector inds; + + statement st = (sql.prepare << + "select val from soci_test order by id", into(vals, inds)); + + st.execute(); + st.fetch(); + CHECK(vals.size() == 4); + CHECK(inds.size() == 4); + vals.resize(3); + st.fetch(); + CHECK(vals.size() == 1); + CHECK(inds.size() == 1); + + std::vector strs(5); + sql << "select str from soci_test order by id", into(strs, inds); + REQUIRE(inds.size() == 5); + CHECK(inds[0] == i_ok); + CHECK(inds[1] == i_ok); + CHECK(inds[2] == i_null); + CHECK(inds[3] == i_null); + CHECK(inds[4] == i_ok); + + strs.resize(1); + sql << "select str from soci_test order by id", into(strs, inds); + CHECK(inds.size() == 1); + + strs.resize(1); + st = (sql.prepare << "select str from soci_test order by id", into(strs, inds)); + st.execute(); + st.fetch(); + CHECK(inds.size() == 1); + while (st.fetch()); + + std::vector ids(1); + sql << "select id from soci_test", into(ids); + CHECK(ids.size() == 1); + } + } + +} + +TEST_CASE_METHOD(common_tests, "Get last insert ID", "[core][get_last_insert_id]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_get_last_insert_id(sql)); + + // If the get_last_insert_id() supported by the backend. + if (!tableCreator.get()) + return; + + long long id; + REQUIRE(sql.get_last_insert_id("soci_test", id)); + // The initial value should be 1 and we call get_last_insert_id() before + // the first insert, so the "pre-initial value" is 0. + CHECK(id == 0); + + sql << "insert into soci_test(val) values(10)"; + + REQUIRE(sql.get_last_insert_id("soci_test", id)); + CHECK(id == 1); + + sql << "insert into soci_test(val) values(11)"; + + REQUIRE(sql.get_last_insert_id("soci_test", id)); + CHECK(id == 2); +} + +// "use" tests, type conversions, etc. +TEST_CASE_METHOD(common_tests, "Use type conversion", "[core][use]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + SECTION("char") + { + char c('a'); + sql << "insert into soci_test(c) values(:c)", use(c); + + c = 'b'; + sql << "select c from soci_test", into(c); + CHECK(c == 'a'); + + } + + SECTION("std::string") + { + std::string s = "Hello SOCI!"; + sql << "insert into soci_test(str) values(:s)", use(s); + + std::string str; + sql << "select str from soci_test", into(str); + + CHECK(str == "Hello SOCI!"); + } + + SECTION("int8_t") + { + int8_t i = 123; + sql << "insert into soci_test(id) values(:id)", use(i); + + int8_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == 123); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:id)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::max)(); + sql << "insert into soci_test(id) values(:id)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::max)()); + } + + SECTION("uint8_t") + { + uint8_t ui = 123; + sql << "insert into soci_test(id) values(:id)", use(ui); + + uint8_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 123); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:id)", use(ui); + + ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::max)(); + sql << "insert into soci_test(id) values(:id)", use(ui); + + ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::max)()); + } + + SECTION("short") + { + short s = 123; + sql << "insert into soci_test(id) values(:id)", use(s); + + short s2 = 0; + sql << "select id from soci_test", into(s2); + + CHECK(s2 == 123); + } + + SECTION("int16_t") + { + int16_t i = 123; + sql << "insert into soci_test(id) values(:id)", use(i); + + int16_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == 123); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:id)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::max)(); + sql << "insert into soci_test(id) values(:id)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::max)()); + } + + SECTION("uint16_t") + { + uint16_t ui = 123; + sql << "insert into soci_test(id) values(:id)", use(ui); + + uint16_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 123); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:id)", use(ui); + + ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::max)(); + sql << "insert into soci_test(id) values(:id)", use(ui); + + ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::max)()); + } + + SECTION("int") + { + int i = -12345678; + sql << "insert into soci_test(id) values(:i)", use(i); + + int i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == -12345678); + } + + SECTION("int32_t") + { + int32_t i = -12345678; + sql << "insert into soci_test(id) values(:i)", use(i); + + int32_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == -12345678); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:i)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::max)(); + sql << "insert into soci_test(id) values(:i)", use(i); + + i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::max)()); + } + + SECTION("uint32_t") + { + uint32_t ui = 12345678; + sql << "insert into soci_test(id) values(:i)", use(ui); + + uint32_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 12345678); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::min)(); + sql << "insert into soci_test(id) values(:i)", use(ui); + + ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::max)(); + sql << "insert into soci_test(ul) values(:i)", use(ui); + + ui2 = 0; + sql << "select ul from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::max)()); + } + + SECTION("unsigned long") + { + unsigned long ul = 4000000000ul; + sql << "insert into soci_test(ul) values(:num)", use(ul); + + unsigned long ul2 = 0; + sql << "select ul from soci_test", into(ul2); + + CHECK(ul2 == 4000000000ul); + } + + SECTION("int64_t") + { + int64_t i = 4000000000ll; + sql << "insert into soci_test(ll) values(:num)", use(i); + + int64_t i2 = 0; + sql << "select ll from soci_test", into(i2); + + CHECK(i2 == 4000000000ll); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::min)(); + sql << "insert into soci_test(ll) values(:num)", use(i); + + i2 = 0; + sql << "select ll from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + i = (std::numeric_limits::max)(); + sql << "insert into soci_test(ll) values(:num)", use(i); + + i2 = 0; + sql << "select ll from soci_test", into(i2); + + CHECK(i2 == (std::numeric_limits::max)()); + } + + SECTION("uint64_t") + { + uint64_t ui = 4000000000ull; + sql << "insert into soci_test(ul) values(:num)", use(ui); + + uint64_t ui2 = 0; + sql << "select ul from soci_test", into(ui2); + + CHECK(ui2 == 4000000000ull); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::min)(); + sql << "insert into soci_test(ul) values(:num)", use(ui); + + ui2 = 0; + sql << "select ul from soci_test", into(ui2); + + CHECK(ui2 == (std::numeric_limits::min)()); + + sql << "delete from soci_test"; + + ui = (std::numeric_limits::max)(); + sql << "insert into soci_test(ul) values(:num)", use(ui); + + ui2 = 0; + sql << "select ul from soci_test", into(ui2); + + if (tc_.truncates_uint64_to_int64()) + { + CHECK(ui2 == static_cast((std::numeric_limits::max)())); + } + else + { + CHECK(ui2 == (std::numeric_limits::max)()); + } + } + + SECTION("double") + { + double d = 3.14159265; + sql << "insert into soci_test(d) values(:d)", use(d); + + double d2 = 0; + sql << "select d from soci_test", into(d2); + + ASSERT_EQUAL(d2, d); + } + + SECTION("std::tm") + { + std::tm t = std::tm(); + t.tm_year = 105; + t.tm_mon = 10; + t.tm_mday = 19; + t.tm_hour = 21; + t.tm_min = 39; + t.tm_sec = 57; + sql << "insert into soci_test(tm) values(:t)", use(t); + + std::tm t2 = std::tm(); + t2.tm_year = 0; + t2.tm_mon = 0; + t2.tm_mday = 0; + t2.tm_hour = 0; + t2.tm_min = 0; + t2.tm_sec = 0; + + sql << "select tm from soci_test", into(t2); + + CHECK(t.tm_year == 105); + CHECK(t.tm_mon == 10); + CHECK(t.tm_mday == 19); + CHECK(t.tm_hour == 21); + CHECK(t.tm_min == 39); + CHECK(t.tm_sec == 57); + } + + SECTION("repeated use") + { + int i; + statement st = (sql.prepare + << "insert into soci_test(id) values(:id)", use(i)); + + i = 5; + st.execute(true); + i = 6; + st.execute(true); + i = 7; + st.execute(true); + + std::vector v(5); + sql << "select id from soci_test order by id", into(v); + + CHECK(v.size() == 3); + CHECK(v[0] == 5); + CHECK(v[1] == 6); + CHECK(v[2] == 7); + } + + // tests for use of const objects + + SECTION("const char") + { + char const c('a'); + sql << "insert into soci_test(c) values(:c)", use(c); + + char c2 = 'b'; + sql << "select c from soci_test", into(c2); + CHECK(c2 == 'a'); + + } + + SECTION("const std::string") + { + std::string const s = "Hello const SOCI!"; + sql << "insert into soci_test(str) values(:s)", use(s); + + std::string str; + sql << "select str from soci_test", into(str); + + CHECK(str == "Hello const SOCI!"); + } + + SECTION("const int8_t") + { + int8_t const i = 123; + sql << "insert into soci_test(id) values(:id)", use(i); + + int8_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == 123); + } + + SECTION("const uint8_t") + { + uint8_t const ui = 123; + sql << "insert into soci_test(id) values(:id)", use(ui); + + uint8_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 123); + } + + SECTION("const short") + { + short const s = 123; + sql << "insert into soci_test(id) values(:id)", use(s); + + short s2 = 0; + sql << "select id from soci_test", into(s2); + + CHECK(s2 == 123); + } + + SECTION("const int16_t") + { + int16_t const i = 123; + sql << "insert into soci_test(id) values(:id)", use(i); + + int16_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == 123); + } + + SECTION("const uint16_t") + { + uint16_t const ui = 123; + sql << "insert into soci_test(id) values(:id)", use(ui); + + uint16_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 123); + } + + SECTION("const int") + { + int const i = -12345678; + sql << "insert into soci_test(id) values(:i)", use(i); + + int i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == -12345678); + } + + SECTION("const int32_t") + { + int32_t const i = -12345678; + sql << "insert into soci_test(id) values(:i)", use(i); + + int32_t i2 = 0; + sql << "select id from soci_test", into(i2); + + CHECK(i2 == -12345678); + } + + SECTION("const uint32_t") + { + uint32_t const ui = 12345678; + sql << "insert into soci_test(id) values(:i)", use(ui); + + uint32_t ui2 = 0; + sql << "select id from soci_test", into(ui2); + + CHECK(ui2 == 12345678); + } + + SECTION("const unsigned long") + { + unsigned long const ul = 4000000000ul; + sql << "insert into soci_test(ul) values(:num)", use(ul); + + unsigned long ul2 = 0; + sql << "select ul from soci_test", into(ul2); + + CHECK(ul2 == 4000000000ul); + } + + SECTION("const int64_t") + { + int64_t const i = 4000000000ll; + sql << "insert into soci_test(ul) values(:num)", use(i); + + int64_t i2 = 0; + sql << "select ul from soci_test", into(i2); + + CHECK(i2 == 4000000000ll); + } + + SECTION("const uint64_t") + { + uint64_t const ui = 4000000000ull; + sql << "insert into soci_test(ul) values(:num)", use(ui); + + uint64_t ui2 = 0; + sql << "select ul from soci_test", into(ui2); + + CHECK(ui2 == 4000000000ull); + } + + SECTION("const double") + { + double const d = 3.14159265; + sql << "insert into soci_test(d) values(:d)", use(d); + + double d2 = 0; + sql << "select d from soci_test", into(d2); + + ASSERT_EQUAL(d2, d); + } + + SECTION("const std::tm") + { + std::tm t = std::tm(); + t.tm_year = 105; + t.tm_mon = 10; + t.tm_mday = 19; + t.tm_hour = 21; + t.tm_min = 39; + t.tm_sec = 57; + std::tm const & ct = t; + sql << "insert into soci_test(tm) values(:t)", use(ct); + + std::tm t2 = std::tm(); + t2.tm_year = 0; + t2.tm_mon = 0; + t2.tm_mday = 0; + t2.tm_hour = 0; + t2.tm_min = 0; + t2.tm_sec = 0; + + sql << "select tm from soci_test", into(t2); + + CHECK(t.tm_year == 105); + CHECK(t.tm_mon == 10); + CHECK(t.tm_mday == 19); + CHECK(t.tm_hour == 21); + CHECK(t.tm_min == 39); + CHECK(t.tm_sec == 57); + } +} + +// test for multiple use (and into) elements +TEST_CASE_METHOD(common_tests, "Multiple use and into", "[core][use][into]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + { + int i1 = 5; + int i2 = 6; + int i3 = 7; + + sql << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", + use(i1), use(i2), use(i3); + + i1 = 0; + i2 = 0; + i3 = 0; + sql << "select i1, i2, i3 from soci_test", + into(i1), into(i2), into(i3); + + CHECK(i1 == 5); + CHECK(i2 == 6); + CHECK(i3 == 7); + + // same for vectors + sql << "delete from soci_test"; + + i1 = 0; + i2 = 0; + i3 = 0; + + statement st = (sql.prepare + << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", + use(i1), use(i2), use(i3)); + + i1 = 1; + i2 = 2; + i3 = 3; + st.execute(true); + i1 = 4; + i2 = 5; + i3 = 6; + st.execute(true); + i1 = 7; + i2 = 8; + i3 = 9; + st.execute(true); + + std::vector v1(5); + std::vector v2(5); + std::vector v3(5); + + sql << "select i1, i2, i3 from soci_test order by i1", + into(v1), into(v2), into(v3); + + CHECK(v1.size() == 3); + CHECK(v2.size() == 3); + CHECK(v3.size() == 3); + CHECK(v1[0] == 1); + CHECK(v1[1] == 4); + CHECK(v1[2] == 7); + CHECK(v2[0] == 2); + CHECK(v2[1] == 5); + CHECK(v2[2] == 8); + CHECK(v3[0] == 3); + CHECK(v3[1] == 6); + CHECK(v3[2] == 9); + } +} + +// use vector elements +TEST_CASE_METHOD(common_tests, "Use vector", "[core][use][vector]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + SECTION("char") + { + std::vector v; + v.push_back('a'); + v.push_back('b'); + v.push_back('c'); + v.push_back('d'); + + sql << "insert into soci_test(c) values(:c)", use(v); + + std::vector v2(4); + + sql << "select c from soci_test order by c", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == 'a'); + CHECK(v2[1] == 'b'); + CHECK(v2[2] == 'c'); + CHECK(v2[3] == 'd'); + } + + SECTION("std::string") + { + std::vector v; + v.push_back("ala"); + v.push_back("ma"); + v.push_back("kota"); + + sql << "insert into soci_test(str) values(:s)", use(v); + + std::vector v2(4); + + sql << "select str from soci_test order by str", into(v2); + CHECK(v2.size() == 3); + CHECK(v2[0] == "ala"); + CHECK(v2[1] == "kota"); + CHECK(v2[2] == "ma"); + } + + SECTION("int8_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(-5); + v.push_back(123); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(sh) values(:sh)", use(v); + + std::vector v2(4); + + sql << "select sh from soci_test order by sh", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == -5); + CHECK(v2[2] == 123); + CHECK(v2[3] == (std::numeric_limits::max)()); + } + + SECTION("uint8_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(6); + v.push_back(123); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(sh) values(:sh)", use(v); + + std::vector v2(4); + + sql << "select sh from soci_test order by sh", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == 6); + CHECK(v2[2] == 123); + CHECK(v2[3] == (std::numeric_limits::max)()); + } + + SECTION("short") + { + std::vector v; + v.push_back(-5); + v.push_back(6); + v.push_back(7); + v.push_back(123); + + sql << "insert into soci_test(sh) values(:sh)", use(v); + + std::vector v2(4); + + sql << "select sh from soci_test order by sh", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == -5); + CHECK(v2[1] == 6); + CHECK(v2[2] == 7); + CHECK(v2[3] == 123); + } + + SECTION("int16_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(-5); + v.push_back(123); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(sh) values(:sh)", use(v); + + std::vector v2(4); + + sql << "select sh from soci_test order by sh", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == -5); + CHECK(v2[2] == 123); + CHECK(v2[3] == (std::numeric_limits::max)()); + } + + SECTION("uint16_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(6); + v.push_back(123); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(val) values(:val)", use(v); + + std::vector v2(4); + + sql << "select val from soci_test order by val", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == 6); + CHECK(v2[2] == 123); + CHECK(v2[3] == (std::numeric_limits::max)()); + } + + SECTION("int") + { + std::vector v; + v.push_back(-2000000000); + v.push_back(0); + v.push_back(1); + v.push_back(2000000000); + + sql << "insert into soci_test(id) values(:i)", use(v); + + std::vector v2(4); + + sql << "select id from soci_test order by id", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == -2000000000); + CHECK(v2[1] == 0); + CHECK(v2[2] == 1); + CHECK(v2[3] == 2000000000); + } + + SECTION("int32_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(-2000000000); + v.push_back(0); + v.push_back(1); + v.push_back(2000000000); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(id) values(:i)", use(v); + + std::vector v2(6); + + sql << "select id from soci_test order by id", into(v2); + CHECK(v2.size() == 6); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == -2000000000); + CHECK(v2[2] == 0); + CHECK(v2[3] == 1); + CHECK(v2[4] == 2000000000); + CHECK(v2[5] == (std::numeric_limits::max)()); + } + + SECTION("unsigned int") + { + std::vector v; + v.push_back(0); + v.push_back(1); + v.push_back(123); + v.push_back(1000); + + sql << "insert into soci_test(ul) values(:ul)", use(v); + + std::vector v2(4); + + sql << "select ul from soci_test order by ul", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == 0); + CHECK(v2[1] == 1); + CHECK(v2[2] == 123); + CHECK(v2[3] == 1000); + } + + SECTION("uint32_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(0); + v.push_back(1); + v.push_back(123); + v.push_back(1000); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(ul) values(:ul)", use(v); + + std::vector v2(6); + + sql << "select ul from soci_test order by ul", into(v2); + CHECK(v2.size() == 6); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == 0); + CHECK(v2[2] == 1); + CHECK(v2[3] == 123); + CHECK(v2[4] == 1000); + CHECK(v2[5] == (std::numeric_limits::max)()); + } + + SECTION("unsigned long long") + { + std::vector v; + v.push_back(0); + v.push_back(1); + v.push_back(123); + v.push_back(1000); + + sql << "insert into soci_test(ul) values(:ul)", use(v); + + std::vector v2(4); + + sql << "select ul from soci_test order by ul", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == 0); + CHECK(v2[1] == 1); + CHECK(v2[2] == 123); + CHECK(v2[3] == 1000); + } + + SECTION("int64_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(0); + v.push_back(1); + v.push_back(123); + v.push_back(1000); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(ll) values(:ll)", use(v); + + std::vector v2(6); + + sql << "select ll from soci_test order by ll", into(v2); + CHECK(v2.size() == 6); + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == 0); + CHECK(v2[2] == 1); + CHECK(v2[3] == 123); + CHECK(v2[4] == 1000); + CHECK(v2[5] == (std::numeric_limits::max)()); + } + + SECTION("uint64_t") + { + std::vector v; + v.push_back((std::numeric_limits::min)()); + v.push_back(0); + v.push_back(1); + v.push_back(123); + v.push_back(1000); + v.push_back((std::numeric_limits::max)()); + + sql << "insert into soci_test(ul) values(:ul)", use(v); + + std::vector v2(6); + + sql << "select ul from soci_test order by ul", into(v2); + CHECK(v2.size() == 6); + if (tc_.has_uint64_storage_bug()) + { + CHECK(v2[0] == (std::numeric_limits::max)()); + CHECK(v2[1] == (std::numeric_limits::min)()); + CHECK(v2[2] == 0); + CHECK(v2[3] == 1); + CHECK(v2[4] == 123); + CHECK(v2[5] == 1000); + } + else + { + CHECK(v2[0] == (std::numeric_limits::min)()); + CHECK(v2[1] == 0); + CHECK(v2[2] == 1); + CHECK(v2[3] == 123); + CHECK(v2[4] == 1000); + if (tc_.truncates_uint64_to_int64()) + { + CHECK(v2[5] == static_cast((std::numeric_limits::max)())); + } + else + { + CHECK(v2[5] == (std::numeric_limits::max)()); + } + } + } + + SECTION("double") + { + std::vector v; + v.push_back(0); + v.push_back(-0.0001); + v.push_back(0.0001); + v.push_back(3.1415926); + + sql << "insert into soci_test(d) values(:d)", use(v); + + std::vector v2(4); + + sql << "select d from soci_test order by d", into(v2); + CHECK(v2.size() == 4); + ASSERT_EQUAL(v2[0],-0.0001); + ASSERT_EQUAL(v2[1], 0); + ASSERT_EQUAL(v2[2], 0.0001); + ASSERT_EQUAL(v2[3], 3.1415926); + } + + SECTION("std::tm") + { + std::vector v; + std::tm t = std::tm(); + t.tm_year = 105; + t.tm_mon = 10; + t.tm_mday = 26; + t.tm_hour = 22; + t.tm_min = 45; + t.tm_sec = 17; + + v.push_back(t); + + t.tm_sec = 37; + v.push_back(t); + + t.tm_mday = 25; + v.push_back(t); + + sql << "insert into soci_test(tm) values(:t)", use(v); + + std::vector v2(4); + + sql << "select tm from soci_test order by tm", into(v2); + CHECK(v2.size() == 3); + CHECK(v2[0].tm_year == 105); + CHECK(v2[0].tm_mon == 10); + CHECK(v2[0].tm_mday == 25); + CHECK(v2[0].tm_hour == 22); + CHECK(v2[0].tm_min == 45); + CHECK(v2[0].tm_sec == 37); + CHECK(v2[1].tm_year == 105); + CHECK(v2[1].tm_mon == 10); + CHECK(v2[1].tm_mday == 26); + CHECK(v2[1].tm_hour == 22); + CHECK(v2[1].tm_min == 45); + CHECK(v2[1].tm_sec == 17); + CHECK(v2[2].tm_year == 105); + CHECK(v2[2].tm_mon == 10); + CHECK(v2[2].tm_mday == 26); + CHECK(v2[2].tm_hour == 22); + CHECK(v2[2].tm_min == 45); + CHECK(v2[2].tm_sec == 37); + } + + SECTION("const int") + { + std::vector v; + v.push_back(-2000000000); + v.push_back(0); + v.push_back(1); + v.push_back(2000000000); + + std::vector const & cv = v; + + sql << "insert into soci_test(id) values(:i)", use(cv); + + std::vector v2(4); + + sql << "select id from soci_test order by id", into(v2); + CHECK(v2.size() == 4); + CHECK(v2[0] == -2000000000); + CHECK(v2[1] == 0); + CHECK(v2[2] == 1); + CHECK(v2[3] == 2000000000); + } +} + +// use vector elements with type convertion +TEST_CASE_METHOD(common_tests, "Use vector of custom type objects", "[core][use][vector][type_conversion]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + // Unfortunately there is no portable way to indicate whether nulls should + // appear at the beginning or the end (SQL 2003 "NULLS LAST" is still not + // supported by MS SQL in 2021...), so use this column just to order by it. + std::vector i; + i.push_back(0); + i.push_back(1); + i.push_back(2); + + std::vector v; + v.push_back(MyOptionalString("string")); // A not empty valid string. + v.push_back(MyOptionalString()); // Invalid string mapped to null. + v.push_back(MyOptionalString("")); // An empty but still valid string. + + sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(v); + + SECTION("standard type") + { + std::vector values(3); + std::vector inds(3); + sql << "select str from soci_test order by id", into(values, inds); + + REQUIRE(values.size() == 3); + REQUIRE(inds.size() == 3); + + CHECK(inds[0] == soci::i_ok); + CHECK(values[0] == "string"); + + CHECK(inds[1] == soci::i_null); + + if ( !tc_.treats_empty_strings_as_null() ) + { + CHECK(inds[2] == soci::i_ok); + CHECK(values[2] == ""); + } + } + + SECTION("user type") + { + std::vector values(3); + std::vector inds(3); + sql << "select str from soci_test order by id", into(values, inds); + + REQUIRE(values.size() == 3); + REQUIRE(inds.size() == 3); + + CHECK(inds[0] == soci::i_ok); + CHECK(values[0].is_valid()); + CHECK(values[0].get() == "string"); + + CHECK(!values[1].is_valid()); + CHECK(inds[1] == soci::i_null); + + if ( !tc_.treats_empty_strings_as_null() ) + { + CHECK(inds[2] == soci::i_ok); + CHECK(values[2].is_valid()); + CHECK(values[2].get() == ""); + } + } +} + +TEST_CASE_METHOD(common_tests, "Into vector of custom type objects", "[core][into][vector][type_conversion]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + // Column used for sorting only, see above. + std::vector i; + i.push_back(0); + i.push_back(1); + i.push_back(2); + + std::vector values(3); + values[0] = "string"; + + std::vector inds; + inds.push_back(i_ok); + inds.push_back(i_null); + inds.push_back(i_ok); + + sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(values, inds); + + std::vector v2(4); + + sql << "select str from soci_test order by id", into(v2); + + INFO("Got back " << v2); + REQUIRE(v2.size() == 3); + + CHECK(v2[0].is_valid()); + CHECK(v2[0].get() == "string"); + + CHECK(!v2[1].is_valid()); + + if ( !tc_.treats_empty_strings_as_null() ) + { + CHECK(v2[2].is_valid()); + CHECK(v2[2].get().empty()); + } +} + +// test for named binding +TEST_CASE_METHOD(common_tests, "Named parameters", "[core][use][named-params]") +{ + soci::session sql(backEndFactory_, connectString_); + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + int i1 = 7; + int i2 = 8; + + // verify the exception is thrown if both by position + // and by name use elements are specified + try + { + sql << "insert into soci_test(i1, i2) values(:i1, :i2)", + use(i1, "i1"), use(i2); + + FAIL("expected exception not thrown"); + } + catch (soci_error const& e) + { + CHECK(e.get_error_message() == + "Binding for use elements must be either by position " + "or by name."); + } + + // normal test + sql << "insert into soci_test(i1, i2) values(:i1, :i2)", + use(i1, "i1"), use(i2, "i2"); + + i1 = 0; + i2 = 0; + sql << "select i1, i2 from soci_test", into(i1), into(i2); + CHECK(i1 == 7); + CHECK(i2 == 8); + + i2 = 0; + sql << "select i2 from soci_test where i1 = :i1", into(i2), use(i1); + CHECK(i2 == 8); + + sql << "delete from soci_test"; + + // test vectors + + std::vector v1; + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + + std::vector v2; + v2.push_back(4); + v2.push_back(5); + v2.push_back(6); + + sql << "insert into soci_test(i1, i2) values(:i1, :i2)", + use(v1, "i1"), use(v2, "i2"); + + sql << "select i2, i1 from soci_test order by i1 desc", + into(v1), into(v2); + CHECK(v1.size() == 3); + CHECK(v2.size() == 3); + CHECK(v1[0] == 6); + CHECK(v1[1] == 5); + CHECK(v1[2] == 4); + CHECK(v2[0] == 3); + CHECK(v2[1] == 2); + CHECK(v2[2] == 1); + } +} + +TEST_CASE_METHOD(common_tests, "Named parameters with similar names", "[core][use][named-params]") +{ + // Verify parsing of parameters with similar names, + // where one name is part of the other, etc. + // https://github.com/SOCI/soci/issues/26 + + soci::session sql(backEndFactory_, connectString_); + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + std::string passwd("abc"); + std::string passwd_clear("clear"); + + SECTION("unnamed") + { + sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", + soci::use(passwd), soci::use(passwd_clear); + } + + SECTION("same order") + { + sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", + soci::use(passwd_clear, "passwd_clear"), soci::use(passwd, "passwd"); + } + + SECTION("reversed order") + { + sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", + soci::use(passwd, "passwd"), soci::use(passwd_clear, "passwd_clear"); + } + + // TODO: Allow binding the same varibale multiple times + // SECTION("one for multiple placeholders") + // { + // sql << "INSERT INTO soci_test(str,name) VALUES(:passwd, :passwd)", + // soci::use(passwd, "passwd"); + // } + } +} + +// transaction test +TEST_CASE_METHOD(common_tests, "Transactions", "[core][transaction]") +{ + soci::session sql(backEndFactory_, connectString_); + + if (!tc_.has_transactions_support(sql)) + { + WARN("Transactions not supported by the database, skipping the test."); + return; + } + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + int count; + sql << "select count(*) from soci_test", into(count); + CHECK(count == 0); + + { + transaction tr(sql); + + sql << "insert into soci_test (id, name) values(1, 'John')"; + sql << "insert into soci_test (id, name) values(2, 'Anna')"; + sql << "insert into soci_test (id, name) values(3, 'Mike')"; + + tr.commit(); + } + { + transaction tr(sql); + + sql << "select count(*) from soci_test", into(count); + CHECK(count == 3); + + sql << "insert into soci_test (id, name) values(4, 'Stan')"; + + sql << "select count(*) from soci_test", into(count); + CHECK(count == 4); + + tr.rollback(); + + sql << "select count(*) from soci_test", into(count); + CHECK(count == 3); + } + { + transaction tr(sql); + + sql << "delete from soci_test"; + + sql << "select count(*) from soci_test", into(count); + CHECK(count == 0); + + tr.rollback(); + + sql << "select count(*) from soci_test", into(count); + CHECK(count == 3); + } + { + // additional test for detection of double commit + transaction tr(sql); + tr.commit(); + try + { + tr.commit(); + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "The transaction object cannot be handled twice."); + } + } +} + +std::tm generate_tm() +{ + std::tm t = std::tm(); + t.tm_year = 105; + t.tm_mon = 10; + t.tm_mday = 15; + t.tm_hour = 22; + t.tm_min = 14; + t.tm_sec = 17; + return t; +} + +// test of use elements with indicators +TEST_CASE_METHOD(common_tests, "Use with indicators", "[core][use][indicator]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + indicator ind1 = i_ok; + indicator ind2 = i_ok; + indicator ind3 = i_ok; + + int id = 1; + int val = 10; + std::tm tm_gen = generate_tm(); + char const* insert = "insert into soci_test(id, val, tm) values(:id, :val, :tm)"; + sql << insert, use(id, ind1), use(val, ind2), use(tm_gen, ind3); + + id = 2; + val = 11; + ind2 = i_null; + std::tm tm = std::tm(); + ind3 = i_null; + + sql << "insert into soci_test(id, val, tm) values(:id, :val, :tm)", + use(id, ind1), use(val, ind2), use(tm, ind3); + + sql << "select val from soci_test where id = 1", into(val, ind2); + CHECK(ind2 == i_ok); + CHECK(val == 10); + sql << "select val, tm from soci_test where id = 2", into(val, ind2), into(tm, ind3); + CHECK(ind2 == i_null); + CHECK(ind3 == i_null); + + std::vector ids; + ids.push_back(3); + ids.push_back(4); + ids.push_back(5); + std::vector vals; + vals.push_back(12); + vals.push_back(13); + vals.push_back(14); + std::vector inds; + inds.push_back(i_ok); + inds.push_back(i_null); + inds.push_back(i_ok); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(ids), use(vals, inds); + + ids.resize(5); + vals.resize(5); + sql << "select id, val from soci_test order by id desc", + into(ids), into(vals, inds); + + CHECK(ids.size() == 5); + CHECK(ids[0] == 5); + CHECK(ids[1] == 4); + CHECK(ids[2] == 3); + CHECK(ids[3] == 2); + CHECK(ids[4] == 1); + CHECK(inds.size() == 5); + CHECK(inds[0] == i_ok); + CHECK(inds[1] == i_null); + CHECK(inds[2] == i_ok); + CHECK(inds[3] == i_null); + CHECK(inds[4] == i_ok); + CHECK(vals.size() == 5); + CHECK(vals[0] == 14); + CHECK(vals[2] == 12); + CHECK(vals[4] == 10); +} + +// Dynamic binding to Row objects +TEST_CASE_METHOD(common_tests, "Dynamic row binding", "[core][dynamic]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + + auto_table_creator tableCreator(tc_.table_creator_2(sql)); + + row r; + sql << "select * from soci_test", into(r); + CHECK(sql.got_data() == false); + + sql << "insert into soci_test" + " values(3.14, 123, \'Johny\'," + << tc_.to_date_time("2005-12-19 22:14:17") + << ", 'a')"; + + // select into a row + { + statement st = (sql.prepare << + "select * from soci_test", into(r)); + st.execute(true); + CHECK(r.size() == 5); + + CHECK(r.get_properties(0).get_data_type() == dt_double); + CHECK(r.get_properties(0).get_db_type() == db_double); + CHECK(r.get_properties(1).get_data_type() == dt_integer); + CHECK(r.get_properties(1).get_db_type() == db_int32); + CHECK(r.get_properties(2).get_data_type() == dt_string); + CHECK(r.get_properties(2).get_db_type() == db_string); + CHECK(r.get_properties(3).get_data_type() == dt_date); + CHECK(r.get_properties(3).get_db_type() == db_date); + + // type char is visible as string + // - to comply with the implementation for Oracle + CHECK(r.get_properties(4).get_data_type() == dt_string); + CHECK(r.get_properties(4).get_db_type() == db_string); + + CHECK(r.get_properties("NUM_INT").get_data_type() == dt_integer); + CHECK(r.get_properties("NUM_INT").get_db_type() == db_int32); + + CHECK(r.get_properties(0).get_name() == "NUM_FLOAT"); + CHECK(r.get_properties(1).get_name() == "NUM_INT"); + CHECK(r.get_properties(2).get_name() == "NAME"); + CHECK(r.get_properties(3).get_name() == "SOMETIME"); + CHECK(r.get_properties(4).get_name() == "CHR"); + + ASSERT_EQUAL_APPROX(r.get(0), 3.14); + CHECK(r.get(1) == 123); + CHECK(r.get(2) == "Johny"); + CHECK(r.get(3).tm_year == 105); + + // again, type char is visible as string + CHECK_EQUAL_PADDED(r.get(4), "a"); + + ASSERT_EQUAL_APPROX(r.get("NUM_FLOAT"), 3.14); + CHECK(r.get("NUM_INT") == 123); + CHECK(r.get("NAME") == "Johny"); + CHECK_EQUAL_PADDED(r.get("CHR"), "a"); + + CHECK(r.get_indicator(0) == i_ok); + + // verify exception thrown on invalid get<> + bool caught = false; + try + { + r.get(0); + } + catch (std::bad_cast const &) + { + caught = true; + } + CHECK(caught); + + // additional test for stream-like extraction + { + double d; + int i; + std::string s; + std::tm t = std::tm(); + std::string c; + + r >> d >> i >> s >> t >> c; + + ASSERT_EQUAL_APPROX(d, 3.14); + CHECK(i == 123); + CHECK(s == "Johny"); + CHECK(t.tm_year == 105); + CHECK(t.tm_mon == 11); + CHECK(t.tm_mday == 19); + CHECK(t.tm_hour == 22); + CHECK(t.tm_min == 14); + CHECK(t.tm_sec == 17); + CHECK_EQUAL_PADDED(c, "a"); + } + } + + // additional test to check if the row object can be + // reused between queries + { + sql << "select * from soci_test", into(r); + + CHECK(r.size() == 5); + + CHECK(r.get_properties(0).get_data_type() == dt_double); + CHECK(r.get_properties(0).get_db_type() == db_double); + CHECK(r.get_properties(1).get_data_type() == dt_integer); + CHECK(r.get_properties(1).get_db_type() == db_int32); + CHECK(r.get_properties(2).get_data_type() == dt_string); + CHECK(r.get_properties(2).get_db_type() == db_string); + CHECK(r.get_properties(3).get_data_type() == dt_date); + CHECK(r.get_properties(3).get_db_type() == db_date); + + sql << "select name, num_int from soci_test", into(r); + + CHECK(r.size() == 2); + + CHECK(r.get_properties(0).get_data_type() == dt_string); + CHECK(r.get_properties(0).get_db_type() == db_string); + CHECK(r.get_properties(1).get_data_type() == dt_integer); + CHECK(r.get_properties(1).get_db_type() == db_int32); + + // Check if row object is movable + row moved = std::move(r); + + CHECK(moved.size() == 2); + // We expect the moved-from row to become empty after the move operation + CHECK(r.size() == 0); + + CHECK(moved.get_properties(0).get_data_type() == dt_string); + CHECK(moved.get_properties(0).get_db_type() == db_string); + CHECK(moved.get_properties(1).get_data_type() == dt_integer); + CHECK(moved.get_properties(1).get_db_type() == db_int32); + } +} + +// more dynamic bindings +TEST_CASE_METHOD(common_tests, "Dynamic row binding 2", "[core][dynamic]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(id, val) values(1, 10)"; + sql << "insert into soci_test(id, val) values(2, 20)"; + sql << "insert into soci_test(id, val) values(3, 30)"; + + { + int id = 2; + row r; + sql << "select val from soci_test where id = :id", use(id), into(r); + + CHECK(r.size() == 1); + CHECK(r.get_properties(0).get_data_type() == dt_integer); + CHECK(r.get_properties(0).get_db_type() == db_int32); + CHECK(r.get(0) == 20); + CHECK(r.get(0) == 20); + } + { + int id; + row r; + statement st = (sql.prepare << + "select val from soci_test where id = :id", use(id), into(r)); + + id = 2; + st.execute(true); + CHECK(r.size() == 1); + CHECK(r.get_properties(0).get_data_type() == dt_integer); + CHECK(r.get_properties(0).get_db_type() == db_int32); + CHECK(r.get(0) == 20); + CHECK(r.get(0) == 20); + + id = 3; + st.execute(true); + CHECK(r.size() == 1); + CHECK(r.get_properties(0).get_data_type() == dt_integer); + CHECK(r.get_properties(0).get_db_type() == db_int32); + CHECK(r.get(0) == 30); + CHECK(r.get(0) == 30); + + id = 1; + st.execute(true); + CHECK(r.size() == 1); + CHECK(r.get_properties(0).get_data_type() == dt_integer); + CHECK(r.get_properties(0).get_db_type() == db_int32); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + } +} + +// More Dynamic binding to row objects +TEST_CASE_METHOD(common_tests, "Dynamic row binding 3", "[core][dynamic]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + + auto_table_creator tableCreator(tc_.table_creator_3(sql)); + + row r1; + sql << "select * from soci_test", into(r1); + CHECK(sql.got_data() == false); + + sql << "insert into soci_test values('david', '(404)123-4567')"; + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + row r2; + statement st = (sql.prepare << "select * from soci_test", into(r2)); + st.execute(); + + CHECK(r2.size() == 2); + + int count = 0; + while (st.fetch()) + { + ++count; + CHECK(r2.get("PHONE") == "(404)123-4567"); + } + CHECK(count == 3); +} + +// This is like the previous test but with a type_conversion instead of a row +TEST_CASE_METHOD(common_tests, "Dynamic binding with type conversions", "[core][dynamic][type_conversion]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + + SECTION("simple conversions") + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + SECTION("between single basic type and user type") + { + MyInt mi; + mi.set(123); + sql << "insert into soci_test(id) values(:id)", use(mi); + + int i; + sql << "select id from soci_test", into(i); + CHECK(i == 123); + + sql << "update soci_test set id = id + 1"; + + sql << "select id from soci_test", into(mi); + CHECK(mi.get() == 124); + } + + SECTION("with use const") + { + MyInt mi; + mi.set(123); + + MyInt const & cmi = mi; + sql << "insert into soci_test(id) values(:id)", use(cmi); + + int i; + sql << "select id from soci_test", into(i); + CHECK(i == 123); + } + } + + SECTION("ORM conversions") + { + auto_table_creator tableCreator(tc_.table_creator_3(sql)); + + SECTION("conversions based on values") + { + PhonebookEntry p1; + sql << "select * from soci_test", into(p1); + CHECK(p1.name == ""); + CHECK(p1.phone == ""); + + p1.name = "david"; + + // Note: uppercase column names are used here (and later on) + // for consistency with how they can be read from database + // (which means forced to uppercase on Oracle) and how they are + // set/get in the type conversion routines for PhonebookEntry. + // In short, IF the database is Oracle, + // then all column names for binding should be uppercase. + sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + PhonebookEntry p2; + statement st = (sql.prepare << "select * from soci_test", into(p2)); + st.execute(); + + int count = 0; + while (st.fetch()) + { + ++count; + if (p2.name == "david") + { + // see type_conversion + CHECK(p2.phone ==""); + } + else + { + CHECK(p2.phone == "(404)123-4567"); + } + } + CHECK(count == 3); + } + + SECTION("conversions based on values with use const") + { + PhonebookEntry p1; + p1.name = "Joe Coder"; + p1.phone = "123-456"; + + PhonebookEntry const & cp1 = p1; + + sql << "insert into soci_test values(:NAME, :PHONE)", use(cp1); + + PhonebookEntry p2; + sql << "select * from soci_test", into(p2); + CHECK(sql.got_data()); + + CHECK(p2.name == "Joe Coder"); + CHECK(p2.phone == "123-456"); + } + + SECTION("conversions based on accessor functions (as opposed to direct variable bindings)") + { + PhonebookEntry3 p1; + p1.setName("Joe Hacker"); + p1.setPhone("10010110"); + + sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); + + PhonebookEntry3 p2; + sql << "select * from soci_test", into(p2); + CHECK(sql.got_data()); + + CHECK(p2.getName() == "Joe Hacker"); + CHECK(p2.getPhone() == "10010110"); + } + + SECTION("PhonebookEntry2 type conversion to test calls to values::get_indicator()") + { + PhonebookEntry2 p1; + sql << "select * from soci_test", into(p1); + CHECK(p1.name == ""); + CHECK(p1.phone == ""); + p1.name = "david"; + + sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + PhonebookEntry2 p2; + statement st = (sql.prepare << "select * from soci_test", into(p2)); + st.execute(); + + int count = 0; + while (st.fetch()) + { + ++count; + if (p2.name == "david") + { + // see type_conversion + CHECK(p2.phone ==""); + } + else + { + CHECK(p2.phone == "(404)123-4567"); + } + } + CHECK(count == 3); + } + } +} + +// Dynamic bindings with type casts +TEST_CASE_METHOD(common_tests, "Dynamic row binding 4", "[core][dynamic]") +{ + soci::session sql(backEndFactory_, connectString_); + + SECTION("simple type cast") + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(id, d, str, tm)" + << " values(10, 20.0, 'foobar'," + << tc_.to_date_time("2005-12-19 22:14:17") + << ")"; + + { + row r; + sql << "select id from soci_test", into(r); + + CHECK(r.size() == 1); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK(r.get(0) == 10); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + } + { + row r; + sql << "select d from soci_test", into(r); + + CHECK(r.size() == 1); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + ASSERT_EQUAL_APPROX(r.get(0), 20.0); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + } + { + row r; + sql << "select str from soci_test", into(r); + + CHECK(r.size() == 1); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK(r.get(0) == "foobar"); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + } + { + row r; + sql << "select tm from soci_test", into(r); + + CHECK(r.size() == 1); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK(r.get(0).tm_year == 105); + CHECK(r.get(0).tm_mon == 11); + CHECK(r.get(0).tm_mday == 19); + CHECK(r.get(0).tm_hour == 22); + CHECK(r.get(0).tm_min == 14); + CHECK(r.get(0).tm_sec == 17); + } + } + SECTION("overflowing type cast") + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(id)" + << " values(" + << (std::numeric_limits::max)() + << ")"; + + row r; + sql << "select id from soci_test", into(r); + + intmax_t v = (intmax_t)(std::numeric_limits::max)(); + uintmax_t uv = (uintmax_t)(std::numeric_limits::max)(); + + CHECK(r.size() == 1); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK(r.get(0) == v); + CHECK(r.get(0) == v); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK_THROWS_AS(r.get(0), std::bad_cast); + CHECK(r.get(0) == uv); + CHECK(r.get(0) == uv); + } +} + +TEST_CASE_METHOD(common_tests, "Prepared insert with ORM", "[core][orm]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + auto_table_creator tableCreator(tc_.table_creator_3(sql)); + + PhonebookEntry temp; + PhonebookEntry e1 = { "name1", "phone1" }; + PhonebookEntry e2 = { "name2", "phone2" }; + + //sql << "insert into soci_test values (:NAME, :PHONE)", use(temp); + statement insertStatement = (sql.prepare << "insert into soci_test values (:NAME, :PHONE)", use(temp)); + + temp = e1; + insertStatement.execute(true); + temp = e2; + insertStatement.execute(true); + + int count = 0; + + sql << "select count(*) from soci_test where NAME in ('name1', 'name2')", into(count); + + CHECK(count == 2); +} + +TEST_CASE_METHOD(common_tests, "Partial match with ORM", "[core][orm]") +{ + soci::session sql(backEndFactory_, connectString_); + sql.uppercase_column_names(true); + auto_table_creator tableCreator(tc_.table_creator_3(sql)); + + PhonebookEntry in = { "name1", "phone1" }; + std::string name = "nameA"; + sql << "insert into soci_test values (:NAMED, :PHONE)", use(in), use(name, "NAMED"); + + PhonebookEntry out; + sql << "select * from soci_test where PHONE = 'phone1'", into(out); + CHECK(out.name == "nameA"); + CHECK(out.phone == "phone1"); +} + +TEST_CASE_METHOD(common_tests, "Numeric round trip", "[core][float]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + double d1 = 0.003958, + d2; + + sql << "insert into soci_test(num76) values (:d1)", use(d1); + sql << "select num76 from soci_test", into(d2); + + // The numeric value should make the round trip unchanged, we really want + // to use exact comparisons here. + ASSERT_EQUAL_EXACT(d1, d2); + + // test negative doubles too + sql << "delete from soci_test"; + d1 = -d1; + + sql << "insert into soci_test(num76) values (:d1)", use(d1); + sql << "select num76 from soci_test", into(d2); + + ASSERT_EQUAL_EXACT(d1, d2); +} + +// test for bulk fetch with single use +TEST_CASE_METHOD(common_tests, "Bulk fetch with single use", "[core][bulk]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(name, id) values('john', 1)"; + sql << "insert into soci_test(name, id) values('george', 2)"; + sql << "insert into soci_test(name, id) values('anthony', 1)"; + sql << "insert into soci_test(name, id) values('marc', 3)"; + sql << "insert into soci_test(name, id) values('julian', 1)"; + + int code = 1; + std::vector names(10); + sql << "select name from soci_test where id = :id order by name", + into(names), use(code); + + CHECK(names.size() == 3); + CHECK(names[0] == "anthony"); + CHECK(names[1] == "john"); + CHECK(names[2] == "julian"); +} + +// test for basic logging support +TEST_CASE_METHOD(common_tests, "Basic logging support", "[core][logging]") +{ + soci::session sql(backEndFactory_, connectString_); + + std::ostringstream log; + sql.set_log_stream(&log); + + try + { + sql << "drop table soci_test1"; + } + catch (...) {} + + CHECK(sql.get_last_query() == "drop table soci_test1"); + + sql.set_log_stream(NULL); + + try + { + sql << "drop table soci_test2"; + } + catch (...) {} + + CHECK(sql.get_last_query() == "drop table soci_test2"); + + sql.set_log_stream(&log); + + try + { + sql << "drop table soci_test3"; + } + catch (...) {} + + CHECK(sql.get_last_query() == "drop table soci_test3"); + CHECK(log.str() == + "drop table soci_test1\n" + "drop table soci_test3\n"); + +} + +// test for rowset creation and copying +TEST_CASE_METHOD(common_tests, "Rowset creation and copying", "[core][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + // Create empty rowset + rowset rs1; + CHECK(rs1.begin() == rs1.end()); + } + { + // Load empty rowset + rowset rs1 = (sql.prepare << "select * from soci_test"); + CHECK(rs1.begin() == rs1.end()); + } + + { + // Copy construction + rowset rs1 = (sql.prepare << "select * from soci_test"); + rowset rs2(rs1); + rowset rs3(rs1); + rowset rs4(rs3); + + CHECK(rs1.begin() == rs2.begin()); + CHECK(rs1.begin() == rs3.begin()); + CHECK(rs1.end() == rs2.end()); + CHECK(rs1.end() == rs3.end()); + } + + if (!tc_.has_multiple_select_bug()) + { + // Assignment + rowset rs1; + rowset rs2 = (sql.prepare << "select * from soci_test"); + rowset rs3 = (sql.prepare << "select * from soci_test"); + rs1 = rs2; + rs3 = rs2; + + CHECK(rs1.begin() == rs2.begin()); + CHECK(rs1.begin() == rs3.begin()); + CHECK(rs1.end() == rs2.end()); + CHECK(rs1.end() == rs3.end()); + } +} + +// test for simple iterating using rowset iterator (without reading data) +TEST_CASE_METHOD(common_tests, "Rowset iteration", "[core][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(id, val) values(1, 10)"; + sql << "insert into soci_test(id, val) values(2, 11)"; + sql << "insert into soci_test(id, val) values(3, NULL)"; + sql << "insert into soci_test(id, val) values(4, NULL)"; + sql << "insert into soci_test(id, val) values(5, 12)"; + { + rowset rs = (sql.prepare << "select * from soci_test"); + + CHECK(5 == std::distance(rs.begin(), rs.end())); + } + { + rowset rs = (sql.prepare << "select * from soci_test"); + + rs.clear(); + CHECK(rs.begin() == rs.end()); + } + } + +} + +// test for reading rowset using iterator +TEST_CASE_METHOD(common_tests, "Reading rows from rowset", "[core][row][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_2(sql)); + { + { + // Empty rowset + rowset rs = (sql.prepare << "select * from soci_test"); + CHECK(0 == std::distance(rs.begin(), rs.end())); + } + + { + // Non-empty rowset + sql << "insert into soci_test values(3.14, 123, \'Johny\'," + << tc_.to_date_time("2005-12-19 22:14:17") + << ", 'a')"; + sql << "insert into soci_test values(6.28, 246, \'Robert\'," + << tc_.to_date_time("2004-10-01 18:44:10") + << ", 'b')"; + + rowset rs = (sql.prepare << "select * from soci_test"); + + rowset::const_iterator it = rs.begin(); + CHECK(it != rs.end()); + + // + // First row + // + row const & r1 = (*it); + + // Properties + CHECK(r1.size() == 5); + CHECK(r1.get_properties(0).get_data_type() == dt_double); + CHECK(r1.get_properties(0).get_db_type() == db_double); + CHECK(r1.get_properties(1).get_data_type() == dt_integer); + CHECK(r1.get_properties(1).get_db_type() == db_int32); + CHECK(r1.get_properties(2).get_data_type() == dt_string); + CHECK(r1.get_properties(2).get_db_type() == db_string); + CHECK(r1.get_properties(3).get_data_type() == dt_date); + CHECK(r1.get_properties(3).get_db_type() == db_date); + CHECK(r1.get_properties(4).get_data_type() == dt_string); + CHECK(r1.get_properties(4).get_db_type() == db_string); + CHECK(r1.get_properties("NUM_INT").get_data_type() == dt_integer); + CHECK(r1.get_properties("NUM_INT").get_db_type() == db_int32); + + // Data + + // Since we didn't specify order by in the above query, + // the 2 rows may be returned in either order + // (If we specify order by, we can't do it in a cross db + // compatible way, because the Oracle table for this has been + // created with lower case column names) + + std::string name = r1.get(2); + + if (name == "Johny") + { + ASSERT_EQUAL_APPROX(r1.get(0), 3.14); + CHECK(r1.get(1) == 123); + CHECK(r1.get(2) == "Johny"); + std::tm t1 = std::tm(); + t1 = r1.get(3); + CHECK(t1.tm_year == 105); + CHECK_EQUAL_PADDED(r1.get(4), "a"); + ASSERT_EQUAL_APPROX(r1.get("NUM_FLOAT"), 3.14); + CHECK(r1.get("NUM_INT") == 123); + CHECK(r1.get("NAME") == "Johny"); + CHECK_EQUAL_PADDED(r1.get("CHR"), "a"); + } + else if (name == "Robert") + { + ASSERT_EQUAL(r1.get(0), 6.28); + CHECK(r1.get(1) == 246); + CHECK(r1.get(2) == "Robert"); + std::tm t1 = r1.get(3); + CHECK(t1.tm_year == 104); + CHECK(r1.get(4) == "b"); + ASSERT_EQUAL(r1.get("NUM_FLOAT"), 6.28); + CHECK(r1.get("NUM_INT") == 246); + CHECK(r1.get("NAME") == "Robert"); + CHECK_EQUAL_PADDED(r1.get("CHR"), "b"); + } + else + { + CAPTURE(name); + FAIL("expected \"Johny\" or \"Robert\""); + } + + // + // Iterate to second row + // + ++it; + CHECK(it != rs.end()); + + // + // Second row + // + row const & r2 = (*it); + + // Properties + CHECK(r2.size() == 5); + CHECK(r2.get_properties(0).get_data_type() == dt_double); + CHECK(r2.get_properties(0).get_db_type() == db_double); + CHECK(r2.get_properties(1).get_data_type() == dt_integer); + CHECK(r2.get_properties(1).get_db_type() == db_int32); + CHECK(r2.get_properties(2).get_data_type() == dt_string); + CHECK(r2.get_properties(2).get_db_type() == db_string); + CHECK(r2.get_properties(3).get_data_type() == dt_date); + CHECK(r2.get_properties(3).get_db_type() == db_date); + CHECK(r2.get_properties(4).get_data_type() == dt_string); + CHECK(r2.get_properties(4).get_db_type() == db_string); + CHECK(r2.get_properties("NUM_INT").get_data_type() == dt_integer); + CHECK(r2.get_properties("NUM_INT").get_db_type() == db_int32); + + std::string newName = r2.get(2); + CHECK(name != newName); + + if (newName == "Johny") + { + ASSERT_EQUAL_APPROX(r2.get(0), 3.14); + CHECK(r2.get(1) == 123); + CHECK(r2.get(2) == "Johny"); + std::tm t2 = r2.get(3); + CHECK(t2.tm_year == 105); + CHECK(r2.get(4) == "a"); + ASSERT_EQUAL_APPROX(r2.get("NUM_FLOAT"), 3.14); + CHECK(r2.get("NUM_INT") == 123); + CHECK(r2.get("NAME") == "Johny"); + CHECK(r2.get("CHR") == "a"); + } + else if (newName == "Robert") + { + ASSERT_EQUAL_APPROX(r2.get(0), 6.28); + CHECK(r2.get(1) == 246); + CHECK(r2.get(2) == "Robert"); + std::tm t2 = r2.get(3); + CHECK(t2.tm_year == 104); + CHECK_EQUAL_PADDED(r2.get(4), "b"); + ASSERT_EQUAL_APPROX(r2.get("NUM_FLOAT"), 6.28); + CHECK(r2.get("NUM_INT") == 246); + CHECK(r2.get("NAME") == "Robert"); + CHECK_EQUAL_PADDED(r2.get("CHR"), "b"); + } + else + { + CAPTURE(newName); + FAIL("expected \"Johny\" or \"Robert\""); + } + } + + { + // Non-empty rowset with NULL values + sql << "insert into soci_test " + << "(num_int, num_float , name, sometime, chr) " + << "values (0, NULL, NULL, NULL, NULL)"; + + rowset rs = (sql.prepare + << "select num_int, num_float, name, sometime, chr " + << "from soci_test where num_int = 0"); + + rowset::const_iterator it = rs.begin(); + CHECK(it != rs.end()); + + // + // First row + // + row const& r1 = (*it); + + // Properties + CHECK(r1.size() == 5); + CHECK(r1.get_properties(0).get_data_type() == dt_integer); + CHECK(r1.get_properties(0).get_db_type() == db_int32); + CHECK(r1.get_properties(1).get_data_type() == dt_double); + CHECK(r1.get_properties(1).get_db_type() == db_double); + CHECK(r1.get_properties(2).get_data_type() == dt_string); + CHECK(r1.get_properties(2).get_db_type() == db_string); + CHECK(r1.get_properties(3).get_data_type() == dt_date); + CHECK(r1.get_properties(3).get_db_type() == db_date); + CHECK(r1.get_properties(4).get_data_type() == dt_string); + CHECK(r1.get_properties(4).get_db_type() == db_string); + + // Data + CHECK(r1.get_indicator(0) == soci::i_ok); + CHECK(r1.get(0) == 0); + CHECK(r1.get_indicator(1) == soci::i_null); + CHECK(r1.get_indicator(2) == soci::i_null); + CHECK(r1.get_indicator(3) == soci::i_null); + CHECK(r1.get_indicator(4) == soci::i_null); + } + } +} + +// test for reading rowset using iterator +TEST_CASE_METHOD(common_tests, "Reading ints from rowset", "[core][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(id) values(1)"; + sql << "insert into soci_test(id) values(2)"; + sql << "insert into soci_test(id) values(3)"; + sql << "insert into soci_test(id) values(4)"; + sql << "insert into soci_test(id) values(5)"; + { + rowset rs = (sql.prepare << "select id from soci_test order by id asc"); + + // 1st row + rowset::const_iterator pos = rs.begin(); + CHECK(1 == (*pos)); + + // 3rd row + std::advance(pos, 2); + CHECK(3 == (*pos)); + + // 5th row + std::advance(pos, 2); + CHECK(5 == (*pos)); + + // The End + ++pos; + CHECK(pos == rs.end()); + } + } + +} + +// test for handling 'use' and reading rowset using iterator +TEST_CASE_METHOD(common_tests, "Reading strings from rowset", "[core][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(str) values('abc')"; + sql << "insert into soci_test(str) values('def')"; + sql << "insert into soci_test(str) values('ghi')"; + sql << "insert into soci_test(str) values('jkl')"; + { + // Expected result in numbers + std::string idle("def"); + rowset rs1 = (sql.prepare + << "select str from soci_test where str = :idle", + use(idle)); + + CHECK(1 == std::distance(rs1.begin(), rs1.end())); + + // Expected result in value + idle = "jkl"; + rowset rs2 = (sql.prepare + << "select str from soci_test where str = :idle", + use(idle)); + + CHECK(idle == *(rs2.begin())); + } + } + +} + +// test for handling troublemaker +TEST_CASE_METHOD(common_tests, "Rowset expected exception", "[core][exception][rowset]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + sql << "insert into soci_test(str) values('abc')"; + + std::string troublemaker; + CHECK_THROWS_AS( + rowset((sql.prepare << "select str from soci_test", into(troublemaker))), + soci_error + ); +} + +// functor for next test +struct THelper +{ + THelper() + : val_() + { + } + void operator()(int i) + { + val_ = i; + } + int val_; +}; + +// test for handling NULL values with expected exception: +// "Null value fetched and no indicator defined." +TEST_CASE_METHOD(common_tests, "NULL expected exception", "[core][exception][null]") +{ + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + sql << "insert into soci_test(val) values(1)"; + sql << "insert into soci_test(val) values(2)"; + sql << "insert into soci_test(val) values(NULL)"; + sql << "insert into soci_test(val) values(3)"; + + rowset rs = (sql.prepare << "select val from soci_test order by val asc"); + + CHECK_THROWS_AS( std::for_each(rs.begin(), rs.end(), THelper()), soci_error ); +} + +TEST_CASE_METHOD(common_tests, "soci_error is nothrow", "[core][exception][nothrow]") +{ + CHECK(std::is_nothrow_copy_assignable::value == true); + CHECK(std::is_nothrow_copy_constructible::value == true); + CHECK(std::is_nothrow_destructible::value == true); +} + +// This is like the first dynamic binding test but with rowset and iterators use +TEST_CASE_METHOD(common_tests, "Dynamic binding with rowset", "[core][dynamic][type_conversion]") +{ + soci::session sql(backEndFactory_, connectString_); + + sql.uppercase_column_names(true); + + { + auto_table_creator tableCreator(tc_.table_creator_3(sql)); + + PhonebookEntry p1; + sql << "select * from soci_test", into(p1); + CHECK(p1.name == ""); + CHECK(p1.phone == ""); + + p1.name = "david"; + + sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); + sql << "insert into soci_test values('john', '(404)123-4567')"; + sql << "insert into soci_test values('doe', '(404)123-4567')"; + + rowset rs = (sql.prepare << "select * from soci_test"); + + int count = 0; + for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it) + { + ++count; + PhonebookEntry const& p2 = (*it); + if (p2.name == "david") + { + // see type_conversion + CHECK(p2.phone ==""); + } + else + { + CHECK(p2.phone == "(404)123-4567"); + } + } + + CHECK(3 == count); + } +} + +#ifdef SOCI_HAVE_BOOST + +// test for handling NULL values with boost::optional +// (both into and use) +TEST_CASE_METHOD(common_tests, "NULL with optional", "[core][boost][null]") +{ + + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator0(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(val) values(7)"; + + { + // verify non-null value is fetched correctly + boost::optional opt; + sql << "select val from soci_test", into(opt); + CHECK(opt.is_initialized()); + CHECK(opt.get() == 7); + + // indicators can be used with optional + // (although that's just a consequence of implementation, + // not an intended feature - but let's test it anyway) + indicator ind; + opt.reset(); + sql << "select val from soci_test", into(opt, ind); + CHECK(opt.is_initialized()); + CHECK(opt.get() == 7); + CHECK(ind == i_ok); + + // verify null value is fetched correctly + sql << "select i1 from soci_test", into(opt); + CHECK(opt.is_initialized() == false); + + // and with indicator + opt = 5; + sql << "select i1 from soci_test", into(opt, ind); + CHECK(opt.is_initialized() == false); + CHECK(ind == i_null); + + // verify non-null is inserted correctly + opt = 3; + sql << "update soci_test set val = :v", use(opt); + int j = 0; + sql << "select val from soci_test", into(j); + CHECK(j == 3); + + // verify null is inserted correctly + opt.reset(); + sql << "update soci_test set val = :v", use(opt); + ind = i_ok; + sql << "select val from soci_test", into(j, ind); + CHECK(ind == i_null); + } + + // vector tests (select) + + { + sql << "delete from soci_test"; + + // simple readout of non-null data + + sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; + sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; + sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; + sql << "insert into soci_test(id, val, str) values(4, 8, null)"; + sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; + + std::vector > v(10); + sql << "select val from soci_test order by val", into(v); + + CHECK(v.size() == 5); + CHECK(v[0].is_initialized()); + CHECK(v[0].get() == 5); + CHECK(v[1].is_initialized()); + CHECK(v[1].get() == 6); + CHECK(v[2].is_initialized()); + CHECK(v[2].get() == 7); + CHECK(v[3].is_initialized()); + CHECK(v[3].get() == 8); + CHECK(v[4].is_initialized()); + CHECK(v[4].get() == 9); + + // readout of nulls + + sql << "update soci_test set val = null where id = 2 or id = 4"; + + std::vector ids(5); + sql << "select id, val from soci_test order by id", into(ids), into(v); + + CHECK(v.size() == 5); + CHECK(ids.size() == 5); + CHECK(v[0].is_initialized()); + CHECK(v[0].get() == 5); + CHECK(v[1].is_initialized() == false); + CHECK(v[2].is_initialized()); + CHECK(v[2].get() == 7); + CHECK(v[3].is_initialized() == false); + CHECK(v[4].is_initialized()); + CHECK(v[4].get() == 9); + + // readout with statement preparation + + int id = 1; + + ids.resize(3); + v.resize(3); + statement st = (sql.prepare << + "select id, val from soci_test order by id", into(ids), into(v)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != v.size(); ++i) + { + CHECK(id == ids[i]); + + if (id == 2 || id == 4) + { + CHECK(v[i].is_initialized() == false); + } + else + { + CHECK(v[i].is_initialized()); + CHECK(v[i].get() == id + 4); + } + + ++id; + } + + ids.resize(3); + v.resize(3); + } + CHECK(id == 6); + } + + // and why not stress iterators and the dynamic binding, too! + + { + rowset rs = (sql.prepare << "select id, val, str from soci_test order by id"); + + rowset::const_iterator it = rs.begin(); + CHECK(it != rs.end()); + + row const& r1 = (*it); + + CHECK(r1.size() == 3); + + // Note: for the reason of differences between number(x,y) type and + // binary representation of integers, the following commented assertions + // do not work for Oracle. + // The problem is that for this single table the data type used in Oracle + // table creator for the id column is number(10,0), + // which allows to insert all int values. + // On the other hand, the column description scheme used in the Oracle + // backend figures out that the natural type for such a column + // is eUnsignedInt - this makes the following assertions fail. + // Other database backends (like PostgreSQL) use other types like int + // and this not only allows to insert all int values (obviously), + // but is also recognized as int (obviously). + // There is a similar problem with stream-like extraction, + // where internally get is called and the type mismatch is detected + // for the id column - that's why the code below skips this column + // and tests the remaining column only. + + //CHECK(r1.get_properties(0).get_data_type() == dt_integer); + //CHECK(r1.get_properties(0).get_exchnage_data_type() == db_int32); + CHECK(r1.get_properties(1).get_data_type() == dt_integer); + CHECK(r1.get_properties(1).get_db_type() == db_int32); + CHECK(r1.get_properties(2).get_data_type() == dt_string); + CHECK(r1.get_properties(2).get_db_type() == db_string); + //CHECK(r1.get(0) == 1); + CHECK(r1.get(1) == 5); + CHECK(r1.get(2) == "abc"); + CHECK(r1.get >(1).is_initialized()); + CHECK(r1.get >(1).get() == 5); + CHECK(r1.get >(2).is_initialized()); + CHECK(r1.get >(2).get() == "abc"); + + ++it; + + row const& r2 = (*it); + + CHECK(r2.size() == 3); + + // CHECK(r2.get_properties(0).get_data_type() == dt_integer); + // CHECK(r2.get_properties(0).get_db_type() == db_int32); + CHECK(r2.get_properties(1).get_data_type() == dt_integer); + CHECK(r2.get_properties(1).get_db_type() == db_int32); + CHECK(r2.get_properties(2).get_data_type() == dt_string); + CHECK(r2.get_properties(2).get_db_type() == db_string); + //CHECK(r2.get(0) == 2); + try + { + // expect exception here, this is NULL value + (void)r1.get(1); + FAIL("expected exception not thrown"); + } + catch (soci_error const &) {} + + // but we can read it as optional + CHECK(r2.get >(1).is_initialized() == false); + + // stream-like data extraction + + ++it; + row const &r3 = (*it); + + boost::optional io; + boost::optional so; + + r3.skip(); // move to val and str columns + r3 >> io >> so; + + CHECK(io.is_initialized()); + CHECK(io.get() == 7); + CHECK(so.is_initialized()); + CHECK(so.get() == "ghi"); + + ++it; + row const &r4 = (*it); + + r3.skip(); // move to val and str columns + r4 >> io >> so; + + CHECK(io.is_initialized() == false); + CHECK(so.is_initialized() == false); + } + + // inserts of non-null const data + { + sql << "delete from soci_test"; + + const int id = 10; + const boost::optional val = 11; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(id, "id"), + use(val, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 11); + } + + // bulk inserts of non-null data + + { + sql << "delete from soci_test"; + + std::vector ids; + std::vector > v; + + ids.push_back(10); v.push_back(20); + ids.push_back(11); v.push_back(21); + ids.push_back(12); v.push_back(22); + ids.push_back(13); v.push_back(23); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(ids, "id"), use(v, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 86); + + // bulk inserts of some-null data + + sql << "delete from soci_test"; + + v[2].reset(); + v[3].reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(ids, "id"), use(v, "val"); + + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 41); + } + + + // bulk inserts of non-null data with const vector + + { + sql << "delete from soci_test"; + + std::vector ids; + std::vector > v; + + ids.push_back(10); v.push_back(20); + ids.push_back(11); v.push_back(21); + ids.push_back(12); v.push_back(22); + ids.push_back(13); v.push_back(23); + + const std::vector& cref_ids = ids; + const std::vector >& cref_v = v; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(cref_ids, "id"), + use(cref_v, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 86); + + // bulk inserts of some-null data + + sql << "delete from soci_test"; + + v[2].reset(); + v[3].reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(cref_ids, "id"), + use(cref_v, "val"); + + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 41); + } + + // composability with user conversions + + { + sql << "delete from soci_test"; + + boost::optional omi1; + boost::optional omi2; + + omi1 = MyInt(125); + omi2.reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(omi1), use(omi2); + + sql << "select id, val from soci_test", into(omi2), into(omi1); + + CHECK(omi1.is_initialized() == false); + CHECK(omi2.is_initialized()); + CHECK(omi2.get().get() == 125); + } + + // use with const optional and user conversions + + { + sql << "delete from soci_test"; + + boost::optional omi1; + boost::optional omi2; + + omi1 = MyInt(125); + omi2.reset(); + + boost::optional const & comi1 = omi1; + boost::optional const & comi2 = omi2; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(comi1), use(comi2); + + sql << "select id, val from soci_test", into(omi2), into(omi1); + + CHECK(omi1.is_initialized() == false); + CHECK(omi2.is_initialized()); + CHECK(omi2.get().get() == 125); + } + + // use with rowset and table containing null values + + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(id, val) values(1, 10)"; + sql << "insert into soci_test(id, val) values(2, 11)"; + sql << "insert into soci_test(id, val) values(3, NULL)"; + sql << "insert into soci_test(id, val) values(4, 13)"; + + rowset > rs = (sql.prepare << + "select val from soci_test order by id asc"); + + // 1st row + rowset >::const_iterator pos = rs.begin(); + CHECK((*pos).is_initialized()); + CHECK(10 == (*pos).get()); + + // 2nd row + ++pos; + CHECK((*pos).is_initialized()); + CHECK(11 == (*pos).get()); + + // 3rd row + ++pos; + CHECK((*pos).is_initialized() == false); + + // 4th row + ++pos; + CHECK((*pos).is_initialized()); + CHECK(13 == (*pos).get()); + } + + // inserting using an i_null indicator with a boost::optional should + // insert null, even if the optional is valid, just as with standard + // types + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + { + indicator ind = i_null; + boost::optional v1(10); + sql << "insert into soci_test(id, val) values(1, :val)", + use(v1, ind); + } + + // verify the value is fetched correctly as null + { + indicator ind; + boost::optional opt; + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 1", into(opt, ind); + CHECK(ind == i_null); + CHECK(!opt.is_initialized()); + } + } + + // prepared statement inserting non-null and null values alternatively + // (without passing an explicit indicator) + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + { + int id; + boost::optional val; + statement st = (sql.prepare + << "insert into soci_test(id, val) values (:id, :val)", + use(id), use(val)); + + id = 1; + val = 10; + st.execute(true); + + id = 2; + val = boost::optional(); + st.execute(true); + + id = 3; + val = 11; + st.execute(true); + } + + // verify values are fetched correctly + { + indicator ind; + boost::optional opt; + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 1", into(opt, ind); + CHECK(ind == i_ok); + CHECK(opt.is_initialized()); + CHECK(opt.get() == 10); + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 2", into(opt, ind); + CHECK(ind == i_null); + CHECK(!opt.is_initialized()); + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 3", into(opt, ind); + CHECK(ind == i_ok); + REQUIRE(opt.is_initialized()); + CHECK(opt.get() == 11); + } + } + } +} + +#endif // SOCI_HAVE_BOOST + +#ifdef SOCI_HAVE_CXX17 + +// test for handling NULL values with std::optional +// (both into and use) +TEST_CASE_METHOD(common_tests, "NULL with std optional", "[core][null]") +{ + + soci::session sql(backEndFactory_, connectString_); + + // create and populate the test table + auto_table_creator tableCreator0(tc_.table_creator_1(sql)); + { + sql << "insert into soci_test(val) values(7)"; + + { + // verify non-null value is fetched correctly + std::optional opt; + sql << "select val from soci_test", into(opt); + CHECK(opt.has_value()); + CHECK(opt.value() == 7); + + // indicators can be used with optional + // (although that's just a consequence of implementation, + // not an intended feature - but let's test it anyway) + indicator ind; + opt.reset(); + sql << "select val from soci_test", into(opt, ind); + CHECK(opt.has_value()); + CHECK(opt.value() == 7); + CHECK(ind == i_ok); + + // verify null value is fetched correctly + sql << "select i1 from soci_test", into(opt); + CHECK(opt.has_value() == false); + + // and with indicator + opt = 5; + sql << "select i1 from soci_test", into(opt, ind); + CHECK(opt.has_value() == false); + CHECK(ind == i_null); + + // verify non-null is inserted correctly + opt = 3; + sql << "update soci_test set val = :v", use(opt); + int j = 0; + sql << "select val from soci_test", into(j); + CHECK(j == 3); + + // verify null is inserted correctly + opt.reset(); + sql << "update soci_test set val = :v", use(opt); + ind = i_ok; + sql << "select val from soci_test", into(j, ind); + CHECK(ind == i_null); + } + + // vector tests (select) + + { + sql << "delete from soci_test"; + + // simple readout of non-null data + + sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; + sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; + sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; + sql << "insert into soci_test(id, val, str) values(4, 8, null)"; + sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; + + std::vector > v(10); + sql << "select val from soci_test order by val", into(v); + + CHECK(v.size() == 5); + CHECK(v[0].has_value()); + CHECK(v[0].value() == 5); + CHECK(v[1].has_value()); + CHECK(v[1].value() == 6); + CHECK(v[2].has_value()); + CHECK(v[2].value() == 7); + CHECK(v[3].has_value()); + CHECK(v[3].value() == 8); + CHECK(v[4].has_value()); + CHECK(v[4].value() == 9); + + // readout of nulls + + sql << "update soci_test set val = null where id = 2 or id = 4"; + + std::vector ids(5); + sql << "select id, val from soci_test order by id", into(ids), into(v); + + CHECK(v.size() == 5); + CHECK(ids.size() == 5); + CHECK(v[0].has_value()); + CHECK(v[0].value() == 5); + CHECK(v[1].has_value() == false); + CHECK(v[2].has_value()); + CHECK(v[2].value() == 7); + CHECK(v[3].has_value() == false); + CHECK(v[4].has_value()); + CHECK(v[4].value() == 9); + + // readout with statement preparation + + int id = 1; + + ids.resize(3); + v.resize(3); + statement st = (sql.prepare << + "select id, val from soci_test order by id", into(ids), into(v)); + st.execute(); + while (st.fetch()) + { + for (std::size_t i = 0; i != v.size(); ++i) + { + CHECK(id == ids[i]); + + if (id == 2 || id == 4) + { + CHECK(v[i].has_value() == false); + } + else + { + CHECK(v[i].has_value()); + CHECK(v[i].value() == id + 4); + } + + ++id; + } + + ids.resize(3); + v.resize(3); + } + CHECK(id == 6); + } + + // and why not stress iterators and the dynamic binding, too! + + { + rowset rs = (sql.prepare << "select id, val, str from soci_test order by id"); + + rowset::const_iterator it = rs.begin(); + CHECK(it != rs.end()); + + row const& r1 = (*it); + + CHECK(r1.size() == 3); + + // Note: for the reason of differences between number(x,y) type and + // binary representation of integers, the following commented assertions + // do not work for Oracle. + // The problem is that for this single table the data type used in Oracle + // table creator for the id column is number(10,0), + // which allows to insert all int values. + // On the other hand, the column description scheme used in the Oracle + // backend figures out that the natural type for such a column + // is eUnsignedInt - this makes the following assertions fail. + // Other database backends (like PostgreSQL) use other types like int + // and this not only allows to insert all int values (obviously), + // but is also recognized as int (obviously). + // There is a similar problem with stream-like extraction, + // where internally get is called and the type mismatch is detected + // for the id column - that's why the code below skips this column + // and tests the remaining column only. + + //CHECK(r1.get_properties(0).get_data_type() == dt_integer); + //CHECK(r1.get_properties(0).get_db_type() == db_int32); + CHECK(r1.get_properties(1).get_data_type() == dt_integer); + CHECK(r1.get_properties(1).get_db_type() == db_int32); + CHECK(r1.get_properties(2).get_data_type() == dt_string); + CHECK(r1.get_properties(2).get_db_type() == db_string); + //CHECK(r1.get(0) == 1); + CHECK(r1.get(1) == 5); + CHECK(r1.get(2) == "abc"); + CHECK(r1.get >(1).has_value()); + CHECK(r1.get >(1).value() == 5); + CHECK(r1.get >(2).has_value()); + CHECK(r1.get >(2).value() == "abc"); + + ++it; + + row const& r2 = (*it); + + CHECK(r2.size() == 3); + + // CHECK(r2.get_properties(0).get_data_type() == dt_integer); + // CHECK(r2.get_properties(0).get_db_type() == db_int32); + CHECK(r2.get_properties(1).get_data_type() == dt_integer); + CHECK(r2.get_properties(1).get_db_type() == db_int32); + CHECK(r2.get_properties(2).get_data_type() == dt_string); + CHECK(r2.get_properties(2).get_db_type() == db_string); + //CHECK(r2.get(0) == 2); + try + { + // expect exception here, this is NULL value + (void)r1.get(1); + FAIL("expected exception not thrown"); + } + catch (soci_error const &) {} + + // but we can read it as optional + CHECK(r2.get >(1).has_value() == false); + + // stream-like data extraction + + ++it; + row const &r3 = (*it); + + std::optional io; + std::optional so; + + r3.skip(); // move to val and str columns + r3 >> io >> so; + + CHECK(io.has_value()); + CHECK(io.value() == 7); + CHECK(so.has_value()); + CHECK(so.value() == "ghi"); + + ++it; + row const &r4 = (*it); + + r3.skip(); // move to val and str columns + r4 >> io >> so; + + CHECK(io.has_value() == false); + CHECK(so.has_value() == false); + } + + // inserts of non-null const data + { + sql << "delete from soci_test"; + + const int id = 10; + const std::optional val = 11; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(id, "id"), + use(val, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 11); + } + + // bulk inserts of non-null data + + { + sql << "delete from soci_test"; + + std::vector ids; + std::vector > v; + + ids.push_back(10); v.push_back(20); + ids.push_back(11); v.push_back(21); + ids.push_back(12); v.push_back(22); + ids.push_back(13); v.push_back(23); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(ids, "id"), use(v, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 86); + + // bulk inserts of some-null data + + sql << "delete from soci_test"; + + v[2].reset(); + v[3].reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(ids, "id"), use(v, "val"); + + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 41); + } + + + // bulk inserts of non-null data with const vector + + { + sql << "delete from soci_test"; + + std::vector ids; + std::vector > v; + + ids.push_back(10); v.push_back(20); + ids.push_back(11); v.push_back(21); + ids.push_back(12); v.push_back(22); + ids.push_back(13); v.push_back(23); + + const std::vector& cref_ids = ids; + const std::vector >& cref_v = v; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(cref_ids, "id"), + use(cref_v, "val"); + + int sum; + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 86); + + // bulk inserts of some-null data + + sql << "delete from soci_test"; + + v[2].reset(); + v[3].reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(cref_ids, "id"), + use(cref_v, "val"); + + sql << "select sum(val) from soci_test", into(sum); + CHECK(sum == 41); + } + + // composability with user conversions + + { + sql << "delete from soci_test"; + + std::optional omi1; + std::optional omi2; + + omi1 = MyInt(125); + omi2.reset(); + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(omi1), use(omi2); + + sql << "select id, val from soci_test", into(omi2), into(omi1); + + CHECK(omi1.has_value() == false); + CHECK(omi2.has_value()); + CHECK(omi2.value().get() == 125); + } + + // use with const optional and user conversions + + { + sql << "delete from soci_test"; + + std::optional omi1; + std::optional omi2; + + omi1 = MyInt(125); + omi2.reset(); + + std::optional const & comi1 = omi1; + std::optional const & comi2 = omi2; + + sql << "insert into soci_test(id, val) values(:id, :val)", + use(comi1), use(comi2); + + sql << "select id, val from soci_test", into(omi2), into(omi1); + + CHECK(omi1.has_value() == false); + CHECK(omi2.has_value()); + CHECK(omi2.value().get() == 125); + } + + // use with rowset and table containing null values + + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + sql << "insert into soci_test(id, val) values(1, 10)"; + sql << "insert into soci_test(id, val) values(2, 11)"; + sql << "insert into soci_test(id, val) values(3, NULL)"; + sql << "insert into soci_test(id, val) values(4, 13)"; + + rowset > rs = (sql.prepare << + "select val from soci_test order by id asc"); + + // 1st row + rowset >::const_iterator pos = rs.begin(); + CHECK((*pos).has_value()); + CHECK(10 == (*pos).value()); + + // 2nd row + ++pos; + CHECK((*pos).has_value()); + CHECK(11 == (*pos).value()); + + // 3rd row + ++pos; + CHECK((*pos).has_value() == false); + + // 4th row + ++pos; + CHECK((*pos).has_value()); + CHECK(13 == (*pos).value()); + } + + // inserting using an i_null indicator with a std::optional should + // insert null, even if the optional is valid, just as with standard + // types + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + { + indicator ind = i_null; + std::optional v1(10); + sql << "insert into soci_test(id, val) values(1, :val)", + use(v1, ind); + } + + // verify the value is fetched correctly as null + { + indicator ind; + std::optional opt; + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 1", into(opt, ind); + CHECK(ind == i_null); + CHECK(!opt.has_value()); + } + } + + // prepared statement inserting non-null and null values alternatively + // (without passing an explicit indicator) + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + { + int id; + std::optional val; + statement st = (sql.prepare + << "insert into soci_test(id, val) values (:id, :val)", + use(id), use(val)); + + id = 1; + val = 10; + st.execute(true); + + id = 2; + val = std::optional(); + st.execute(true); + + id = 3; + val = 11; + st.execute(true); + } + + // verify values are fetched correctly + { + indicator ind; + std::optional opt; + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 1", into(opt, ind); + CHECK(ind == i_ok); + CHECK(opt.has_value()); + CHECK(opt.value() == 10); + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 2", into(opt, ind); + CHECK(ind == i_null); + CHECK(!opt.has_value()); + + ind = i_truncated; + opt = 0; + sql << "select val from soci_test where id = 3", into(opt, ind); + CHECK(ind == i_ok); + REQUIRE(opt.has_value()); + CHECK(opt.value() == 11); + } + } + } +} +#endif + +// connection and reconnection tests +TEST_CASE_METHOD(common_tests, "Connection and reconnection", "[core][connect]") +{ + { + // empty session + soci::session sql; + + CHECK(!sql.is_connected()); + + // idempotent: + sql.close(); + + try + { + sql.reconnect(); + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "Cannot reconnect without previous connection."); + } + + // open from empty session + sql.open(backEndFactory_, connectString_); + CHECK(sql.is_connected()); + sql.close(); + CHECK(!sql.is_connected()); + + // reconnecting from closed session + sql.reconnect(); + CHECK(sql.is_connected()); + + // opening already connected session + try + { + sql.open(backEndFactory_, connectString_); + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "Cannot open already connected session."); + } + + CHECK(sql.is_connected()); + sql.close(); + + // open from closed + sql.open(backEndFactory_, connectString_); + CHECK(sql.is_connected()); + + // reconnect from already connected session + sql.reconnect(); + CHECK(sql.is_connected()); + } + + { + soci::session sql; + + try + { + sql << "this statement cannot execute"; + FAIL("expected exception not thrown"); + } + catch (soci_error const &e) + { + CHECK(e.get_error_message() == + "Session is not connected."); + } + } + + { + // check move semantics of session + + #if __GNUC__ >= 13 || defined (__clang__) + SOCI_GCC_WARNING_SUPPRESS(self-move) + #endif + + soci::session sql_0; + soci::session sql_1 = std::move(sql_0); + + CHECK(!sql_0.is_connected()); + CHECK(!sql_1.is_connected()); + + sql_0.open(backEndFactory_, connectString_); + CHECK(sql_0.is_connected()); + CHECK(sql_0.get_backend()); + + sql_1 = std::move(sql_0); + CHECK(!sql_0.is_connected()); + CHECK(!sql_0.get_backend()); + CHECK(sql_1.is_connected()); + CHECK(sql_1.get_backend()); + + sql_1 = std::move(sql_1); + CHECK(sql_1.is_connected()); + CHECK(sql_1.get_backend()); + + #if __GNUC__ >= 13 || defined (__clang__) + SOCI_GCC_WARNING_RESTORE(self-move) + #endif + } +} + +#ifdef SOCI_HAVE_BOOST + +TEST_CASE_METHOD(common_tests, "Boost tuple", "[core][boost][tuple]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_2(sql)); + { + boost::tuple t1(3.5, 7, "Joe Hacker"); + ASSERT_EQUAL(t1.get<0>(), 3.5); + CHECK(t1.get<1>() == 7); + CHECK(t1.get<2>() == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // basic query + + boost::tuple t2; + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(t2.get<0>(), 3.5); + CHECK(t2.get<1>() == 7); + CHECK(t2.get<2>() == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // composability with boost::optional + + // use: + boost::tuple, std::string> t1( + 3.5, boost::optional(7), "Joe Hacker"); + ASSERT_EQUAL(t1.get<0>(), 3.5); + CHECK(t1.get<1>().is_initialized()); + CHECK(t1.get<1>().get() == 7); + CHECK(t1.get<2>() == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::tuple, std::string> t2; + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(t2.get<0>(), 3.5); + CHECK(t2.get<1>().is_initialized()); + CHECK(t2.get<1>().get() == 7); + CHECK(t2.get<2>() == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // composability with user-provided conversions + + // use: + boost::tuple t1(3.5, 7, "Joe Hacker"); + ASSERT_EQUAL(t1.get<0>(), 3.5); + CHECK(t1.get<1>().get() == 7); + CHECK(t1.get<2>() == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::tuple t2; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(t2.get<0>(), 3.5); + CHECK(t2.get<1>().get() == 7); + CHECK(t2.get<2>() == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // let's have fun - composition of tuple, optional and user-defined type + + // use: + boost::tuple, std::string> t1( + 3.5, boost::optional(7), "Joe Hacker"); + ASSERT_EQUAL(t1.get<0>(), 3.5); + CHECK(t1.get<1>().is_initialized()); + CHECK(t1.get<1>().get().get() == 7); + CHECK(t1.get<2>() == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::tuple, std::string> t2; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(t2.get<0>(), 3.5); + CHECK(t2.get<1>().is_initialized()); + CHECK(t2.get<1>().get().get() == 7); + CHECK(t2.get<2>() == "Joe Hacker"); + + sql << "update soci_test set num_int = NULL"; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(t2.get<0>(), 3.5); + CHECK(t2.get<1>().is_initialized() == false); + CHECK(t2.get<2>() == "Joe Hacker"); + } + + { + // rowset + + sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; + sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; + sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; + + typedef boost::tuple, std::string> T; + + rowset rs = (sql.prepare + << "select num_float, num_int, name from soci_test order by num_float asc"); + + rowset::const_iterator pos = rs.begin(); + + ASSERT_EQUAL(pos->get<0>(), 3.5); + CHECK(pos->get<1>().is_initialized() == false); + CHECK(pos->get<2>() == "Joe Hacker"); + + ++pos; + ASSERT_EQUAL(pos->get<0>(), 4.0); + CHECK(pos->get<1>().is_initialized()); + CHECK(pos->get<1>().get() == 8); + CHECK(pos->get<2>() == "Tony Coder"); + + ++pos; + ASSERT_EQUAL(pos->get<0>(), 4.5); + CHECK(pos->get<1>().is_initialized() == false); + CHECK(pos->get<2>() == "Cecile Sharp"); + + ++pos; + ASSERT_EQUAL(pos->get<0>(), 5.0); + CHECK(pos->get<1>().is_initialized()); + CHECK(pos->get<1>().get() == 10); + CHECK(pos->get<2>() == "Djhava Ravaa"); + + ++pos; + CHECK(pos == rs.end()); + } +} + +#if defined(BOOST_VERSION) && BOOST_VERSION >= 103500 + +TEST_CASE_METHOD(common_tests, "Boost fusion", "[core][boost][fusion]") +{ + + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_2(sql)); + { + boost::fusion::vector t1(3.5, 7, "Joe Hacker"); + ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); + CHECK(boost::fusion::at_c<1>(t1) == 7); + CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // basic query + + boost::fusion::vector t2; + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); + CHECK(boost::fusion::at_c<1>(t2) == 7); + CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // composability with boost::optional + + // use: + boost::fusion::vector, std::string> t1( + 3.5, boost::optional(7), "Joe Hacker"); + ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); + CHECK(boost::fusion::at_c<1>(t1).is_initialized()); + CHECK(boost::fusion::at_c<1>(t1).get() == 7); + CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::fusion::vector, std::string> t2; + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); + CHECK(boost::fusion::at_c<1>(t2).is_initialized()); + CHECK(boost::fusion::at_c<1>(t2) == 7); + CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // composability with user-provided conversions + + // use: + boost::fusion::vector t1(3.5, 7, "Joe Hacker"); + ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); + CHECK(boost::fusion::at_c<1>(t1).get() == 7); + CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::fusion::vector t2; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); + CHECK(boost::fusion::at_c<1>(t2).get() == 7); + CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); + + sql << "delete from soci_test"; + } + + { + // let's have fun - composition of tuple, optional and user-defined type + + // use: + boost::fusion::vector, std::string> t1( + 3.5, boost::optional(7), "Joe Hacker"); + ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); + CHECK(boost::fusion::at_c<1>(t1).is_initialized()); + CHECK(boost::fusion::at_c<1>(t1).get().get() == 7); + CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); + + sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); + + // into: + boost::fusion::vector, std::string> t2; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); + CHECK(boost::fusion::at_c<1>(t2).is_initialized()); + CHECK(boost::fusion::at_c<1>(t2).get().get() == 7); + CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); + + sql << "update soci_test set num_int = NULL"; + + sql << "select num_float, num_int, name from soci_test", into(t2); + + ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); + CHECK(boost::fusion::at_c<1>(t2).is_initialized() == false); + CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); + } + + { + // rowset + + sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; + sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; + sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; + + typedef boost::fusion::vector, std::string> T; + + rowset rs = (sql.prepare + << "select num_float, num_int, name from soci_test order by num_float asc"); + + rowset::const_iterator pos = rs.begin(); + + ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 3.5); + CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); + CHECK(boost::fusion::at_c<2>(*pos) == "Joe Hacker"); + + ++pos; + ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.0); + CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); + CHECK(boost::fusion::at_c<1>(*pos).get() == 8); + CHECK(boost::fusion::at_c<2>(*pos) == "Tony Coder"); + + ++pos; + ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.5); + CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); + CHECK(boost::fusion::at_c<2>(*pos) == "Cecile Sharp"); + + ++pos; + ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 5.0); + CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); + CHECK(boost::fusion::at_c<1>(*pos).get() == 10); + CHECK(boost::fusion::at_c<2>(*pos) == "Djhava Ravaa"); + + ++pos; + CHECK(pos == rs.end()); + } +} + +#endif // defined(BOOST_VERSION) && BOOST_VERSION >= 103500 + +// test for boost::gregorian::date +TEST_CASE_METHOD(common_tests, "Boost date", "[core][boost][datetime]") +{ + soci::session sql(backEndFactory_, connectString_); + + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + std::tm nov15 = std::tm(); + nov15.tm_year = 105; + nov15.tm_mon = 10; + nov15.tm_mday = 15; + nov15.tm_hour = 0; + nov15.tm_min = 0; + nov15.tm_sec = 0; + + sql << "insert into soci_test(tm) values(:tm)", use(nov15); + + boost::gregorian::date bgd; + sql << "select tm from soci_test", into(bgd); + + CHECK(bgd.year() == 2005); + CHECK(bgd.month() == 11); + CHECK(bgd.day() == 15); + + sql << "update soci_test set tm = NULL"; + try + { + sql << "select tm from soci_test", into(bgd); + FAIL("expected exception not thrown"); + } + catch (soci_error const & e) + { + CHECK(e.get_error_message() == + "Null value not allowed for this type"); + } + } + + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + boost::gregorian::date bgd(2008, boost::gregorian::May, 5); + + sql << "insert into soci_test(tm) values(:tm)", use(bgd); + + std::tm t = std::tm(); + sql << "select tm from soci_test", into(t); + + CHECK(t.tm_year == 108); + CHECK(t.tm_mon == 4); + CHECK(t.tm_mday == 5); + } + +} + +#endif // SOCI_HAVE_BOOST + +// connection pool - simple sequential test, no multiple threads +TEST_CASE_METHOD(common_tests, "Connection pool", "[core][connection][pool]") +{ + // phase 1: preparation + const size_t pool_size = 10; + connection_pool pool(pool_size); + + for (std::size_t i = 0; i != pool_size; ++i) + { + session & sql = pool.at(i); + sql.open(backEndFactory_, connectString_); + } + + // phase 2: usage + for (std::size_t i = 0; i != pool_size; ++i) + { + // poor man way to lease more than one connection + soci::session sql_unused1(pool); + soci::session sql(pool); + soci::session sql_unused2(pool); + { + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + char c('a'); + sql << "insert into soci_test(c) values(:c)", use(c); + sql << "select c from soci_test", into(c); + CHECK(c == 'a'); + } + } +} + +// Issue 66 - test query transformation callback feature +static std::string no_op_transform(std::string query) +{ + return query; +} + +static std::string lower_than_g(std::string query) +{ + return query + " WHERE c < 'g'"; +} + +struct where_condition +{ + where_condition(std::string const& where) + : where_(where) + {} + + std::string operator()(std::string const& query) const + { + return query + " WHERE " + where_; + } + + std::string where_; +}; + + +void run_query_transformation_test(test_context_base const& tc, session& sql) +{ + // create and populate the test table + auto_table_creator tableCreator(tc.table_creator_1(sql)); + + for (char c = 'a'; c <= 'z'; ++c) + { + sql << "insert into soci_test(c) values(\'" << c << "\')"; + } + + char const* query = "select count(*) from soci_test"; + + // free function, no-op + { + sql.set_query_transformation(no_op_transform); + int count; + sql << query, into(count); + CHECK(count == 'z' - 'a' + 1); + } + + // free function + { + sql.set_query_transformation(lower_than_g); + int count; + sql << query, into(count); + CHECK(count == 'g' - 'a'); + } + + // function object with state + { + sql.set_query_transformation(where_condition("c > 'g' AND c < 'j'")); + int count = 0; + sql << query, into(count); + CHECK(count == 'j' - 'h'); + count = 0; + sql.set_query_transformation(where_condition("c > 's' AND c <= 'z'")); + sql << query, into(count); + CHECK(count == 'z' - 's'); + } + +#if 0 + // lambda is just presented as an example to curious users + { + sql.set_query_transformation( + [](std::string const& query) { + return query + " WHERE c > 'g' AND c < 'j'"; + }); + + int count = 0; + sql << query, into(count); + CHECK(count == 'j' - 'h'); + } +#endif + + // prepared statements + + // constant effect (pre-prepare set transformation) + { + // set transformation after statement is prepared + sql.set_query_transformation(lower_than_g); + // prepare statement + int count; + statement st = (sql.prepare << query, into(count)); + // observe transformation effect + st.execute(true); + CHECK(count == 'g' - 'a'); + // reset transformation + sql.set_query_transformation(no_op_transform); + // observe the same transformation, no-op set above has no effect + count = 0; + st.execute(true); + CHECK(count == 'g' - 'a'); + } + + // no effect (post-prepare set transformation) + { + // reset + sql.set_query_transformation(no_op_transform); + + // prepare statement + int count; + statement st = (sql.prepare << query, into(count)); + // set transformation after statement is prepared + sql.set_query_transformation(lower_than_g); + // observe no effect of WHERE clause injection + st.execute(true); + CHECK(count == 'z' - 'a' + 1); + } +} + +TEST_CASE_METHOD(common_tests, "Query transformation", "[core][query-transform]") +{ + soci::session sql(backEndFactory_, connectString_); + run_query_transformation_test(tc_, sql); +} + +TEST_CASE_METHOD(common_tests, "Query transformation with connection pool", "[core][query-transform][pool]") +{ + // phase 1: preparation + const size_t pool_size = 10; + connection_pool pool(pool_size); + + for (std::size_t i = 0; i != pool_size; ++i) + { + session & sql = pool.at(i); + sql.open(backEndFactory_, connectString_); + } + + soci::session sql(pool); + run_query_transformation_test(tc_, sql); +} + +// Originally, submitted to SQLite3 backend and later moved to common test. +// Test commit b394d039530f124802d06c3b1a969c3117683152 +// Author: Mika Fischer +// Date: Thu Nov 17 13:28:07 2011 +0100 +// Implement get_affected_rows for SQLite3 backend +TEST_CASE_METHOD(common_tests, "Get affected rows", "[core][affected-rows]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_4(sql)); + if (!tableCreator.get()) + { + std::cout << "test get_affected_rows skipped (function not implemented)" << std::endl; + return; + } + + + for (int i = 0; i != 10; i++) + { + sql << "insert into soci_test(val) values(:val)", use(i); + } + + int step = 2; + statement st1 = (sql.prepare << + "update soci_test set val = val + :step where val = 5", use(step, "step")); + st1.execute(true); + CHECK(st1.get_affected_rows() == 1); + + // attempts to run the query again, no rows should be affected + st1.execute(true); + CHECK(st1.get_affected_rows() == 0); + + statement st2 = (sql.prepare << + "update soci_test set val = val + 1"); + st2.execute(true); + + CHECK(st2.get_affected_rows() == 10); + + statement st3 = (sql.prepare << + "delete from soci_test where val <= 5"); + st3.execute(true); + + CHECK(st3.get_affected_rows() == 5); + + statement st4 = (sql.prepare << + "update soci_test set val = val + 1"); + st4.execute(true); + + CHECK(st4.get_affected_rows() == 5); + + std::vector v(5, 0); + for (std::size_t i = 0; i < v.size(); ++i) + { + v[i] = (7 + static_cast(i)); + } + + // test affected rows for bulk operations. + statement st5 = (sql.prepare << + "delete from soci_test where val = :v", use(v)); + st5.execute(true); + + CHECK(st5.get_affected_rows() == 5); + + std::vector w(2, "1"); + w[1] = "a"; // this invalid value may cause an exception. + statement st6 = (sql.prepare << + "insert into soci_test(val) values(:val)", use(w)); + try { st6.execute(true); } + catch(...) {} + + // confirm the partial insertion. + int val = 0; + sql << "select count(val) from soci_test", into(val); + if(val != 0) + { + // Notice that some ODBC drivers don't return the number of updated + // rows at all in the case of partially executed statement like this + // one, while MySQL ODBC driver wrongly returns 2 affected rows even + // though only one was actually inserted. + // + // So we can't check for "get_affected_rows() == val" here, it would + // fail in too many cases -- just check that the backend doesn't lie to + // us about no rows being affected at all (even if it just honestly + // admits that it has no idea by returning -1). + CHECK(st6.get_affected_rows() != 0); + } +} + +// test fix for: Backend is not set properly with connection pool (pull #5) +TEST_CASE_METHOD(common_tests, "Backend with connection pool", "[core][pool]") +{ + const size_t pool_size = 1; + connection_pool pool(pool_size); + + for (std::size_t i = 0; i != pool_size; ++i) + { + session & sql = pool.at(i); + sql.open(backEndFactory_, connectString_); + } + + soci::session sql(pool); + sql.reconnect(); + sql.begin(); // no crash expected +} + +// test fix for: Session from connection pool not set backend properly when call open +TEST_CASE_METHOD(common_tests, "Session from connection pool call open reset backend", "[core][pool]") +{ + const size_t pool_size = 1; + connection_pool pool(pool_size); + + soci::session sql(pool); + sql.open(backEndFactory_, connectString_); + REQUIRE_NOTHROW( sql.begin() ); +} + +// issue 67 - Allocated statement backend memory leaks on exception +// If the test runs under memory debugger and it passes, then +// soci::details::statement_impl::backEnd_ must not leak +TEST_CASE_METHOD(common_tests, "Backend memory leak", "[core][leak]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + try + { + rowset rs1 = (sql.prepare << "select * from soci_testX"); + + // TODO: On Linux, no exception thrown; neither from prepare, nor from execute? + // soci_odbc_test_postgresql: + // /home/travis/build/SOCI/soci/src/core/test/common-tests.h:3505: + // void soci::tests::common_tests::test_issue67(): Assertion `!"exception expected"' failed. + //FAIL("exception expected"); // relax temporarily + } + catch (soci_error const &e) + { + (void)e; + } +} + +// issue 154 - Calling undefine_and_bind and then define_and_bind causes a leak. +// If the test runs under memory debugger and it passes, then +// soci::details::standard_use_type_backend and vector_use_type_backend must not leak +TEST_CASE_METHOD(common_tests, "Bind memory leak", "[core][leak]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + sql << "insert into soci_test(id) values (1)"; + { + int id = 1; + int val = 0; + statement st(sql); + st.exchange(use(id)); + st.alloc(); + st.prepare("select id from soci_test where id = :1"); + st.define_and_bind(); + st.undefine_and_bind(); + st.exchange(soci::into(val)); + st.define_and_bind(); + st.execute(true); + CHECK(val == 1); + } + // vector variation + { + std::vector ids(1, 2); + std::vector vals(1, 1); + int val = 0; + statement st(sql); + st.exchange(use(ids)); + st.alloc(); + st.prepare("insert into soci_test(id, val) values (:1, :2)"); + st.define_and_bind(); + st.undefine_and_bind(); + st.exchange(use(vals)); + st.define_and_bind(); + st.execute(true); + sql << "select val from soci_test where id = 2", into(val); + CHECK(val == 1); + } +} + +// Helper functions for issue 723 test +namespace { + + // Creates a std::tm with UK DST threshold 31st March 2019 01:00:00 + std::tm create_uk_dst_threshold() + { + std::tm dst_threshold = std::tm(); + dst_threshold.tm_year = 119; // 2019 + dst_threshold.tm_mon = 2; // March + dst_threshold.tm_mday = 31; // 31st + dst_threshold.tm_hour = 1; // 1AM + dst_threshold.tm_min = 0; + dst_threshold.tm_sec = 0; + dst_threshold.tm_isdst = -1; // Determine DST from OS + return dst_threshold; + } + + // Sanity check to verify that the DST threshold causes mktime to modify + // the input hour (the condition that causes issue 723). + // This check really shouldn't fail but since it is the basis of the test + // it is worth verifying. + bool does_mktime_modify_input_hour() + { + std::tm dst_threshold = create_uk_dst_threshold(); + std::tm verify_mktime = dst_threshold; + mktime(&verify_mktime); + return verify_mktime.tm_hour != dst_threshold.tm_hour; + } + + // We don't have any way to change the time zone for just this process + // under MSW, so we just skip this test when not running in UK time-zone + // there. Under Unix systems we can however switch to UK time zone + // temporarily by just setting the TZ environment variable. +#ifndef _WIN32 + // Helper RAII class changing time zone to the specified one in its ctor + // and restoring the original time zone in its dtor. + class tz_setter + { + public: + explicit tz_setter(const std::string& time_zone) + { + char* tz_value = getenv("TZ"); + if (tz_value != NULL) + { + original_tz_value_ = tz_value; + } + + setenv("TZ", time_zone.c_str(), 1 /* overwrite */); + tzset(); + } + + ~tz_setter() + { + // Restore TZ value so other tests aren't affected. + if (original_tz_value_.empty()) + unsetenv("TZ"); + else + setenv("TZ", original_tz_value_.c_str(), 1); + tzset(); + } + + private: + std::string original_tz_value_; + }; +#endif // !_WIN32 +} + +// Issue 723 - std::tm timestamp problem with DST. +// When reading date/time on Daylight Saving Time threshold, hour value is +// silently changed. +TEST_CASE_METHOD(common_tests, "std::tm timestamp problem with DST", "[core][into][tm][dst]") +{ +#ifdef _WIN32 + if (!does_mktime_modify_input_hour()) + { + WARN("The DST test can only be run in the UK time zone, please switch to it manually."); + return; + } +#else // !_WIN32 + // Set UK timezone for this test scope. + tz_setter switch_to_UK_tz("Europe/London"); + + if (!does_mktime_modify_input_hour()) + { + WARN("Switching to the UK time zone unexpectedly failed, skipping the DST test."); + return; + } +#endif // _WIN32/!_WIN32 + + // Open session and create table with a date/time column. + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + // Round trip dst threshold time to database. + std::tm write_time = create_uk_dst_threshold(); + sql << "insert into soci_test(tm) values(:tm)", use(write_time); + std::tm read_time = std::tm(); + sql << "select tm from soci_test", soci::into(read_time); + + // Check that the round trip was consistent. + std::tm dst_threshold = create_uk_dst_threshold(); + CHECK(read_time.tm_year == dst_threshold.tm_year); + CHECK(read_time.tm_mon == dst_threshold.tm_mon); + CHECK(read_time.tm_mday == dst_threshold.tm_mday); + CHECK(read_time.tm_hour == dst_threshold.tm_hour); + CHECK(read_time.tm_min == dst_threshold.tm_min); + CHECK(read_time.tm_sec == dst_threshold.tm_sec); +} + +TEST_CASE_METHOD(common_tests, "Insert error", "[core][insert][exception]") +{ + soci::session sql(backEndFactory_, connectString_); + + struct pk_table_creator : table_creator_base + { + explicit pk_table_creator(session& sql) : table_creator_base(sql) + { + // For some backends (at least Firebird), it is important to + // execute the DDL statements in a separate transaction, so start + // one here and commit it before using the new table below. + sql.begin(); + sql << "create table soci_test(" + "name varchar(100) not null primary key, " + "age integer not null" + ")"; + sql.commit(); + } + } table_creator(sql); + + SECTION("literal SQL queries appear in the error message") + { + sql << "insert into soci_test(name, age) values ('John', 74)"; + sql << "insert into soci_test(name, age) values ('Paul', 72)"; + sql << "insert into soci_test(name, age) values ('George', 72)"; + + try + { + // Oops, this should have been 'Ringo' + sql << "insert into soci_test(name, age) values ('John', 74)"; + + FAIL("exception expected on unique constraint violation not thrown"); + } + catch (soci_error const &e) + { + std::string const msg = e.what(); + CAPTURE(msg); + + CHECK(msg.find("John") != std::string::npos); + } + } + + SECTION("SQL queries parameters appear in the error message") + { + char const* const names[] = { "John", "Paul", "George", "John", NULL }; + int const ages[] = { 74, 72, 72, 74, 0 }; + + std::string name; + int age; + + statement st = (sql.prepare << + "insert into soci_test(name, age) values (:name, :age)", + use(name), use(age)); + try + { + int const *a = ages; + for (char const* const* n = names; *n; ++n, ++a) + { + name = *n; + age = *a; + st.execute(true); + } + + FAIL("exception expected on unique constraint violation with prepared statement not thrown"); + } + catch (soci_error const &e) + { + std::string const msg = e.what(); + CAPTURE(msg); + + CHECK(msg.find("John") != std::string::npos); + } + } +} + +namespace +{ + +// This is just a helper to avoid duplicating the same code in two sections in +// the test below, it's logically part of it. +void check_for_exception_on_truncation(session& sql) +{ + // As the name column has length 20, inserting a longer string into it + // shouldn't work, unless we're dealing with a database that doesn't + // respect column types at all (hello SQLite). + try + { + std::string const long_name("George Raymond Richard Martin"); + sql << "insert into soci_test(name) values(:name)", use(long_name); + + // If insert didn't throw, it should have at least preserved the data + // (only SQLite does this currently). + std::string name; + sql << "select name from soci_test", into(name); + CHECK(name == long_name); + } + catch (soci_error const &) + { + // Unfortunately the contents of the message differ too much between + // the backends (most give an error about value being "too long", + // Oracle says "too large" while SQL Server (via ODBC) just says that + // it "would be truncated"), so we can't really check that we received + // the right error here -- be optimistic and hope that we did. + } +} + +// And another helper for the test below. +void check_for_no_truncation(session& sql, bool with_padding) +{ + const std::string str20 = "exactly of length 20"; + + sql << "delete from soci_test"; + + // Also check that there is no truncation when inserting a string of + // the same length as the column size. + CHECK_NOTHROW( (sql << "insert into soci_test(name) values(:s)", use(str20)) ); + + std::string s; + sql << "select name from soci_test", into(s); + + // Firebird can pad CHAR(N) columns when using UTF-8 encoding. + // the result will be padded to 80 bytes (UTF-8 max for 20 chars) + if (with_padding) + CHECK_EQUAL_PADDED(s, str20) + else + CHECK( s == str20 ); +} + +} // anonymous namespace + +TEST_CASE_METHOD(common_tests, "Truncation error", "[core][insert][truncate][exception]") +{ + soci::session sql(backEndFactory_, connectString_); + + if (tc_.has_silent_truncate_bug(sql)) + { + WARN("Database is broken and silently truncates input data."); + return; + } + + SECTION("Error given for char column") + { + struct fixed_name_table_creator : table_creator_base + { + fixed_name_table_creator(session& sql) + : table_creator_base(sql) + { + sql << "create table soci_test(name char(20))"; + } + } tableCreator(sql); + + tc_.on_after_ddl(sql); + + check_for_exception_on_truncation(sql); + + // Firebird can pad CHAR(N) columns when using UTF-8 encoding. + check_for_no_truncation(sql, sql.get_backend_name() == "firebird"); + } + + SECTION("Error given for varchar column") + { + // Reuse one of the standard tables which has a varchar(20) column. + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + check_for_exception_on_truncation(sql); + + check_for_no_truncation(sql, false); + } +} + +TEST_CASE_METHOD(common_tests, "Blank padding", "[core][insert][exception]") +{ + soci::session sql(backEndFactory_, connectString_); + if (!tc_.enable_std_char_padding(sql)) + { + WARN("This backend doesn't pad CHAR(N) correctly, skipping test."); + return; + } + + struct fixed_name_table_creator : table_creator_base + { + fixed_name_table_creator(session& sql) + : table_creator_base(sql) + { + sql.begin(); + sql << "create table soci_test(sc char, name char(10), name2 varchar(10))"; + sql.commit(); + } + } tableCreator(sql); + + std::string test1 = "abcde "; + std::string singleChar = "a"; + sql << "insert into soci_test(sc, name,name2) values(:sc,:name,:name2)", + use(singleChar), use(test1), use(test1); + + std::string sc, tchar,tvarchar; + sql << "select sc,name,name2 from soci_test", + into(sc), into(tchar), into(tvarchar); + + // Firebird can pad "a" to "a " when using UTF-8 encoding. + CHECK_EQUAL_PADDED(sc, singleChar); + CHECK_EQUAL_PADDED(tchar, test1); + CHECK(tvarchar == test1); + + // Check 10-space string - same as inserting empty string since spaces will + // be padded up to full size of the column. + test1 = " "; + singleChar = " "; + sql << "update soci_test set sc=:sc, name=:name, name2=:name2", + use(singleChar), use(test1), use(test1); + sql << "select sc, name,name2 from soci_test", + into(sc), into(tchar), into(tvarchar); + + CHECK_EQUAL_PADDED(sc, singleChar); + CHECK_EQUAL_PADDED(tchar, test1); + CHECK(tvarchar == test1); +} + +TEST_CASE_METHOD(common_tests, "Select without table", "[core][select][dummy_from]") +{ + soci::session sql(backEndFactory_, connectString_); + + int plus17; + sql << ("select abs(-17)" + sql.get_dummy_from_clause()), + into(plus17); + + CHECK(plus17 == 17); +} + +TEST_CASE_METHOD(common_tests, "String length", "[core][string][length]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + std::string s("123"); + REQUIRE_NOTHROW(( + sql << "insert into soci_test(str) values(:s)", use(s) + )); + + std::string sout; + size_t slen; + REQUIRE_NOTHROW(( + sql << "select str," + tc_.sql_length("str") + " from soci_test", + into(sout), into(slen) + )); + CHECK(slen == 3); + CHECK(sout.length() == 3); + CHECK(sout == s); + + sql << "delete from soci_test"; + + + std::vector v; + v.push_back("Hello"); + v.push_back(""); + v.push_back("whole of varchar(20)"); + + REQUIRE_NOTHROW(( + sql << "insert into soci_test(str) values(:s)", use(v) + )); + + std::vector vout(10); + // Although none of the strings here is really null, Oracle handles the + // empty string as being null, so to avoid an error about not providing + // the indicator when retrieving a null value, we must provide it here. + std::vector vind(10); + std::vector vlen(10); + + REQUIRE_NOTHROW(( + sql << "select str," + tc_.sql_length("str") + " from soci_test" + " order by " + tc_.sql_length("str"), + into(vout, vind), into(vlen) + )); + + REQUIRE(vout.size() == 3); + REQUIRE(vlen.size() == 3); + + CHECK(vlen[0] == 0); + CHECK(vout[0].length() == 0); + + CHECK(vlen[1] == 5); + CHECK(vout[1].length() == 5); + + CHECK(vlen[2] == 20); + CHECK(vout[2].length() == 20); +} + +// Helper function used in some tests below. Generates an XML sample about +// approximateSize bytes long. +static std::string make_long_xml_string(int approximateSize = 5000) +{ + const int tagsSize = 6 + 7; + const int patternSize = 26; + const int patternsCount = approximateSize / patternSize + 1; + + std::string s; + s.reserve(tagsSize + patternsCount * patternSize); + + std::ostringstream ss; + ss << ""; + for (int i = 0; i != patternsCount; ++i) + { + ss << "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + } + ss << ""; + + return ss.str(); +} + +// The helper function to remove trailing \n from a given string. +// Used for XML strings, returned from the DB. +// The returned XML value doesn't need to be identical to the original one as +// string, only structurally equal as XML. In particular, extra whitespace +// can be added and this does happen with Oracle, for example, which adds +// an extra new line, so remove it if it's present. +static std::string remove_trailing_nl(std::string str) +{ + if (!str.empty() && *str.rbegin() == '\n') + { + str.resize(str.length() - 1); + } + + return str; +} + +TEST_CASE_METHOD(common_tests, "CLOB", "[core][clob]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_clob(sql)); + if (!tableCreator.get()) + { + WARN("CLOB type not supported by the database, skipping the test."); + return; + } + + long_string s1; // empty + sql << "insert into soci_test(id, s) values (1, :s)", use(s1); + + long_string s2; + s2.value = "hello"; + sql << "select s from soci_test where id = 1", into(s2); + + CHECK(s2.value.size() == 0); + + s1.value = make_long_xml_string(); + + sql << "update soci_test set s = :s where id = 1", use(s1); + + sql << "select s from soci_test where id = 1", into(s2); + + CHECK(s2.value == s1.value); + + // Check that trailing new lines are preserved. + s1.value = "multi\nline\nstring\n\n"; + sql << "update soci_test set s = :s where id = 1", use(s1); + sql << "select s from soci_test where id = 1", into(s2); + CHECK(tc_.fix_crlf_if_necessary(s2.value) == s1.value); +} + +TEST_CASE_METHOD(common_tests, "CLOB vector", "[core][clob][vector]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_clob(sql)); + if (!tableCreator.get()) + { + WARN("CLOB type not supported by the database, skipping the test."); + return; + } + + std::vector ids(2); + ids[0] = 1; + ids[1] = 2; + std::vector s1(2); // empty values + sql << "insert into soci_test(id, s) values (:id, :s)", use(ids), use(s1); + + std::vector s2(2); + s2[0].value = "hello_1"; + s2[1].value = "hello_2"; + sql << "select s from soci_test", into(s2); + + REQUIRE(s2.size() == 2); + CHECK(s2[0].value.empty()); + CHECK(s2[1].value.empty()); + + s1[0].value = make_long_xml_string(); + s1[1].value = make_long_xml_string(10000); + + sql << "update soci_test set s = :s where id = :id", use(s1), use(ids); + + sql << "select s from soci_test", into(s2); + + REQUIRE(s2.size() == 2); + CHECK(s2[0].value == s1[0].value); + CHECK(s2[1].value == s1[1].value); +} + +TEST_CASE_METHOD(common_tests, "XML", "[core][xml]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_xml(sql)); + if (!tableCreator.get()) + { + WARN("XML type not supported by the database, skipping the test."); + return; + } + + int id = 1; + xml_type xml; + xml.value = make_long_xml_string(); + + sql << "insert into soci_test (id, x) values (:1, " + << tc_.to_xml(":2") + << ")", + use(id), use(xml); + + xml_type xml2; + + sql << "select " + << tc_.from_xml("x") + << " from soci_test where id = :1", + into(xml2), use(id); + + CHECK(xml.value == remove_trailing_nl(xml2.value)); + + sql << "update soci_test set x = null where id = :1", use(id); + + indicator ind; + sql << "select " + << tc_.from_xml("x") + << " from soci_test where id = :1", + into(xml2, ind), use(id); + + CHECK(ind == i_null); + + // Inserting malformed XML into an XML column must fail but some backends + // (e.g. Firebird) don't have real XML support, so exclude them from this + // test. + if (tc_.has_real_xml_support()) + { + xml.value = ""; + CHECK_THROWS_AS( + (sql << "insert into soci_test(id, x) values (2, " + + tc_.to_xml(":1") + ")", + use(xml) + ), soci_error + ); + } +} + +// Tha same test as above, but using vectors of xml_type values. +TEST_CASE_METHOD(common_tests, "XML vector", "[core][xml][vector]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_xml(sql)); + if (!tableCreator.get()) + { + WARN("XML type not supported by the database, skipping the test."); + return; + } + + std::vector id(2); + id[0] = 1; + id[1] = 1; // Use the same ID to select both objects by ID. + std::vector xml(2); + xml[0].value = make_long_xml_string(); + // Check long strings handling. + xml[1].value = make_long_xml_string(10000); + + sql << "insert into soci_test (id, x) values (:1, " + << tc_.to_xml(":2") + << ")", + use(id), use(xml); + + std::vector xml2(2); + + sql << "select " + << tc_.from_xml("x") + << " from soci_test where id = :1", + into(xml2), use(id.at(0)); + + CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); + CHECK(xml.at(1).value == remove_trailing_nl(xml2.at(1).value)); + + sql << "update soci_test set x = null where id = :1", use(id.at(0)); + + std::vector ind(2); + sql << "select " + << tc_.from_xml("x") + << " from soci_test where id = :1", + into(xml2, ind), use(id.at(0)); + + CHECK(ind.at(0) == i_null); + CHECK(ind.at(1) == i_null); +} + +TEST_CASE_METHOD(common_tests, "XML and int vectors", "[core][xml][vector]") +{ + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_xml(sql)); + if (!tableCreator.get()) + { + WARN("XML type not supported by the database, skipping the test."); + return; + } + + std::vector id(3); + id[0] = 0; + id[1] = 1; + id[2] = 2; + std::vector xml(3); + std::vector ind(3); + xml[0].value = make_long_xml_string(); + ind[0] = i_ok; + ind[1] = i_null; + // Check long strings handling. + xml[2].value = make_long_xml_string(10000); + ind[2] = i_ok; + + sql << "insert into soci_test (id, x) values (:1, " + << tc_.to_xml(":2") + << ")", + use(id), use(xml, ind); + + std::vector id2(3); + std::vector xml2(3); + std::vector ind2(3); + + sql << "select id, " + << tc_.from_xml("x") + << " from soci_test order by id", + into(id2), into(xml2, ind2); + + CHECK(id.at(0) == id2.at(0)); + CHECK(id.at(1) == id2.at(1)); + CHECK(id.at(2) == id2.at(2)); + + CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); + CHECK(xml.at(2).value == remove_trailing_nl(xml2.at(2).value)); + + CHECK(ind.at(0) == ind2.at(0)); + CHECK(ind.at(1) == ind2.at(1)); + CHECK(ind.at(2) == ind2.at(2)); +} + +TEST_CASE_METHOD(common_tests, "Into XML vector with several fetches", "[core][xml][into][vector][statement]") +{ + int stringSize = 0; + SECTION("short string") + { + stringSize = 100; + } + SECTION("long string") + { + stringSize = 10000; + } + + // Skip the rest when not executing the current section. + if (!stringSize) + return; + + soci::session sql(backEndFactory_, connectString_); + + auto_table_creator tableCreator(tc_.table_creator_xml(sql)); + if (!tableCreator.get()) + { + WARN("XML type not supported by the database, skipping the test."); + return; + } + + int const count = 5; + std::vector values(count); + for (int i = 0; i != count; ++i) + values[i].value = make_long_xml_string(stringSize + i*100); + + sql << "insert into soci_test (x) values (" + << tc_.to_xml(":2") + << ")", + use(values); + + std::vector result(3); + soci::statement st = (sql.prepare << + "select " << tc_.from_xml("x") << " from soci_test", into(result)); + + st.execute(true); + REQUIRE(result.size() == 3); + CHECK(remove_trailing_nl(result[0].value) == values[0].value); + CHECK(remove_trailing_nl(result[1].value) == values[1].value); + CHECK(remove_trailing_nl(result[2].value) == values[2].value); + + REQUIRE(st.fetch()); + REQUIRE(result.size() == 2); + CHECK(remove_trailing_nl(result[0].value) == values[3].value); + CHECK(remove_trailing_nl(result[1].value) == values[4].value); + + REQUIRE(!st.fetch()); +} + +TEST_CASE_METHOD(common_tests, "Logger", "[core][log]") +{ + // Logger class used for testing: appends all queries to the provided + // buffer. + class test_log_impl : public soci::logger_impl + { + public: + explicit test_log_impl(std::vector& logbuf) + : m_logbuf(logbuf) + { + } + + virtual void start_query(std::string const & query) + { + m_logbuf.push_back(query); + } + + private: + virtual logger_impl* do_clone() const + { + return new test_log_impl(m_logbuf); + } + + std::vector& m_logbuf; + }; + + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + soci::logger const logger_orig = sql.get_logger(); + + std::vector logbuf; + sql.set_logger(new test_log_impl(logbuf)); + + int count; + sql << "select count(*) from soci_test", into(count); + + REQUIRE( logbuf.size() == 1 ); + CHECK( logbuf.front() == "select count(*) from soci_test" ); + + sql.set_logger(logger_orig); +} + +// These tests are disabled by default, as they require manual intevention, but +// can be run by explicitly giving their names on the command line. + +// Check if reconnecting to the database after losing connection to it works. +TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]") +{ + soci::session sql(backEndFactory_, connectString_); + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + int id = 17; + sql << "insert into soci_test (id) values (:id)", use(id); + + REQUIRE_NOTHROW( sql.commit() ); + CHECK( sql.is_connected() ); + + std::cout << "Please break connection to the database " + "(stop the server, unplug the network cable, ...) " + "and press Enter" << std::endl; + std::cin.get(); + + try + { + CHECK( !sql.is_connected() ); + + int id2; + sql << "select id from soci_test", into(id2); + + FAIL("Connection to the database still available"); + return; + } + catch (soci_error const& e) + { + if ( sql.get_backend_name() == "odbc" || + e.get_error_category() == soci_error::unknown ) + { + WARN( "Skipping error check because ODBC driver returned " + "unknown error: " << e.what() ); + } + else + { + INFO( "Exception message: " << e.what() ); + CHECK( e.get_error_category() == soci_error::connection_error ); + } + } + + std::cout << "Please undo the previous action " + "(restart the server, plug the cable back, ...) " + "and press Enter" << std::endl; + std::cin.get(); + + REQUIRE_NOTHROW( sql.reconnect() ); + CHECK( sql.is_connected() ); + + int id2 = 1234; + sql << "select id from soci_test", into(id2); + CHECK( id2 == id ); +} + +// Check if automatically reconnecting to the database works. +// +// Note: this test doesn't work at all, failover doesn't happen neither with +// Oracle nor with PostgreSQL (which are the only backends for which it's +// implemented at all) and it's not clear how is it even supposed to work. +TEST_CASE_METHOD(common_tests, "Failover", "[keep-alive][.]") +{ + soci::session sql(backEndFactory_, connectString_); + + class MyCallback : public soci::failover_callback + { + public: + MyCallback() : attempted_(false), reconnected_(false) + { + } + + bool did_reconnect() const { return reconnected_; } + + void started() override + { + std::cout << "Please undo the previous action " + "(restart the server, plug the cable back, ...) " + "and press Enter" << std::endl; + std::cin.get(); + } + + void failed(bool& retry, std::string&) override + { + // We only retry once. + retry = !attempted_; + attempted_ = true; + } + + void finished(soci::session&) override + { + reconnected_ = true; + } + + void aborted() override + { + FAIL( "Failover aborted" ); + } + + private: + bool attempted_; + bool reconnected_; + } myCallback; + + sql.set_failover_callback(myCallback); + + auto_table_creator tableCreator(tc_.table_creator_1(sql)); + + int id = 17; + sql << "insert into soci_test (id) values (:id)", use(id); + REQUIRE_NOTHROW( sql.commit() ); + + std::cout << "Please break connection to the database " + "(stop the server, unplug the network cable, ...) " + "and press Enter" << std::endl; + std::cin.get(); + + int id2; + sql << "select id from soci_test", into(id2); + CHECK( id2 == id ); + + CHECK( myCallback.did_reconnect() ); +} + +} // namespace test_cases + + +std::ostream& operator<<(std::ostream& ostr, const MyOptionalString& optstr) +{ + ostr << (optstr.is_valid() ? "\"" + optstr.get() + "\"" : std::string("(null)")); + + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const std::vector& vec) +{ + if ( vec.empty() ) + { + ostr << "Empty vector"; + } + else + { + ostr << "Vector of size " << vec.size() << " containing\n"; + for ( size_t n = 0; n < vec.size(); ++n ) + { + ostr << "\t [" << std::setw(3) << n << "] = " << vec[n] << "\n"; + } + } + + return ostr; +} + +bool are_doubles_approx_equal(double const a, double const b) +{ + // The formula taken from CATCH test framework + // https://github.com/philsquared/Catch/ + // Thanks to Richard Harris for his help refining this formula + double const epsilon(std::numeric_limits::epsilon() * 100); + double const scale(1.0); + return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b))); +} + +bool are_doubles_exactly_equal(double a, double b) +{ + // Avoid g++ warnings: we do really want the exact equality here. + SOCI_GCC_WARNING_SUPPRESS(float-equal) + + return a == b; + + SOCI_GCC_WARNING_RESTORE(float-equal) +} + +bool are_doubles_equal(test_context_base const& tc, double a, double b) +{ + return tc.has_fp_bug() + ? are_doubles_approx_equal(a, b) + : are_doubles_exactly_equal(a, b); +} + +void checkEqualPadded(const std::string& padded_str, const std::string& expected_str) +{ + size_t const len = expected_str.length(); + std::string const start_str(padded_str, 0, len); + + if (start_str != expected_str) + { + throw soci::soci_error( + "Expected string \"" + expected_str + "\" " + "is different from the padded string \"" + padded_str + "\"" + ); + } + + if (padded_str.length() > len) + { + std::string const end_str(padded_str, len); + if (end_str != std::string(padded_str.length() - len, ' ')) + { + throw soci::soci_error( + "\"" + padded_str + "\" starts with \"" + padded_str + + "\" but non-space characater(s) are found aftewards" + ); + } + } +} + +} // namespace tests + +} // namespace soci + + +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 assert()-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 + + std::string connectString; + + if (argc >= 2 && argv[1][0] != '-') + { + connectString = argv[1]; + + // Replace the connect string with the process name to ensure that + // CATCH uses the correct name in its messages. + argv[1] = argv[0]; + + argc--; + argv++; + } + + const soci::backend_factory &factory = soci::tests::create_backend_factory(); + std::unique_ptr ctx = soci::tests::instantiate_test_context(factory, connectString); + + return Catch::Session().run(argc, argv); +} + diff --git a/tests/common-tests.h b/tests/common-tests.h index 139bd2ba6..a1c692249 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -1,14 +1,17 @@ -// -// Copyright (C) 2004-2008 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_COMMON_TESTS_H_ +#define SOCI_COMMON_TESTS_H_ + +#include -#ifndef SOCI_COMMON_TESTS_H_INCLUDED -#define SOCI_COMMON_TESTS_H_INCLUDED +#include +#include -#include "soci/soci.h" +#include +#include +#include +#include +#include +#include #ifdef SOCI_HAVE_BOOST // explicitly pull conversions for Boost's optional, tuple and fusion: @@ -24,272 +27,12 @@ #include "soci/std-optional.h" #endif -#include "soci-compiler.h" - -#include "soci/callbacks.h" - -#define CATCH_CONFIG_RUNNER -#include #if defined(_MSC_VER) && (_MSC_VER < 1500) #undef SECTION #define SECTION(name) INTERNAL_CATCH_SECTION(name, "dummy-for-vc8") #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "soci-mktime.h" - -// Although SQL standard mandates right padding CHAR(N) values to their length -// with spaces, some backends don't confirm to it: -// -// - Firebird does pad the string but to the byte-size (not character size) of -// the column (i.e. CHAR(10) NONE is padded to 10 bytes but CHAR(10) UTF8 -- -// to 40). -// - For MySql PAD_CHAR_TO_FULL_LENGTH option must be set, otherwise the value -// is trimmed. -// - SQLite never behaves correctly at all. -// -// This method will check result string from column defined as fixed char It -// will check only bytes up to the original string size. If padded string is -// bigger than expected string then all remaining chars must be spaces so if -// any non-space character is found it will fail. -void -checkEqualPadded(const std::string& padded_str, const std::string& expected_str) -{ - size_t const len = expected_str.length(); - std::string const start_str(padded_str, 0, len); - - if (start_str != expected_str) - { - throw soci::soci_error( - "Expected string \"" + expected_str + "\" " - "is different from the padded string \"" + padded_str + "\"" - ); - } - - if (padded_str.length() > len) - { - std::string const end_str(padded_str, len); - if (end_str != std::string(padded_str.length() - len, ' ')) - { - throw soci::soci_error( - "\"" + padded_str + "\" starts with \"" + padded_str + - "\" but non-space characater(s) are found aftewards" - ); - } - } -} - -#define CHECK_EQUAL_PADDED(padded_str, expected_str) \ - CHECK_NOTHROW(checkEqualPadded(padded_str, expected_str)); - -// Objects used later in tests 14,15 -struct PhonebookEntry -{ - std::string name; - std::string phone; -}; - -struct PhonebookEntry2 : public PhonebookEntry -{ -}; - -class PhonebookEntry3 -{ -public: - void setName(std::string const & n) { name_ = n; } - std::string getName() const { return name_; } - - void setPhone(std::string const & p) { phone_ = p; } - std::string getPhone() const { return phone_; } - -public: - std::string name_; - std::string phone_; -}; - -// user-defined object for test26 and test28 -class MyInt -{ -public: - MyInt() : i_() {} - MyInt(int i) : i_(i) {} - void set(int i) { i_ = i; } - int get() const { return i_; } -private: - int i_; -}; - -// user-defined object for the "vector of custom type objects" tests. -class MyOptionalString -{ -public: - MyOptionalString() : valid_(false) {} - MyOptionalString(const std::string& str) : valid_(true), str_(str) {} - void set(const std::string& str) { valid_ = true; str_ = str; } - void reset() { valid_ = false; str_.clear(); } - bool is_valid() const { return valid_; } - const std::string &get() const { return str_; } -private: - bool valid_; - std::string str_; -}; - -std::ostream& operator<<(std::ostream& ostr, const MyOptionalString& optstr) -{ - ostr << (optstr.is_valid() ? "\"" + optstr.get() + "\"" : std::string("(null)")); - - return ostr; -} - -std::ostream& operator<<(std::ostream& ostr, const std::vector& vec) -{ - if ( vec.empty() ) - { - ostr << "Empty vector"; - } - else - { - ostr << "Vector of size " << vec.size() << " containing\n"; - for ( size_t n = 0; n < vec.size(); ++n ) - { - ostr << "\t [" << std::setw(3) << n << "] = " << vec[n] << "\n"; - } - } - - return ostr; -} - -namespace soci -{ - -// basic type conversion for user-defined type with single base value -template<> struct type_conversion -{ - typedef int base_type; - - static void from_base(int i, indicator ind, MyInt &mi) - { - if (ind == i_ok) - { - mi.set(i); - } - } - - static void to_base(MyInt const &mi, int &i, indicator &ind) - { - i = mi.get(); - ind = i_ok; - } -}; - -// basic type conversion for string based user-defined type which can be null -template<> struct type_conversion -{ - typedef std::string base_type; - - static void from_base(const base_type& in, indicator ind, MyOptionalString& out) - { - if (ind == i_null) - { - out.reset(); - } - else - { - out.set(in); - } - } - - static void to_base(const MyOptionalString& in, base_type& out, indicator& ind) - { - if (in.is_valid()) - { - out = in.get(); - ind = i_ok; - } - else - { - ind = i_null; - } - } -}; - -// basic type conversion on many values (ORM) -template<> struct type_conversion -{ - typedef soci::values base_type; - - static void from_base(values const &v, indicator /* ind */, PhonebookEntry &pe) - { - // here we ignore the possibility the the whole object might be NULL - pe.name = v.get("NAME"); - pe.phone = v.get("PHONE", ""); - } - - static void to_base(PhonebookEntry const &pe, values &v, indicator &ind) - { - v.set("NAME", pe.name); - v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); - ind = i_ok; - } -}; - -// type conversion which directly calls values::get_indicator() -template<> struct type_conversion -{ - typedef soci::values base_type; - - static void from_base(values const &v, indicator /* ind */, PhonebookEntry2 &pe) - { - // here we ignore the possibility the the whole object might be NULL - - pe.name = v.get("NAME"); - indicator ind = v.get_indicator("PHONE"); //another way to test for null - pe.phone = ind == i_null ? "" : v.get("PHONE"); - } - - static void to_base(PhonebookEntry2 const &pe, values &v, indicator &ind) - { - v.set("NAME", pe.name); - v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); - ind = i_ok; - } -}; - -template<> struct type_conversion -{ - typedef soci::values base_type; - - static void from_base(values const &v, indicator /* ind */, PhonebookEntry3 &pe) - { - // here we ignore the possibility the the whole object might be NULL - - pe.setName(v.get("NAME")); - pe.setPhone(v.get("PHONE", "")); - } - - static void to_base(PhonebookEntry3 const &pe, values &v, indicator &ind) - { - v.set("NAME", pe.getName()); - v.set("PHONE", pe.getPhone(), pe.getPhone().empty() ? i_null : i_ok); - ind = i_ok; - } -}; - -} // namespace soci - namespace soci { @@ -594,7 +337,7 @@ class test_context_base virtual ~test_context_base() { - the_test_context_ = NULL; + the_test_context_ = nullptr; } private: @@ -606,58 +349,114 @@ class test_context_base SOCI_NOT_COPYABLE(test_context_base) }; -// Currently all tests consist of just a single source file, so we can define -// this member here because this header is included exactly once. -tests::test_context_base* tests::test_context_base::the_test_context_ = NULL; - +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string); +const backend_factory &create_backend_factory(); -// Compare doubles for approximate equality. This has to be used everywhere -// where we write "3.14" (or "6.28") to the database as a string and then -// compare the value read back with the literal 3.14 floating point constant -// because they are not the same. +// Although SQL standard mandates right padding CHAR(N) values to their length +// with spaces, some backends don't confirm to it: // -// It is also used for the backends which currently don't handle doubles -// correctly. +// - Firebird does pad the string but to the byte-size (not character size) of +// the column (i.e. CHAR(10) NONE is padded to 10 bytes but CHAR(10) UTF8 -- +// to 40). +// - For MySql PAD_CHAR_TO_FULL_LENGTH option must be set, otherwise the value +// is trimmed. +// - SQLite never behaves correctly at all. // -// Notice that this function is normally not used directly but rather from the -// macro below. -inline bool are_doubles_approx_equal(double const a, double const b) -{ - // The formula taken from CATCH test framework - // https://github.com/philsquared/Catch/ - // Thanks to Richard Harris for his help refining this formula - double const epsilon(std::numeric_limits::epsilon() * 100); - double const scale(1.0); - return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b))); -} - -// This is a macro to ensure we use the correct line numbers. The weird -// do/while construction is used to make this a statement and the even weirder -// condition in while ensures that the loop is executed exactly once without -// triggering warnings from MSVC about the condition being always false. -#define ASSERT_EQUAL_APPROX(a, b) \ - do { \ - if (!are_doubles_approx_equal((a), (b))) { \ - FAIL( "Approximate equality check failed: " \ - << std::fixed \ - << std::setprecision(std::numeric_limits::digits10 + 1) \ - << (a) << " != " << (b) ); \ - } \ - } while ( (void)0, 0 ) +// This method will check result string from column defined as fixed char It +// will check only bytes up to the original string size. If padded string is +// bigger than expected string then all remaining chars must be spaces so if +// any non-space character is found it will fail. +void checkEqualPadded(const std::string& padded_str, const std::string& expected_str); +#define CHECK_EQUAL_PADDED(padded_str, expected_str) \ + CHECK_NOTHROW(checkEqualPadded(padded_str, expected_str)); -// Exact double comparison function. We need one, instead of writing "a == b", -// only in order to have some place to put the pragmas disabling gcc warnings. -inline bool -are_doubles_exactly_equal(double a, double b) +// Objects used later in tests 14,15 +struct PhonebookEntry +{ + std::string name; + std::string phone; +}; + +struct PhonebookEntry2 : public PhonebookEntry { - // Avoid g++ warnings: we do really want the exact equality here. - SOCI_GCC_WARNING_SUPPRESS(float-equal) +}; - return a == b; +class PhonebookEntry3 +{ +public: + void setName(std::string const & n) { name_ = n; } + std::string getName() const { return name_; } - SOCI_GCC_WARNING_RESTORE(float-equal) -} + void setPhone(std::string const & p) { phone_ = p; } + std::string getPhone() const { return phone_; } + +public: + std::string name_; + std::string phone_; +}; + +// user-defined object for test26 and test28 +class MyInt +{ +public: + MyInt() : i_() {} + MyInt(int i) : i_(i) {} + void set(int i) { i_ = i; } + int get() const { return i_; } +private: + int i_; +}; + +// user-defined object for the "vector of custom type objects" tests. +class MyOptionalString +{ +public: + MyOptionalString() : valid_(false) {} + MyOptionalString(const std::string& str) : valid_(true), str_(str) {} + void set(const std::string& str) { valid_ = true; str_ = str; } + void reset() { valid_ = false; str_.clear(); } + bool is_valid() const { return valid_; } + const std::string &get() const { return str_; } +private: + bool valid_; + std::string str_; +}; + +std::ostream& operator<<(std::ostream& ostr, const MyOptionalString& optstr); + +std::ostream& operator<<(std::ostream& ostr, const std::vector& vec); + +// Compare doubles for approximate equality. This has to be used everywhere +// where we write "3.14" (or "6.28") to the database as a string and then +// compare the value read back with the literal 3.14 floating point constant +// because they are not the same. +// +// It is also used for the backends which currently don't handle doubles +// correctly. +// +// Notice that this function is normally not used directly but rather from the +// macro below. +bool are_doubles_approx_equal(double const a, double const b); + +// This is a macro to ensure we use the correct line numbers. The weird +// do/while construction is used to make this a statement and the even weirder +// condition in while ensures that the loop is executed exactly once without +// triggering warnings from MSVC about the condition being always false. +#define ASSERT_EQUAL_APPROX(a, b) \ + do { \ + if (!are_doubles_approx_equal((a), (b))) { \ + FAIL( "Approximate equality check failed: " \ + << std::fixed \ + << std::setprecision(std::numeric_limits::digits10 + 1) \ + << (a) << " != " << (b) ); \ + } \ + } while ( (void)0, 0 ) + + +// Exact double comparison function. We need one, instead of writing "a == b", +// only in order to have some place to put the pragmas disabling gcc warnings. +bool are_doubles_exactly_equal(double a, double b); #define ASSERT_EQUAL_EXACT(a, b) \ do { \ @@ -672,13 +471,7 @@ are_doubles_exactly_equal(double a, double b) // Compare two floating point numbers either exactly or approximately depending // on test_context::has_fp_bug() return value. -inline bool -are_doubles_equal(test_context_base const& tc, double a, double b) -{ - return tc.has_fp_bug() - ? are_doubles_approx_equal(a, b) - : are_doubles_exactly_equal(a, b); -} +bool are_doubles_equal(test_context_base const& tc, double a, double b); // This macro should be used when where we don't have any problems with string // literals vs floating point literals mismatches described above and would @@ -699,6437 +492,125 @@ are_doubles_equal(test_context_base const& tc, double a, double b) } \ } while ( (void)0, 0 ) - -class common_tests -{ -public: - common_tests() - : tc_(test_context_base::get_instance()), - backEndFactory_(tc_.get_backend_factory()), - connectString_(tc_.get_connect_string()) - {} - -protected: - test_context_base const & tc_; - backend_factory const &backEndFactory_; - std::string const connectString_; - - SOCI_NOT_COPYABLE(common_tests) -}; - -using auto_table_creator = std::unique_ptr; - -// Define the test cases in their own namespace to avoid clashes with the test -// cases defined in individual backend tests: as only line number is used for -// building the name of the "anonymous" function by the TEST_CASE macro, we -// could have a conflict between a test defined here and in some backend if -// they happened to start on the same line. -namespace test_cases -{ - -inline bool operator== ( const std::tm& a, const std::tm& b ) -{ - return a.tm_sec == b.tm_sec && a.tm_min == b.tm_min && a.tm_hour == b.tm_hour && a.tm_mday == b.tm_mday && a.tm_mon == b.tm_mon && - a.tm_year == b.tm_year && a.tm_wday == b.tm_wday && a.tm_yday == b.tm_yday && a.tm_isdst == b.tm_isdst; -} - -TEST_CASE_METHOD ( common_tests, "timegm implementation", "[core][timegm]" ) -{ - std::tm t1; - t1.tm_year = 105; - t1.tm_mon = 13; // + 1 year - t1.tm_mday = 15; - t1.tm_hour = 28; // + 1 day - t1.tm_min = 14; - t1.tm_sec = 17; - - std::tm t2 = t1; - - const auto timegm_result = timegm (&t1); - const auto timegm_soci_result = details::timegm_impl_soci (&t2); - CHECK ( timegm_result == timegm_soci_result ); - CHECK ( t1.tm_year == 106 ); - CHECK ( t1.tm_mon == 1 ); - CHECK ( t1.tm_mday == 16 ); - CHECK ( t1.tm_hour == 4 ); - CHECK ( ( t1 == t2 ) ); } - -TEST_CASE_METHOD(common_tests, "Exception on not connected", "[core][exception]") -{ - soci::session sql; // no connection - - // ensure connection is checked, no crash occurs - CHECK_THROWS_AS(sql.begin(), soci_error); - CHECK_THROWS_AS(sql.commit(), soci_error); - CHECK_THROWS_AS(sql.rollback(), soci_error); - CHECK_THROWS_AS(sql.get_backend_name(), soci_error); - CHECK_THROWS_AS(sql.make_statement_backend(), soci_error); - CHECK_THROWS_AS(sql.make_rowid_backend(), soci_error); - CHECK_THROWS_AS(sql.make_blob_backend(), soci_error); - - std::string s; - long long l; - CHECK_THROWS_AS(sql.get_next_sequence_value(s, l), soci_error); - CHECK_THROWS_AS(sql.get_last_insert_id(s, l), soci_error); } -TEST_CASE_METHOD(common_tests, "Basic functionality", "[core][basics]") +namespace soci { - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - CHECK_THROWS_AS(sql << "drop table soci_test_nosuchtable", soci_error); - sql << "insert into soci_test (id) values (" << 123 << ")"; - int id; - sql << "select id from soci_test", into(id); - CHECK(id == 123); - - sql << "insert into soci_test (id) values (" << 234 << ")"; - sql << "insert into soci_test (id) values (" << 345 << ")"; - // Test prepare, execute, fetch correctness - statement st = (sql.prepare << "select id from soci_test", into(id)); - st.execute(); - int count = 0; - while(st.fetch()) - count++; - CHECK(count == 3 ); - bool fetchEnd = st.fetch(); // All the data has been read here so additional fetch must return false - CHECK(fetchEnd == false); -} - -// "into" tests, type conversions, etc. -TEST_CASE_METHOD(common_tests, "Use and into", "[core][into]") +// basic type conversion for user-defined type with single base value +template<> struct type_conversion { - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - SECTION("Round trip works for char") - { - char c('a'); - sql << "insert into soci_test(c) values(:c)", use(c); - sql << "select c from soci_test", into(c); - CHECK(c == 'a'); - } - - SECTION("Round trip works for string") - { - std::string helloSOCI("Hello, SOCI!"); - sql << "insert into soci_test(str) values(:s)", use(helloSOCI); - std::string str; - sql << "select str from soci_test", into(str); - CHECK(str == "Hello, SOCI!"); - } - - SECTION("Round trip works for short") - { - short three(3); - sql << "insert into soci_test(sh) values(:id)", use(three); - short sh(0); - sql << "select sh from soci_test", into(sh); - CHECK(sh == 3); - } - - SECTION("Round trip works for int") - { - int five(5); - sql << "insert into soci_test(id) values(:id)", use(five); - int i(0); - sql << "select id from soci_test", into(i); - CHECK(i == 5); - } - - SECTION("Round trip works for unsigned long") - { - unsigned long seven(7); - sql << "insert into soci_test(ul) values(:ul)", use(seven); - unsigned long ul(0); - sql << "select ul from soci_test", into(ul); - CHECK(ul == 7); - } - - SECTION("Round trip works for double") - { - double pi(3.14159265); - sql << "insert into soci_test(d) values(:d)", use(pi); - double d(0.0); - sql << "select d from soci_test", into(d); - ASSERT_EQUAL(d, pi); - } - - SECTION("Round trip works for date without time") - { - std::tm nov15 = std::tm(); - nov15.tm_year = 105; - nov15.tm_mon = 10; - nov15.tm_mday = 15; - nov15.tm_hour = 0; - nov15.tm_min = 0; - nov15.tm_sec = 0; - - sql << "insert into soci_test(tm) values(:tm)", use(nov15); - - std::tm t = std::tm(); - sql << "select tm from soci_test", into(t); - CHECK(t.tm_year == 105); - CHECK(t.tm_mon == 10); - CHECK(t.tm_mday == 15); - CHECK(t.tm_hour == 0); - CHECK(t.tm_min == 0); - CHECK(t.tm_sec == 0); - } - - SECTION("Round trip works for date with time") - { - std::tm nov15 = std::tm(); - nov15.tm_year = 105; - nov15.tm_mon = 10; - nov15.tm_mday = 15; - nov15.tm_hour = 22; - nov15.tm_min = 14; - nov15.tm_sec = 17; - - sql << "insert into soci_test(tm) values(:tm)", use(nov15); - - std::tm t = std::tm(); - sql << "select tm from soci_test", into(t); - CHECK(t.tm_year == 105); - CHECK(t.tm_mon == 10); - CHECK(t.tm_mday == 15); - CHECK(t.tm_hour == 22); - CHECK(t.tm_min == 14); - CHECK(t.tm_sec == 17); - } + typedef int base_type; - SECTION("Indicator is filled correctly in the simplest case") + static void from_base(int i, indicator ind, soci::tests::MyInt &mi) { - int id(1); - std::string str("Hello"); - sql << "insert into soci_test(id, str) values(:id, :str)", - use(id), use(str); - - int i; - indicator ind; - sql << "select id from soci_test", into(i, ind); - CHECK(ind == i_ok); + if (ind == i_ok) + { + mi.set(i); + } } - SECTION("Indicators work correctly more generally") + static void to_base(soci::tests::MyInt const &mi, int &i, indicator &ind) { - sql << "insert into soci_test(id,tm) values(NULL,NULL)"; - int i; - indicator ind; - sql << "select id from soci_test", into(i, ind); - CHECK(ind == i_null); - - // additional test for NULL with std::tm - std::tm t = std::tm(); - sql << "select tm from soci_test", into(t, ind); - CHECK(ind == i_null); - - // indicator should be initialized even when nothing is found + i = mi.get(); ind = i_ok; - sql << "select id from soci_test where str='NO SUCH ROW'", - into(i, ind); - CHECK(ind == i_null); - - try - { - // expect error - sql << "select id from soci_test", into(i); - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "Null value fetched and no indicator defined."); - CHECK_THAT(e.what(), - Catch::Contains("for the parameter number 1")); - } - - sql << "select id from soci_test where id = 1000", into(i, ind); - CHECK(sql.got_data() == false); - - // no data expected - sql << "select id from soci_test where id = 1000", into(i); - CHECK(sql.got_data() == false); - - // no data expected, test correct behaviour with use - int id = 1000; - sql << "select id from soci_test where id = :id", use(id), into(i); - CHECK(sql.got_data() == false); } -} +}; -// repeated fetch and bulk fetch -TEST_CASE_METHOD(common_tests, "Repeated and bulk fetch", "[core][bulk]") +// basic type conversion for string based user-defined type which can be null +template<> struct type_conversion { - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); + typedef std::string base_type; - SECTION("char") + static void from_base(const base_type& in, indicator ind, soci::tests::MyOptionalString& out) { - char c; - for (c = 'a'; c <= 'z'; ++c) - { - sql << "insert into soci_test(c) values(\'" << c << "\')"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == 'z' - 'a' + 1); - - { - char c2 = 'a'; - - statement st = (sql.prepare << - "select c from soci_test order by c", into(c)); - - st.execute(); - while (st.fetch()) - { - CHECK(c == c2); - ++c2; - } - CHECK(c2 == 'a' + count); - } + if (ind == i_null) { - char c2 = 'a'; - - std::vector vec(10); - statement st = (sql.prepare << - "select c from soci_test order by c", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != vec.size(); ++i) - { - CHECK(c2 == vec[i]); - ++c2; - } - - vec.resize(10); - } - CHECK(c2 == 'a' + count); + out.reset(); } - + else { - // verify an exception is thrown when empty vector is used - std::vector vec; - try - { - sql << "select c from soci_test", into(vec); - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "Vectors of size 0 are not allowed."); - } + out.set(in); } - } - // repeated fetch and bulk fetch of std::string - SECTION("std::string") + static void to_base(const soci::tests::MyOptionalString& in, base_type& out, indicator& ind) { - int const rowsToTest = 10; - for (int i = 0; i != rowsToTest; ++i) + if (in.is_valid()) { - std::ostringstream ss; - ss << "Hello_" << i; - - sql << "insert into soci_test(str) values(\'" - << ss.str() << "\')"; + out = in.get(); + ind = i_ok; } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == rowsToTest); - + else { - int i = 0; - std::string s; - statement st = (sql.prepare << - "select str from soci_test order by str", into(s)); - - st.execute(); - while (st.fetch()) - { - std::ostringstream ss; - ss << "Hello_" << i; - CHECK(s == ss.str()); - ++i; - } - CHECK(i == rowsToTest); + ind = i_null; } - { - int i = 0; + } +}; - std::vector vec(4); - statement st = (sql.prepare << - "select str from soci_test order by str", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t j = 0; j != vec.size(); ++j) - { - std::ostringstream ss; - ss << "Hello_" << i; - CHECK(ss.str() == vec[j]); - ++i; - } +// basic type conversion on many values (ORM) +template<> struct type_conversion +{ + typedef soci::values base_type; - vec.resize(4); - } - CHECK(i == rowsToTest); - } + static void from_base(values const &v, indicator /* ind */, soci::tests::PhonebookEntry &pe) + { + // here we ignore the possibility the the whole object might be NULL + pe.name = v.get("NAME"); + pe.phone = v.get("PHONE", ""); } - SECTION("short") + static void to_base(soci::tests::PhonebookEntry const &pe, values &v, indicator &ind) { - short const rowsToTest = 100; - short sh; - for (sh = 0; sh != rowsToTest; ++sh) - { - sql << "insert into soci_test(sh) values(" << sh << ")"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == rowsToTest); - - { - short sh2 = 0; - - statement st = (sql.prepare << - "select sh from soci_test order by sh", into(sh)); + v.set("NAME", pe.name); + v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); + ind = i_ok; + } +}; - st.execute(); - while (st.fetch()) - { - CHECK(sh == sh2); - ++sh2; - } - CHECK(sh2 == rowsToTest); - } - { - short sh2 = 0; +// type conversion which directly calls values::get_indicator() +template<> struct type_conversion +{ + typedef soci::values base_type; - std::vector vec(8); - statement st = (sql.prepare << - "select sh from soci_test order by sh", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != vec.size(); ++i) - { - CHECK(sh2 == vec[i]); - ++sh2; - } + static void from_base(values const &v, indicator /* ind */, soci::tests::PhonebookEntry2 &pe) + { + // here we ignore the possibility the the whole object might be NULL - vec.resize(8); - } - CHECK(sh2 == rowsToTest); - } + pe.name = v.get("NAME"); + indicator ind = v.get_indicator("PHONE"); //another way to test for null + pe.phone = ind == i_null ? "" : v.get("PHONE"); } - SECTION("int") + static void to_base(soci::tests::PhonebookEntry2 const &pe, values &v, indicator &ind) { - int const rowsToTest = 100; - int i; - for (i = 0; i != rowsToTest; ++i) - { - sql << "insert into soci_test(id) values(" << i << ")"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == rowsToTest); - - { - int i2 = 0; - - statement st = (sql.prepare << - "select id from soci_test order by id", into(i)); - - st.execute(); - while (st.fetch()) - { - CHECK(i == i2); - ++i2; - } - CHECK(i2 == rowsToTest); - } - { - // additional test with the use element - - int i2 = 0; - int cond = 0; // this condition is always true - - statement st = (sql.prepare << - "select id from soci_test where id >= :cond order by id", - use(cond), into(i)); + v.set("NAME", pe.name); + v.set("PHONE", pe.phone, pe.phone.empty() ? i_null : i_ok); + ind = i_ok; + } +}; - st.execute(); - while (st.fetch()) - { - CHECK(i == i2); - ++i2; - } - CHECK(i2 == rowsToTest); - } - { - int i2 = 0; +template<> struct type_conversion +{ + typedef soci::values base_type; - std::vector vec(8); - statement st = (sql.prepare << - "select id from soci_test order by id", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t n = 0; n != vec.size(); ++n) - { - CHECK(i2 == vec[n]); - ++i2; - } + static void from_base(values const &v, indicator /* ind */, soci::tests::PhonebookEntry3 &pe) + { + // here we ignore the possibility the the whole object might be NULL - vec.resize(8); - } - CHECK(i2 == rowsToTest); - } + pe.setName(v.get("NAME")); + pe.setPhone(v.get("PHONE", "")); } - SECTION("unsigned int") + static void to_base(soci::tests::PhonebookEntry3 const &pe, values &v, indicator &ind) { - unsigned int const rowsToTest = 100; - unsigned int ul; - for (ul = 0; ul != rowsToTest; ++ul) - { - sql << "insert into soci_test(ul) values(" << ul << ")"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == static_cast(rowsToTest)); + v.set("NAME", pe.getName()); + v.set("PHONE", pe.getPhone(), pe.getPhone().empty() ? i_null : i_ok); + ind = i_ok; + } +}; - { - unsigned int ul2 = 0; - - statement st = (sql.prepare << - "select ul from soci_test order by ul", into(ul)); - - st.execute(); - while (st.fetch()) - { - CHECK(ul == ul2); - ++ul2; - } - CHECK(ul2 == rowsToTest); - } - { - unsigned int ul2 = 0; - - std::vector vec(8); - statement st = (sql.prepare << - "select ul from soci_test order by ul", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != vec.size(); ++i) - { - CHECK(ul2 == vec[i]); - ++ul2; - } - - vec.resize(8); - } - CHECK(ul2 == rowsToTest); - } - } - - SECTION("unsigned long long") - { - unsigned int const rowsToTest = 100; - unsigned long long ul; - for (ul = 0; ul != rowsToTest; ++ul) - { - sql << "insert into soci_test(ul) values(" << ul << ")"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == static_cast(rowsToTest)); - - { - unsigned long long ul2 = 0; - - statement st = (sql.prepare << - "select ul from soci_test order by ul", into(ul)); - - st.execute(); - while (st.fetch()) - { - CHECK(ul == ul2); - ++ul2; - } - CHECK(ul2 == rowsToTest); - } - { - unsigned long long ul2 = 0; - - std::vector vec(8); - statement st = (sql.prepare << - "select ul from soci_test order by ul", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != vec.size(); ++i) - { - CHECK(ul2 == vec[i]); - ++ul2; - } - - vec.resize(8); - } - CHECK(ul2 == rowsToTest); - } - } - - SECTION("double") - { - int const rowsToTest = 100; - double d = 0.0; - - statement sti = (sql.prepare << - "insert into soci_test(d) values(:d)", use(d)); - for (int i = 0; i != rowsToTest; ++i) - { - sti.execute(true); - d += 0.6; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == rowsToTest); - - { - double d2 = 0.0; - int i = 0; - - statement st = (sql.prepare << - "select d from soci_test order by d", into(d)); - - st.execute(); - while (st.fetch()) - { - ASSERT_EQUAL(d, d2); - d2 += 0.6; - ++i; - } - CHECK(i == rowsToTest); - } - { - double d2 = 0.0; - int i = 0; - - std::vector vec(8); - statement st = (sql.prepare << - "select d from soci_test order by d", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t j = 0; j != vec.size(); ++j) - { - ASSERT_EQUAL(d2, vec[j]); - d2 += 0.6; - ++i; - } - - vec.resize(8); - } - CHECK(i == rowsToTest); - } - } - - SECTION("std::tm") - { - int const rowsToTest = 8; - for (int i = 0; i != rowsToTest; ++i) - { - std::ostringstream ss; - ss << 2000 + i << "-0" << 1 + i << '-' << 20 - i << ' ' - << 15 + i << ':' << 50 - i << ':' << 40 + i; - - sql << "insert into soci_test(id, tm) values(" << i - << ", " << tc_.to_date_time(ss.str()) << ")"; - } - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == rowsToTest); - - { - std::tm t = std::tm(); - int i = 0; - - statement st = (sql.prepare << - "select tm from soci_test order by id", into(t)); - - st.execute(); - while (st.fetch()) - { - CHECK(t.tm_year == 2000 - 1900 + i); - CHECK(t.tm_mon == i); - CHECK(t.tm_mday == 20 - i); - CHECK(t.tm_hour == 15 + i); - CHECK(t.tm_min == 50 - i); - CHECK(t.tm_sec == 40 + i); - - ++i; - } - CHECK(i == rowsToTest); - } - { - int i = 0; - - std::vector vec(3); - statement st = (sql.prepare << - "select tm from soci_test order by id", into(vec)); - st.execute(); - while (st.fetch()) - { - for (std::size_t j = 0; j != vec.size(); ++j) - { - CHECK(vec[j].tm_year == 2000 - 1900 + i); - CHECK(vec[j].tm_mon == i); - CHECK(vec[j].tm_mday == 20 - i); - CHECK(vec[j].tm_hour == 15 + i); - CHECK(vec[j].tm_min == 50 - i); - CHECK(vec[j].tm_sec == 40 + i); - - ++i; - } - - vec.resize(3); - } - CHECK(i == rowsToTest); - } - } -} - -// test for indicators (repeated fetch and bulk) -TEST_CASE_METHOD(common_tests, "Indicators", "[core][indicator]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(id, val) values(1, 10)"; - sql << "insert into soci_test(id, val) values(2, 11)"; - sql << "insert into soci_test(id, val) values(3, NULL)"; - sql << "insert into soci_test(id, val) values(4, NULL)"; - sql << "insert into soci_test(id, val) values(5, 12)"; - - { - int val; - indicator ind; - - statement st = (sql.prepare << - "select val from soci_test order by id", into(val, ind)); - - st.execute(); - bool gotData = st.fetch(); - CHECK(gotData); - CHECK(ind == i_ok); - CHECK(val == 10); - gotData = st.fetch(); - CHECK(gotData); - CHECK(ind == i_ok); - CHECK(val == 11); - gotData = st.fetch(); - CHECK(gotData); - CHECK(ind == i_null); - gotData = st.fetch(); - CHECK(gotData); - CHECK(ind == i_null); - gotData = st.fetch(); - CHECK(gotData); - CHECK(ind == i_ok); - CHECK(val == 12); - gotData = st.fetch(); - CHECK(gotData == false); - } - { - std::vector vals(3); - std::vector inds(3); - - statement st = (sql.prepare << - "select val from soci_test order by id", into(vals, inds)); - - st.execute(); - bool gotData = st.fetch(); - CHECK(gotData); - CHECK(vals.size() == 3); - CHECK(inds.size() == 3); - CHECK(inds[0] == i_ok); - CHECK(vals[0] == 10); - CHECK(inds[1] == i_ok); - CHECK(vals[1] == 11); - CHECK(inds[2] == i_null); - gotData = st.fetch(); - CHECK(gotData); - CHECK(vals.size() == 2); - CHECK(inds[0] == i_null); - CHECK(inds[1] == i_ok); - CHECK(vals[1] == 12); - gotData = st.fetch(); - CHECK(gotData == false); - } - - // additional test for "no data" condition - { - std::vector vals(3); - std::vector inds(3); - - statement st = (sql.prepare << - "select val from soci_test where 0 = 1", into(vals, inds)); - - bool gotData = st.execute(true); - CHECK(gotData == false); - - // for convenience, vectors should be truncated - CHECK(vals.empty()); - CHECK(inds.empty()); - - // for even more convenience, fetch should not fail - // but just report end of rowset - // (and vectors should be truncated) - - vals.resize(1); - inds.resize(1); - - gotData = st.fetch(); - CHECK(gotData == false); - CHECK(vals.empty()); - CHECK(inds.empty()); - } - - // additional test for "no data" without prepared statement - { - std::vector vals(3); - std::vector inds(3); - - sql << "select val from soci_test where 0 = 1", - into(vals, inds); - - // vectors should be truncated - CHECK(vals.empty()); - CHECK(inds.empty()); - } - } - -} - -// test for different sizes of data vector and indicators vector -// (library should force ind. vector to have same size as data vector) -TEST_CASE_METHOD(common_tests, "Indicators vector", "[core][indicator][vector]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(id, str, val) values(1, 'ten', 10)"; - sql << "insert into soci_test(id, str, val) values(2, 'elf', 11)"; - sql << "insert into soci_test(id, str, val) values(3, NULL, NULL)"; - sql << "insert into soci_test(id, str, val) values(4, NULL, NULL)"; - sql << "insert into soci_test(id, str, val) values(5, 'xii', 12)"; - - { - std::vector vals(4); - std::vector inds; - - statement st = (sql.prepare << - "select val from soci_test order by id", into(vals, inds)); - - st.execute(); - st.fetch(); - CHECK(vals.size() == 4); - CHECK(inds.size() == 4); - vals.resize(3); - st.fetch(); - CHECK(vals.size() == 1); - CHECK(inds.size() == 1); - - std::vector strs(5); - sql << "select str from soci_test order by id", into(strs, inds); - REQUIRE(inds.size() == 5); - CHECK(inds[0] == i_ok); - CHECK(inds[1] == i_ok); - CHECK(inds[2] == i_null); - CHECK(inds[3] == i_null); - CHECK(inds[4] == i_ok); - - strs.resize(1); - sql << "select str from soci_test order by id", into(strs, inds); - CHECK(inds.size() == 1); - - strs.resize(1); - st = (sql.prepare << "select str from soci_test order by id", into(strs, inds)); - st.execute(); - st.fetch(); - CHECK(inds.size() == 1); - while (st.fetch()); - - std::vector ids(1); - sql << "select id from soci_test", into(ids); - CHECK(ids.size() == 1); - } - } - -} - -TEST_CASE_METHOD(common_tests, "Get last insert ID", "[core][get_last_insert_id]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_get_last_insert_id(sql)); - - // If the get_last_insert_id() supported by the backend. - if (!tableCreator.get()) - return; - - long long id; - REQUIRE(sql.get_last_insert_id("soci_test", id)); - // The initial value should be 1 and we call get_last_insert_id() before - // the first insert, so the "pre-initial value" is 0. - CHECK(id == 0); - - sql << "insert into soci_test(val) values(10)"; - - REQUIRE(sql.get_last_insert_id("soci_test", id)); - CHECK(id == 1); - - sql << "insert into soci_test(val) values(11)"; - - REQUIRE(sql.get_last_insert_id("soci_test", id)); - CHECK(id == 2); -} - -// "use" tests, type conversions, etc. -TEST_CASE_METHOD(common_tests, "Use type conversion", "[core][use]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - SECTION("char") - { - char c('a'); - sql << "insert into soci_test(c) values(:c)", use(c); - - c = 'b'; - sql << "select c from soci_test", into(c); - CHECK(c == 'a'); - - } - - SECTION("std::string") - { - std::string s = "Hello SOCI!"; - sql << "insert into soci_test(str) values(:s)", use(s); - - std::string str; - sql << "select str from soci_test", into(str); - - CHECK(str == "Hello SOCI!"); - } - - SECTION("int8_t") - { - int8_t i = 123; - sql << "insert into soci_test(id) values(:id)", use(i); - - int8_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == 123); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:id)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::max)(); - sql << "insert into soci_test(id) values(:id)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::max)()); - } - - SECTION("uint8_t") - { - uint8_t ui = 123; - sql << "insert into soci_test(id) values(:id)", use(ui); - - uint8_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 123); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:id)", use(ui); - - ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::max)(); - sql << "insert into soci_test(id) values(:id)", use(ui); - - ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::max)()); - } - - SECTION("short") - { - short s = 123; - sql << "insert into soci_test(id) values(:id)", use(s); - - short s2 = 0; - sql << "select id from soci_test", into(s2); - - CHECK(s2 == 123); - } - - SECTION("int16_t") - { - int16_t i = 123; - sql << "insert into soci_test(id) values(:id)", use(i); - - int16_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == 123); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:id)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::max)(); - sql << "insert into soci_test(id) values(:id)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::max)()); - } - - SECTION("uint16_t") - { - uint16_t ui = 123; - sql << "insert into soci_test(id) values(:id)", use(ui); - - uint16_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 123); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:id)", use(ui); - - ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::max)(); - sql << "insert into soci_test(id) values(:id)", use(ui); - - ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::max)()); - } - - SECTION("int") - { - int i = -12345678; - sql << "insert into soci_test(id) values(:i)", use(i); - - int i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == -12345678); - } - - SECTION("int32_t") - { - int32_t i = -12345678; - sql << "insert into soci_test(id) values(:i)", use(i); - - int32_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == -12345678); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:i)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::max)(); - sql << "insert into soci_test(id) values(:i)", use(i); - - i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::max)()); - } - - SECTION("uint32_t") - { - uint32_t ui = 12345678; - sql << "insert into soci_test(id) values(:i)", use(ui); - - uint32_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 12345678); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::min)(); - sql << "insert into soci_test(id) values(:i)", use(ui); - - ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::max)(); - sql << "insert into soci_test(ul) values(:i)", use(ui); - - ui2 = 0; - sql << "select ul from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::max)()); - } - - SECTION("unsigned long") - { - unsigned long ul = 4000000000ul; - sql << "insert into soci_test(ul) values(:num)", use(ul); - - unsigned long ul2 = 0; - sql << "select ul from soci_test", into(ul2); - - CHECK(ul2 == 4000000000ul); - } - - SECTION("int64_t") - { - int64_t i = 4000000000ll; - sql << "insert into soci_test(ll) values(:num)", use(i); - - int64_t i2 = 0; - sql << "select ll from soci_test", into(i2); - - CHECK(i2 == 4000000000ll); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::min)(); - sql << "insert into soci_test(ll) values(:num)", use(i); - - i2 = 0; - sql << "select ll from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - i = (std::numeric_limits::max)(); - sql << "insert into soci_test(ll) values(:num)", use(i); - - i2 = 0; - sql << "select ll from soci_test", into(i2); - - CHECK(i2 == (std::numeric_limits::max)()); - } - - SECTION("uint64_t") - { - uint64_t ui = 4000000000ull; - sql << "insert into soci_test(ul) values(:num)", use(ui); - - uint64_t ui2 = 0; - sql << "select ul from soci_test", into(ui2); - - CHECK(ui2 == 4000000000ull); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::min)(); - sql << "insert into soci_test(ul) values(:num)", use(ui); - - ui2 = 0; - sql << "select ul from soci_test", into(ui2); - - CHECK(ui2 == (std::numeric_limits::min)()); - - sql << "delete from soci_test"; - - ui = (std::numeric_limits::max)(); - sql << "insert into soci_test(ul) values(:num)", use(ui); - - ui2 = 0; - sql << "select ul from soci_test", into(ui2); - - if (tc_.truncates_uint64_to_int64()) - { - CHECK(ui2 == static_cast((std::numeric_limits::max)())); - } - else - { - CHECK(ui2 == (std::numeric_limits::max)()); - } - } - - SECTION("double") - { - double d = 3.14159265; - sql << "insert into soci_test(d) values(:d)", use(d); - - double d2 = 0; - sql << "select d from soci_test", into(d2); - - ASSERT_EQUAL(d2, d); - } - - SECTION("std::tm") - { - std::tm t = std::tm(); - t.tm_year = 105; - t.tm_mon = 10; - t.tm_mday = 19; - t.tm_hour = 21; - t.tm_min = 39; - t.tm_sec = 57; - sql << "insert into soci_test(tm) values(:t)", use(t); - - std::tm t2 = std::tm(); - t2.tm_year = 0; - t2.tm_mon = 0; - t2.tm_mday = 0; - t2.tm_hour = 0; - t2.tm_min = 0; - t2.tm_sec = 0; - - sql << "select tm from soci_test", into(t2); - - CHECK(t.tm_year == 105); - CHECK(t.tm_mon == 10); - CHECK(t.tm_mday == 19); - CHECK(t.tm_hour == 21); - CHECK(t.tm_min == 39); - CHECK(t.tm_sec == 57); - } - - SECTION("repeated use") - { - int i; - statement st = (sql.prepare - << "insert into soci_test(id) values(:id)", use(i)); - - i = 5; - st.execute(true); - i = 6; - st.execute(true); - i = 7; - st.execute(true); - - std::vector v(5); - sql << "select id from soci_test order by id", into(v); - - CHECK(v.size() == 3); - CHECK(v[0] == 5); - CHECK(v[1] == 6); - CHECK(v[2] == 7); - } - - // tests for use of const objects - - SECTION("const char") - { - char const c('a'); - sql << "insert into soci_test(c) values(:c)", use(c); - - char c2 = 'b'; - sql << "select c from soci_test", into(c2); - CHECK(c2 == 'a'); - - } - - SECTION("const std::string") - { - std::string const s = "Hello const SOCI!"; - sql << "insert into soci_test(str) values(:s)", use(s); - - std::string str; - sql << "select str from soci_test", into(str); - - CHECK(str == "Hello const SOCI!"); - } - - SECTION("const int8_t") - { - int8_t const i = 123; - sql << "insert into soci_test(id) values(:id)", use(i); - - int8_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == 123); - } - - SECTION("const uint8_t") - { - uint8_t const ui = 123; - sql << "insert into soci_test(id) values(:id)", use(ui); - - uint8_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 123); - } - - SECTION("const short") - { - short const s = 123; - sql << "insert into soci_test(id) values(:id)", use(s); - - short s2 = 0; - sql << "select id from soci_test", into(s2); - - CHECK(s2 == 123); - } - - SECTION("const int16_t") - { - int16_t const i = 123; - sql << "insert into soci_test(id) values(:id)", use(i); - - int16_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == 123); - } - - SECTION("const uint16_t") - { - uint16_t const ui = 123; - sql << "insert into soci_test(id) values(:id)", use(ui); - - uint16_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 123); - } - - SECTION("const int") - { - int const i = -12345678; - sql << "insert into soci_test(id) values(:i)", use(i); - - int i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == -12345678); - } - - SECTION("const int32_t") - { - int32_t const i = -12345678; - sql << "insert into soci_test(id) values(:i)", use(i); - - int32_t i2 = 0; - sql << "select id from soci_test", into(i2); - - CHECK(i2 == -12345678); - } - - SECTION("const uint32_t") - { - uint32_t const ui = 12345678; - sql << "insert into soci_test(id) values(:i)", use(ui); - - uint32_t ui2 = 0; - sql << "select id from soci_test", into(ui2); - - CHECK(ui2 == 12345678); - } - - SECTION("const unsigned long") - { - unsigned long const ul = 4000000000ul; - sql << "insert into soci_test(ul) values(:num)", use(ul); - - unsigned long ul2 = 0; - sql << "select ul from soci_test", into(ul2); - - CHECK(ul2 == 4000000000ul); - } - - SECTION("const int64_t") - { - int64_t const i = 4000000000ll; - sql << "insert into soci_test(ul) values(:num)", use(i); - - int64_t i2 = 0; - sql << "select ul from soci_test", into(i2); - - CHECK(i2 == 4000000000ll); - } - - SECTION("const uint64_t") - { - uint64_t const ui = 4000000000ull; - sql << "insert into soci_test(ul) values(:num)", use(ui); - - uint64_t ui2 = 0; - sql << "select ul from soci_test", into(ui2); - - CHECK(ui2 == 4000000000ull); - } - - SECTION("const double") - { - double const d = 3.14159265; - sql << "insert into soci_test(d) values(:d)", use(d); - - double d2 = 0; - sql << "select d from soci_test", into(d2); - - ASSERT_EQUAL(d2, d); - } - - SECTION("const std::tm") - { - std::tm t = std::tm(); - t.tm_year = 105; - t.tm_mon = 10; - t.tm_mday = 19; - t.tm_hour = 21; - t.tm_min = 39; - t.tm_sec = 57; - std::tm const & ct = t; - sql << "insert into soci_test(tm) values(:t)", use(ct); - - std::tm t2 = std::tm(); - t2.tm_year = 0; - t2.tm_mon = 0; - t2.tm_mday = 0; - t2.tm_hour = 0; - t2.tm_min = 0; - t2.tm_sec = 0; - - sql << "select tm from soci_test", into(t2); - - CHECK(t.tm_year == 105); - CHECK(t.tm_mon == 10); - CHECK(t.tm_mday == 19); - CHECK(t.tm_hour == 21); - CHECK(t.tm_min == 39); - CHECK(t.tm_sec == 57); - } -} - -// test for multiple use (and into) elements -TEST_CASE_METHOD(common_tests, "Multiple use and into", "[core][use][into]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - { - int i1 = 5; - int i2 = 6; - int i3 = 7; - - sql << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", - use(i1), use(i2), use(i3); - - i1 = 0; - i2 = 0; - i3 = 0; - sql << "select i1, i2, i3 from soci_test", - into(i1), into(i2), into(i3); - - CHECK(i1 == 5); - CHECK(i2 == 6); - CHECK(i3 == 7); - - // same for vectors - sql << "delete from soci_test"; - - i1 = 0; - i2 = 0; - i3 = 0; - - statement st = (sql.prepare - << "insert into soci_test(i1, i2, i3) values(:i1, :i2, :i3)", - use(i1), use(i2), use(i3)); - - i1 = 1; - i2 = 2; - i3 = 3; - st.execute(true); - i1 = 4; - i2 = 5; - i3 = 6; - st.execute(true); - i1 = 7; - i2 = 8; - i3 = 9; - st.execute(true); - - std::vector v1(5); - std::vector v2(5); - std::vector v3(5); - - sql << "select i1, i2, i3 from soci_test order by i1", - into(v1), into(v2), into(v3); - - CHECK(v1.size() == 3); - CHECK(v2.size() == 3); - CHECK(v3.size() == 3); - CHECK(v1[0] == 1); - CHECK(v1[1] == 4); - CHECK(v1[2] == 7); - CHECK(v2[0] == 2); - CHECK(v2[1] == 5); - CHECK(v2[2] == 8); - CHECK(v3[0] == 3); - CHECK(v3[1] == 6); - CHECK(v3[2] == 9); - } -} - -// use vector elements -TEST_CASE_METHOD(common_tests, "Use vector", "[core][use][vector]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - SECTION("char") - { - std::vector v; - v.push_back('a'); - v.push_back('b'); - v.push_back('c'); - v.push_back('d'); - - sql << "insert into soci_test(c) values(:c)", use(v); - - std::vector v2(4); - - sql << "select c from soci_test order by c", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == 'a'); - CHECK(v2[1] == 'b'); - CHECK(v2[2] == 'c'); - CHECK(v2[3] == 'd'); - } - - SECTION("std::string") - { - std::vector v; - v.push_back("ala"); - v.push_back("ma"); - v.push_back("kota"); - - sql << "insert into soci_test(str) values(:s)", use(v); - - std::vector v2(4); - - sql << "select str from soci_test order by str", into(v2); - CHECK(v2.size() == 3); - CHECK(v2[0] == "ala"); - CHECK(v2[1] == "kota"); - CHECK(v2[2] == "ma"); - } - - SECTION("int8_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(-5); - v.push_back(123); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(sh) values(:sh)", use(v); - - std::vector v2(4); - - sql << "select sh from soci_test order by sh", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == -5); - CHECK(v2[2] == 123); - CHECK(v2[3] == (std::numeric_limits::max)()); - } - - SECTION("uint8_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(6); - v.push_back(123); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(sh) values(:sh)", use(v); - - std::vector v2(4); - - sql << "select sh from soci_test order by sh", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == 6); - CHECK(v2[2] == 123); - CHECK(v2[3] == (std::numeric_limits::max)()); - } - - SECTION("short") - { - std::vector v; - v.push_back(-5); - v.push_back(6); - v.push_back(7); - v.push_back(123); - - sql << "insert into soci_test(sh) values(:sh)", use(v); - - std::vector v2(4); - - sql << "select sh from soci_test order by sh", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == -5); - CHECK(v2[1] == 6); - CHECK(v2[2] == 7); - CHECK(v2[3] == 123); - } - - SECTION("int16_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(-5); - v.push_back(123); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(sh) values(:sh)", use(v); - - std::vector v2(4); - - sql << "select sh from soci_test order by sh", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == -5); - CHECK(v2[2] == 123); - CHECK(v2[3] == (std::numeric_limits::max)()); - } - - SECTION("uint16_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(6); - v.push_back(123); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(val) values(:val)", use(v); - - std::vector v2(4); - - sql << "select val from soci_test order by val", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == 6); - CHECK(v2[2] == 123); - CHECK(v2[3] == (std::numeric_limits::max)()); - } - - SECTION("int") - { - std::vector v; - v.push_back(-2000000000); - v.push_back(0); - v.push_back(1); - v.push_back(2000000000); - - sql << "insert into soci_test(id) values(:i)", use(v); - - std::vector v2(4); - - sql << "select id from soci_test order by id", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == -2000000000); - CHECK(v2[1] == 0); - CHECK(v2[2] == 1); - CHECK(v2[3] == 2000000000); - } - - SECTION("int32_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(-2000000000); - v.push_back(0); - v.push_back(1); - v.push_back(2000000000); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(id) values(:i)", use(v); - - std::vector v2(6); - - sql << "select id from soci_test order by id", into(v2); - CHECK(v2.size() == 6); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == -2000000000); - CHECK(v2[2] == 0); - CHECK(v2[3] == 1); - CHECK(v2[4] == 2000000000); - CHECK(v2[5] == (std::numeric_limits::max)()); - } - - SECTION("unsigned int") - { - std::vector v; - v.push_back(0); - v.push_back(1); - v.push_back(123); - v.push_back(1000); - - sql << "insert into soci_test(ul) values(:ul)", use(v); - - std::vector v2(4); - - sql << "select ul from soci_test order by ul", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == 0); - CHECK(v2[1] == 1); - CHECK(v2[2] == 123); - CHECK(v2[3] == 1000); - } - - SECTION("uint32_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(0); - v.push_back(1); - v.push_back(123); - v.push_back(1000); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(ul) values(:ul)", use(v); - - std::vector v2(6); - - sql << "select ul from soci_test order by ul", into(v2); - CHECK(v2.size() == 6); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == 0); - CHECK(v2[2] == 1); - CHECK(v2[3] == 123); - CHECK(v2[4] == 1000); - CHECK(v2[5] == (std::numeric_limits::max)()); - } - - SECTION("unsigned long long") - { - std::vector v; - v.push_back(0); - v.push_back(1); - v.push_back(123); - v.push_back(1000); - - sql << "insert into soci_test(ul) values(:ul)", use(v); - - std::vector v2(4); - - sql << "select ul from soci_test order by ul", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == 0); - CHECK(v2[1] == 1); - CHECK(v2[2] == 123); - CHECK(v2[3] == 1000); - } - - SECTION("int64_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(0); - v.push_back(1); - v.push_back(123); - v.push_back(1000); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(ll) values(:ll)", use(v); - - std::vector v2(6); - - sql << "select ll from soci_test order by ll", into(v2); - CHECK(v2.size() == 6); - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == 0); - CHECK(v2[2] == 1); - CHECK(v2[3] == 123); - CHECK(v2[4] == 1000); - CHECK(v2[5] == (std::numeric_limits::max)()); - } - - SECTION("uint64_t") - { - std::vector v; - v.push_back((std::numeric_limits::min)()); - v.push_back(0); - v.push_back(1); - v.push_back(123); - v.push_back(1000); - v.push_back((std::numeric_limits::max)()); - - sql << "insert into soci_test(ul) values(:ul)", use(v); - - std::vector v2(6); - - sql << "select ul from soci_test order by ul", into(v2); - CHECK(v2.size() == 6); - if (tc_.has_uint64_storage_bug()) - { - CHECK(v2[0] == (std::numeric_limits::max)()); - CHECK(v2[1] == (std::numeric_limits::min)()); - CHECK(v2[2] == 0); - CHECK(v2[3] == 1); - CHECK(v2[4] == 123); - CHECK(v2[5] == 1000); - } - else - { - CHECK(v2[0] == (std::numeric_limits::min)()); - CHECK(v2[1] == 0); - CHECK(v2[2] == 1); - CHECK(v2[3] == 123); - CHECK(v2[4] == 1000); - if (tc_.truncates_uint64_to_int64()) - { - CHECK(v2[5] == static_cast((std::numeric_limits::max)())); - } - else - { - CHECK(v2[5] == (std::numeric_limits::max)()); - } - } - } - - SECTION("double") - { - std::vector v; - v.push_back(0); - v.push_back(-0.0001); - v.push_back(0.0001); - v.push_back(3.1415926); - - sql << "insert into soci_test(d) values(:d)", use(v); - - std::vector v2(4); - - sql << "select d from soci_test order by d", into(v2); - CHECK(v2.size() == 4); - ASSERT_EQUAL(v2[0],-0.0001); - ASSERT_EQUAL(v2[1], 0); - ASSERT_EQUAL(v2[2], 0.0001); - ASSERT_EQUAL(v2[3], 3.1415926); - } - - SECTION("std::tm") - { - std::vector v; - std::tm t = std::tm(); - t.tm_year = 105; - t.tm_mon = 10; - t.tm_mday = 26; - t.tm_hour = 22; - t.tm_min = 45; - t.tm_sec = 17; - - v.push_back(t); - - t.tm_sec = 37; - v.push_back(t); - - t.tm_mday = 25; - v.push_back(t); - - sql << "insert into soci_test(tm) values(:t)", use(v); - - std::vector v2(4); - - sql << "select tm from soci_test order by tm", into(v2); - CHECK(v2.size() == 3); - CHECK(v2[0].tm_year == 105); - CHECK(v2[0].tm_mon == 10); - CHECK(v2[0].tm_mday == 25); - CHECK(v2[0].tm_hour == 22); - CHECK(v2[0].tm_min == 45); - CHECK(v2[0].tm_sec == 37); - CHECK(v2[1].tm_year == 105); - CHECK(v2[1].tm_mon == 10); - CHECK(v2[1].tm_mday == 26); - CHECK(v2[1].tm_hour == 22); - CHECK(v2[1].tm_min == 45); - CHECK(v2[1].tm_sec == 17); - CHECK(v2[2].tm_year == 105); - CHECK(v2[2].tm_mon == 10); - CHECK(v2[2].tm_mday == 26); - CHECK(v2[2].tm_hour == 22); - CHECK(v2[2].tm_min == 45); - CHECK(v2[2].tm_sec == 37); - } - - SECTION("const int") - { - std::vector v; - v.push_back(-2000000000); - v.push_back(0); - v.push_back(1); - v.push_back(2000000000); - - std::vector const & cv = v; - - sql << "insert into soci_test(id) values(:i)", use(cv); - - std::vector v2(4); - - sql << "select id from soci_test order by id", into(v2); - CHECK(v2.size() == 4); - CHECK(v2[0] == -2000000000); - CHECK(v2[1] == 0); - CHECK(v2[2] == 1); - CHECK(v2[3] == 2000000000); - } -} - -// use vector elements with type convertion -TEST_CASE_METHOD(common_tests, "Use vector of custom type objects", "[core][use][vector][type_conversion]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - // Unfortunately there is no portable way to indicate whether nulls should - // appear at the beginning or the end (SQL 2003 "NULLS LAST" is still not - // supported by MS SQL in 2021...), so use this column just to order by it. - std::vector i; - i.push_back(0); - i.push_back(1); - i.push_back(2); - - std::vector v; - v.push_back(MyOptionalString("string")); // A not empty valid string. - v.push_back(MyOptionalString()); // Invalid string mapped to null. - v.push_back(MyOptionalString("")); // An empty but still valid string. - - sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(v); - - SECTION("standard type") - { - std::vector values(3); - std::vector inds(3); - sql << "select str from soci_test order by id", into(values, inds); - - REQUIRE(values.size() == 3); - REQUIRE(inds.size() == 3); - - CHECK(inds[0] == soci::i_ok); - CHECK(values[0] == "string"); - - CHECK(inds[1] == soci::i_null); - - if ( !tc_.treats_empty_strings_as_null() ) - { - CHECK(inds[2] == soci::i_ok); - CHECK(values[2] == ""); - } - } - - SECTION("user type") - { - std::vector values(3); - std::vector inds(3); - sql << "select str from soci_test order by id", into(values, inds); - - REQUIRE(values.size() == 3); - REQUIRE(inds.size() == 3); - - CHECK(inds[0] == soci::i_ok); - CHECK(values[0].is_valid()); - CHECK(values[0].get() == "string"); - - CHECK(!values[1].is_valid()); - CHECK(inds[1] == soci::i_null); - - if ( !tc_.treats_empty_strings_as_null() ) - { - CHECK(inds[2] == soci::i_ok); - CHECK(values[2].is_valid()); - CHECK(values[2].get() == ""); - } - } -} - -TEST_CASE_METHOD(common_tests, "Into vector of custom type objects", "[core][into][vector][type_conversion]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - // Column used for sorting only, see above. - std::vector i; - i.push_back(0); - i.push_back(1); - i.push_back(2); - - std::vector values(3); - values[0] = "string"; - - std::vector inds; - inds.push_back(i_ok); - inds.push_back(i_null); - inds.push_back(i_ok); - - sql << "insert into soci_test(id, str) values(:i, :v)", use(i), use(values, inds); - - std::vector v2(4); - - sql << "select str from soci_test order by id", into(v2); - - INFO("Got back " << v2); - REQUIRE(v2.size() == 3); - - CHECK(v2[0].is_valid()); - CHECK(v2[0].get() == "string"); - - CHECK(!v2[1].is_valid()); - - if ( !tc_.treats_empty_strings_as_null() ) - { - CHECK(v2[2].is_valid()); - CHECK(v2[2].get().empty()); - } -} - -// test for named binding -TEST_CASE_METHOD(common_tests, "Named parameters", "[core][use][named-params]") -{ - soci::session sql(backEndFactory_, connectString_); - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - int i1 = 7; - int i2 = 8; - - // verify the exception is thrown if both by position - // and by name use elements are specified - try - { - sql << "insert into soci_test(i1, i2) values(:i1, :i2)", - use(i1, "i1"), use(i2); - - FAIL("expected exception not thrown"); - } - catch (soci_error const& e) - { - CHECK(e.get_error_message() == - "Binding for use elements must be either by position " - "or by name."); - } - - // normal test - sql << "insert into soci_test(i1, i2) values(:i1, :i2)", - use(i1, "i1"), use(i2, "i2"); - - i1 = 0; - i2 = 0; - sql << "select i1, i2 from soci_test", into(i1), into(i2); - CHECK(i1 == 7); - CHECK(i2 == 8); - - i2 = 0; - sql << "select i2 from soci_test where i1 = :i1", into(i2), use(i1); - CHECK(i2 == 8); - - sql << "delete from soci_test"; - - // test vectors - - std::vector v1; - v1.push_back(1); - v1.push_back(2); - v1.push_back(3); - - std::vector v2; - v2.push_back(4); - v2.push_back(5); - v2.push_back(6); - - sql << "insert into soci_test(i1, i2) values(:i1, :i2)", - use(v1, "i1"), use(v2, "i2"); - - sql << "select i2, i1 from soci_test order by i1 desc", - into(v1), into(v2); - CHECK(v1.size() == 3); - CHECK(v2.size() == 3); - CHECK(v1[0] == 6); - CHECK(v1[1] == 5); - CHECK(v1[2] == 4); - CHECK(v2[0] == 3); - CHECK(v2[1] == 2); - CHECK(v2[2] == 1); - } -} - -TEST_CASE_METHOD(common_tests, "Named parameters with similar names", "[core][use][named-params]") -{ - // Verify parsing of parameters with similar names, - // where one name is part of the other, etc. - // https://github.com/SOCI/soci/issues/26 - - soci::session sql(backEndFactory_, connectString_); - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - std::string passwd("abc"); - std::string passwd_clear("clear"); - - SECTION("unnamed") - { - sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", - soci::use(passwd), soci::use(passwd_clear); - } - - SECTION("same order") - { - sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", - soci::use(passwd_clear, "passwd_clear"), soci::use(passwd, "passwd"); - } - - SECTION("reversed order") - { - sql << "INSERT INTO soci_test(str,name) VALUES(:passwd_clear, :passwd)", - soci::use(passwd, "passwd"), soci::use(passwd_clear, "passwd_clear"); - } - - // TODO: Allow binding the same varibale multiple times - // SECTION("one for multiple placeholders") - // { - // sql << "INSERT INTO soci_test(str,name) VALUES(:passwd, :passwd)", - // soci::use(passwd, "passwd"); - // } - } -} - -// transaction test -TEST_CASE_METHOD(common_tests, "Transactions", "[core][transaction]") -{ - soci::session sql(backEndFactory_, connectString_); - - if (!tc_.has_transactions_support(sql)) - { - WARN("Transactions not supported by the database, skipping the test."); - return; - } - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - int count; - sql << "select count(*) from soci_test", into(count); - CHECK(count == 0); - - { - transaction tr(sql); - - sql << "insert into soci_test (id, name) values(1, 'John')"; - sql << "insert into soci_test (id, name) values(2, 'Anna')"; - sql << "insert into soci_test (id, name) values(3, 'Mike')"; - - tr.commit(); - } - { - transaction tr(sql); - - sql << "select count(*) from soci_test", into(count); - CHECK(count == 3); - - sql << "insert into soci_test (id, name) values(4, 'Stan')"; - - sql << "select count(*) from soci_test", into(count); - CHECK(count == 4); - - tr.rollback(); - - sql << "select count(*) from soci_test", into(count); - CHECK(count == 3); - } - { - transaction tr(sql); - - sql << "delete from soci_test"; - - sql << "select count(*) from soci_test", into(count); - CHECK(count == 0); - - tr.rollback(); - - sql << "select count(*) from soci_test", into(count); - CHECK(count == 3); - } - { - // additional test for detection of double commit - transaction tr(sql); - tr.commit(); - try - { - tr.commit(); - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "The transaction object cannot be handled twice."); - } - } -} - -std::tm generate_tm() -{ - std::tm t = std::tm(); - t.tm_year = 105; - t.tm_mon = 10; - t.tm_mday = 15; - t.tm_hour = 22; - t.tm_min = 14; - t.tm_sec = 17; - return t; -} - -// test of use elements with indicators -TEST_CASE_METHOD(common_tests, "Use with indicators", "[core][use][indicator]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - indicator ind1 = i_ok; - indicator ind2 = i_ok; - indicator ind3 = i_ok; - - int id = 1; - int val = 10; - std::tm tm_gen = generate_tm(); - char const* insert = "insert into soci_test(id, val, tm) values(:id, :val, :tm)"; - sql << insert, use(id, ind1), use(val, ind2), use(tm_gen, ind3); - - id = 2; - val = 11; - ind2 = i_null; - std::tm tm = std::tm(); - ind3 = i_null; - - sql << "insert into soci_test(id, val, tm) values(:id, :val, :tm)", - use(id, ind1), use(val, ind2), use(tm, ind3); - - sql << "select val from soci_test where id = 1", into(val, ind2); - CHECK(ind2 == i_ok); - CHECK(val == 10); - sql << "select val, tm from soci_test where id = 2", into(val, ind2), into(tm, ind3); - CHECK(ind2 == i_null); - CHECK(ind3 == i_null); - - std::vector ids; - ids.push_back(3); - ids.push_back(4); - ids.push_back(5); - std::vector vals; - vals.push_back(12); - vals.push_back(13); - vals.push_back(14); - std::vector inds; - inds.push_back(i_ok); - inds.push_back(i_null); - inds.push_back(i_ok); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(ids), use(vals, inds); - - ids.resize(5); - vals.resize(5); - sql << "select id, val from soci_test order by id desc", - into(ids), into(vals, inds); - - CHECK(ids.size() == 5); - CHECK(ids[0] == 5); - CHECK(ids[1] == 4); - CHECK(ids[2] == 3); - CHECK(ids[3] == 2); - CHECK(ids[4] == 1); - CHECK(inds.size() == 5); - CHECK(inds[0] == i_ok); - CHECK(inds[1] == i_null); - CHECK(inds[2] == i_ok); - CHECK(inds[3] == i_null); - CHECK(inds[4] == i_ok); - CHECK(vals.size() == 5); - CHECK(vals[0] == 14); - CHECK(vals[2] == 12); - CHECK(vals[4] == 10); -} - -// Dynamic binding to Row objects -TEST_CASE_METHOD(common_tests, "Dynamic row binding", "[core][dynamic]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - - auto_table_creator tableCreator(tc_.table_creator_2(sql)); - - row r; - sql << "select * from soci_test", into(r); - CHECK(sql.got_data() == false); - - sql << "insert into soci_test" - " values(3.14, 123, \'Johny\'," - << tc_.to_date_time("2005-12-19 22:14:17") - << ", 'a')"; - - // select into a row - { - statement st = (sql.prepare << - "select * from soci_test", into(r)); - st.execute(true); - CHECK(r.size() == 5); - - CHECK(r.get_properties(0).get_data_type() == dt_double); - CHECK(r.get_properties(0).get_db_type() == db_double); - CHECK(r.get_properties(1).get_data_type() == dt_integer); - CHECK(r.get_properties(1).get_db_type() == db_int32); - CHECK(r.get_properties(2).get_data_type() == dt_string); - CHECK(r.get_properties(2).get_db_type() == db_string); - CHECK(r.get_properties(3).get_data_type() == dt_date); - CHECK(r.get_properties(3).get_db_type() == db_date); - - // type char is visible as string - // - to comply with the implementation for Oracle - CHECK(r.get_properties(4).get_data_type() == dt_string); - CHECK(r.get_properties(4).get_db_type() == db_string); - - CHECK(r.get_properties("NUM_INT").get_data_type() == dt_integer); - CHECK(r.get_properties("NUM_INT").get_db_type() == db_int32); - - CHECK(r.get_properties(0).get_name() == "NUM_FLOAT"); - CHECK(r.get_properties(1).get_name() == "NUM_INT"); - CHECK(r.get_properties(2).get_name() == "NAME"); - CHECK(r.get_properties(3).get_name() == "SOMETIME"); - CHECK(r.get_properties(4).get_name() == "CHR"); - - ASSERT_EQUAL_APPROX(r.get(0), 3.14); - CHECK(r.get(1) == 123); - CHECK(r.get(2) == "Johny"); - CHECK(r.get(3).tm_year == 105); - - // again, type char is visible as string - CHECK_EQUAL_PADDED(r.get(4), "a"); - - ASSERT_EQUAL_APPROX(r.get("NUM_FLOAT"), 3.14); - CHECK(r.get("NUM_INT") == 123); - CHECK(r.get("NAME") == "Johny"); - CHECK_EQUAL_PADDED(r.get("CHR"), "a"); - - CHECK(r.get_indicator(0) == i_ok); - - // verify exception thrown on invalid get<> - bool caught = false; - try - { - r.get(0); - } - catch (std::bad_cast const &) - { - caught = true; - } - CHECK(caught); - - // additional test for stream-like extraction - { - double d; - int i; - std::string s; - std::tm t = std::tm(); - std::string c; - - r >> d >> i >> s >> t >> c; - - ASSERT_EQUAL_APPROX(d, 3.14); - CHECK(i == 123); - CHECK(s == "Johny"); - CHECK(t.tm_year == 105); - CHECK(t.tm_mon == 11); - CHECK(t.tm_mday == 19); - CHECK(t.tm_hour == 22); - CHECK(t.tm_min == 14); - CHECK(t.tm_sec == 17); - CHECK_EQUAL_PADDED(c, "a"); - } - } - - // additional test to check if the row object can be - // reused between queries - { - sql << "select * from soci_test", into(r); - - CHECK(r.size() == 5); - - CHECK(r.get_properties(0).get_data_type() == dt_double); - CHECK(r.get_properties(0).get_db_type() == db_double); - CHECK(r.get_properties(1).get_data_type() == dt_integer); - CHECK(r.get_properties(1).get_db_type() == db_int32); - CHECK(r.get_properties(2).get_data_type() == dt_string); - CHECK(r.get_properties(2).get_db_type() == db_string); - CHECK(r.get_properties(3).get_data_type() == dt_date); - CHECK(r.get_properties(3).get_db_type() == db_date); - - sql << "select name, num_int from soci_test", into(r); - - CHECK(r.size() == 2); - - CHECK(r.get_properties(0).get_data_type() == dt_string); - CHECK(r.get_properties(0).get_db_type() == db_string); - CHECK(r.get_properties(1).get_data_type() == dt_integer); - CHECK(r.get_properties(1).get_db_type() == db_int32); - - // Check if row object is movable - row moved = std::move(r); - - CHECK(moved.size() == 2); - // We expect the moved-from row to become empty after the move operation - CHECK(r.size() == 0); - - CHECK(moved.get_properties(0).get_data_type() == dt_string); - CHECK(moved.get_properties(0).get_db_type() == db_string); - CHECK(moved.get_properties(1).get_data_type() == dt_integer); - CHECK(moved.get_properties(1).get_db_type() == db_int32); - } -} - -// more dynamic bindings -TEST_CASE_METHOD(common_tests, "Dynamic row binding 2", "[core][dynamic]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(id, val) values(1, 10)"; - sql << "insert into soci_test(id, val) values(2, 20)"; - sql << "insert into soci_test(id, val) values(3, 30)"; - - { - int id = 2; - row r; - sql << "select val from soci_test where id = :id", use(id), into(r); - - CHECK(r.size() == 1); - CHECK(r.get_properties(0).get_data_type() == dt_integer); - CHECK(r.get_properties(0).get_db_type() == db_int32); - CHECK(r.get(0) == 20); - CHECK(r.get(0) == 20); - } - { - int id; - row r; - statement st = (sql.prepare << - "select val from soci_test where id = :id", use(id), into(r)); - - id = 2; - st.execute(true); - CHECK(r.size() == 1); - CHECK(r.get_properties(0).get_data_type() == dt_integer); - CHECK(r.get_properties(0).get_db_type() == db_int32); - CHECK(r.get(0) == 20); - CHECK(r.get(0) == 20); - - id = 3; - st.execute(true); - CHECK(r.size() == 1); - CHECK(r.get_properties(0).get_data_type() == dt_integer); - CHECK(r.get_properties(0).get_db_type() == db_int32); - CHECK(r.get(0) == 30); - CHECK(r.get(0) == 30); - - id = 1; - st.execute(true); - CHECK(r.size() == 1); - CHECK(r.get_properties(0).get_data_type() == dt_integer); - CHECK(r.get_properties(0).get_db_type() == db_int32); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - } -} - -// More Dynamic binding to row objects -TEST_CASE_METHOD(common_tests, "Dynamic row binding 3", "[core][dynamic]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - - auto_table_creator tableCreator(tc_.table_creator_3(sql)); - - row r1; - sql << "select * from soci_test", into(r1); - CHECK(sql.got_data() == false); - - sql << "insert into soci_test values('david', '(404)123-4567')"; - sql << "insert into soci_test values('john', '(404)123-4567')"; - sql << "insert into soci_test values('doe', '(404)123-4567')"; - - row r2; - statement st = (sql.prepare << "select * from soci_test", into(r2)); - st.execute(); - - CHECK(r2.size() == 2); - - int count = 0; - while (st.fetch()) - { - ++count; - CHECK(r2.get("PHONE") == "(404)123-4567"); - } - CHECK(count == 3); -} - -// This is like the previous test but with a type_conversion instead of a row -TEST_CASE_METHOD(common_tests, "Dynamic binding with type conversions", "[core][dynamic][type_conversion]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - - SECTION("simple conversions") - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - SECTION("between single basic type and user type") - { - MyInt mi; - mi.set(123); - sql << "insert into soci_test(id) values(:id)", use(mi); - - int i; - sql << "select id from soci_test", into(i); - CHECK(i == 123); - - sql << "update soci_test set id = id + 1"; - - sql << "select id from soci_test", into(mi); - CHECK(mi.get() == 124); - } - - SECTION("with use const") - { - MyInt mi; - mi.set(123); - - MyInt const & cmi = mi; - sql << "insert into soci_test(id) values(:id)", use(cmi); - - int i; - sql << "select id from soci_test", into(i); - CHECK(i == 123); - } - } - - SECTION("ORM conversions") - { - auto_table_creator tableCreator(tc_.table_creator_3(sql)); - - SECTION("conversions based on values") - { - PhonebookEntry p1; - sql << "select * from soci_test", into(p1); - CHECK(p1.name == ""); - CHECK(p1.phone == ""); - - p1.name = "david"; - - // Note: uppercase column names are used here (and later on) - // for consistency with how they can be read from database - // (which means forced to uppercase on Oracle) and how they are - // set/get in the type conversion routines for PhonebookEntry. - // In short, IF the database is Oracle, - // then all column names for binding should be uppercase. - sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); - sql << "insert into soci_test values('john', '(404)123-4567')"; - sql << "insert into soci_test values('doe', '(404)123-4567')"; - - PhonebookEntry p2; - statement st = (sql.prepare << "select * from soci_test", into(p2)); - st.execute(); - - int count = 0; - while (st.fetch()) - { - ++count; - if (p2.name == "david") - { - // see type_conversion - CHECK(p2.phone ==""); - } - else - { - CHECK(p2.phone == "(404)123-4567"); - } - } - CHECK(count == 3); - } - - SECTION("conversions based on values with use const") - { - PhonebookEntry p1; - p1.name = "Joe Coder"; - p1.phone = "123-456"; - - PhonebookEntry const & cp1 = p1; - - sql << "insert into soci_test values(:NAME, :PHONE)", use(cp1); - - PhonebookEntry p2; - sql << "select * from soci_test", into(p2); - CHECK(sql.got_data()); - - CHECK(p2.name == "Joe Coder"); - CHECK(p2.phone == "123-456"); - } - - SECTION("conversions based on accessor functions (as opposed to direct variable bindings)") - { - PhonebookEntry3 p1; - p1.setName("Joe Hacker"); - p1.setPhone("10010110"); - - sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); - - PhonebookEntry3 p2; - sql << "select * from soci_test", into(p2); - CHECK(sql.got_data()); - - CHECK(p2.getName() == "Joe Hacker"); - CHECK(p2.getPhone() == "10010110"); - } - - SECTION("PhonebookEntry2 type conversion to test calls to values::get_indicator()") - { - PhonebookEntry2 p1; - sql << "select * from soci_test", into(p1); - CHECK(p1.name == ""); - CHECK(p1.phone == ""); - p1.name = "david"; - - sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); - sql << "insert into soci_test values('john', '(404)123-4567')"; - sql << "insert into soci_test values('doe', '(404)123-4567')"; - - PhonebookEntry2 p2; - statement st = (sql.prepare << "select * from soci_test", into(p2)); - st.execute(); - - int count = 0; - while (st.fetch()) - { - ++count; - if (p2.name == "david") - { - // see type_conversion - CHECK(p2.phone ==""); - } - else - { - CHECK(p2.phone == "(404)123-4567"); - } - } - CHECK(count == 3); - } - } -} - -// Dynamic bindings with type casts -TEST_CASE_METHOD(common_tests, "Dynamic row binding 4", "[core][dynamic]") -{ - soci::session sql(backEndFactory_, connectString_); - - SECTION("simple type cast") - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(id, d, str, tm)" - << " values(10, 20.0, 'foobar'," - << tc_.to_date_time("2005-12-19 22:14:17") - << ")"; - - { - row r; - sql << "select id from soci_test", into(r); - - CHECK(r.size() == 1); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK(r.get(0) == 10); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - } - { - row r; - sql << "select d from soci_test", into(r); - - CHECK(r.size() == 1); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - ASSERT_EQUAL_APPROX(r.get(0), 20.0); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - } - { - row r; - sql << "select str from soci_test", into(r); - - CHECK(r.size() == 1); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK(r.get(0) == "foobar"); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - } - { - row r; - sql << "select tm from soci_test", into(r); - - CHECK(r.size() == 1); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK(r.get(0).tm_year == 105); - CHECK(r.get(0).tm_mon == 11); - CHECK(r.get(0).tm_mday == 19); - CHECK(r.get(0).tm_hour == 22); - CHECK(r.get(0).tm_min == 14); - CHECK(r.get(0).tm_sec == 17); - } - } - SECTION("overflowing type cast") - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(id)" - << " values(" - << (std::numeric_limits::max)() - << ")"; - - row r; - sql << "select id from soci_test", into(r); - - intmax_t v = (intmax_t)(std::numeric_limits::max)(); - uintmax_t uv = (uintmax_t)(std::numeric_limits::max)(); - - CHECK(r.size() == 1); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK(r.get(0) == v); - CHECK(r.get(0) == v); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK_THROWS_AS(r.get(0), std::bad_cast); - CHECK(r.get(0) == uv); - CHECK(r.get(0) == uv); - } -} - -TEST_CASE_METHOD(common_tests, "Prepared insert with ORM", "[core][orm]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - auto_table_creator tableCreator(tc_.table_creator_3(sql)); - - PhonebookEntry temp; - PhonebookEntry e1 = { "name1", "phone1" }; - PhonebookEntry e2 = { "name2", "phone2" }; - - //sql << "insert into soci_test values (:NAME, :PHONE)", use(temp); - statement insertStatement = (sql.prepare << "insert into soci_test values (:NAME, :PHONE)", use(temp)); - - temp = e1; - insertStatement.execute(true); - temp = e2; - insertStatement.execute(true); - - int count = 0; - - sql << "select count(*) from soci_test where NAME in ('name1', 'name2')", into(count); - - CHECK(count == 2); -} - -TEST_CASE_METHOD(common_tests, "Partial match with ORM", "[core][orm]") -{ - soci::session sql(backEndFactory_, connectString_); - sql.uppercase_column_names(true); - auto_table_creator tableCreator(tc_.table_creator_3(sql)); - - PhonebookEntry in = { "name1", "phone1" }; - std::string name = "nameA"; - sql << "insert into soci_test values (:NAMED, :PHONE)", use(in), use(name, "NAMED"); - - PhonebookEntry out; - sql << "select * from soci_test where PHONE = 'phone1'", into(out); - CHECK(out.name == "nameA"); - CHECK(out.phone == "phone1"); -} - -TEST_CASE_METHOD(common_tests, "Numeric round trip", "[core][float]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - double d1 = 0.003958, - d2; - - sql << "insert into soci_test(num76) values (:d1)", use(d1); - sql << "select num76 from soci_test", into(d2); - - // The numeric value should make the round trip unchanged, we really want - // to use exact comparisons here. - ASSERT_EQUAL_EXACT(d1, d2); - - // test negative doubles too - sql << "delete from soci_test"; - d1 = -d1; - - sql << "insert into soci_test(num76) values (:d1)", use(d1); - sql << "select num76 from soci_test", into(d2); - - ASSERT_EQUAL_EXACT(d1, d2); -} - -// test for bulk fetch with single use -TEST_CASE_METHOD(common_tests, "Bulk fetch with single use", "[core][bulk]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(name, id) values('john', 1)"; - sql << "insert into soci_test(name, id) values('george', 2)"; - sql << "insert into soci_test(name, id) values('anthony', 1)"; - sql << "insert into soci_test(name, id) values('marc', 3)"; - sql << "insert into soci_test(name, id) values('julian', 1)"; - - int code = 1; - std::vector names(10); - sql << "select name from soci_test where id = :id order by name", - into(names), use(code); - - CHECK(names.size() == 3); - CHECK(names[0] == "anthony"); - CHECK(names[1] == "john"); - CHECK(names[2] == "julian"); -} - -// test for basic logging support -TEST_CASE_METHOD(common_tests, "Basic logging support", "[core][logging]") -{ - soci::session sql(backEndFactory_, connectString_); - - std::ostringstream log; - sql.set_log_stream(&log); - - try - { - sql << "drop table soci_test1"; - } - catch (...) {} - - CHECK(sql.get_last_query() == "drop table soci_test1"); - - sql.set_log_stream(NULL); - - try - { - sql << "drop table soci_test2"; - } - catch (...) {} - - CHECK(sql.get_last_query() == "drop table soci_test2"); - - sql.set_log_stream(&log); - - try - { - sql << "drop table soci_test3"; - } - catch (...) {} - - CHECK(sql.get_last_query() == "drop table soci_test3"); - CHECK(log.str() == - "drop table soci_test1\n" - "drop table soci_test3\n"); - -} - -// test for rowset creation and copying -TEST_CASE_METHOD(common_tests, "Rowset creation and copying", "[core][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - // Create empty rowset - rowset rs1; - CHECK(rs1.begin() == rs1.end()); - } - { - // Load empty rowset - rowset rs1 = (sql.prepare << "select * from soci_test"); - CHECK(rs1.begin() == rs1.end()); - } - - { - // Copy construction - rowset rs1 = (sql.prepare << "select * from soci_test"); - rowset rs2(rs1); - rowset rs3(rs1); - rowset rs4(rs3); - - CHECK(rs1.begin() == rs2.begin()); - CHECK(rs1.begin() == rs3.begin()); - CHECK(rs1.end() == rs2.end()); - CHECK(rs1.end() == rs3.end()); - } - - if (!tc_.has_multiple_select_bug()) - { - // Assignment - rowset rs1; - rowset rs2 = (sql.prepare << "select * from soci_test"); - rowset rs3 = (sql.prepare << "select * from soci_test"); - rs1 = rs2; - rs3 = rs2; - - CHECK(rs1.begin() == rs2.begin()); - CHECK(rs1.begin() == rs3.begin()); - CHECK(rs1.end() == rs2.end()); - CHECK(rs1.end() == rs3.end()); - } -} - -// test for simple iterating using rowset iterator (without reading data) -TEST_CASE_METHOD(common_tests, "Rowset iteration", "[core][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(id, val) values(1, 10)"; - sql << "insert into soci_test(id, val) values(2, 11)"; - sql << "insert into soci_test(id, val) values(3, NULL)"; - sql << "insert into soci_test(id, val) values(4, NULL)"; - sql << "insert into soci_test(id, val) values(5, 12)"; - { - rowset rs = (sql.prepare << "select * from soci_test"); - - CHECK(5 == std::distance(rs.begin(), rs.end())); - } - { - rowset rs = (sql.prepare << "select * from soci_test"); - - rs.clear(); - CHECK(rs.begin() == rs.end()); - } - } - -} - -// test for reading rowset using iterator -TEST_CASE_METHOD(common_tests, "Reading rows from rowset", "[core][row][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_2(sql)); - { - { - // Empty rowset - rowset rs = (sql.prepare << "select * from soci_test"); - CHECK(0 == std::distance(rs.begin(), rs.end())); - } - - { - // Non-empty rowset - sql << "insert into soci_test values(3.14, 123, \'Johny\'," - << tc_.to_date_time("2005-12-19 22:14:17") - << ", 'a')"; - sql << "insert into soci_test values(6.28, 246, \'Robert\'," - << tc_.to_date_time("2004-10-01 18:44:10") - << ", 'b')"; - - rowset rs = (sql.prepare << "select * from soci_test"); - - rowset::const_iterator it = rs.begin(); - CHECK(it != rs.end()); - - // - // First row - // - row const & r1 = (*it); - - // Properties - CHECK(r1.size() == 5); - CHECK(r1.get_properties(0).get_data_type() == dt_double); - CHECK(r1.get_properties(0).get_db_type() == db_double); - CHECK(r1.get_properties(1).get_data_type() == dt_integer); - CHECK(r1.get_properties(1).get_db_type() == db_int32); - CHECK(r1.get_properties(2).get_data_type() == dt_string); - CHECK(r1.get_properties(2).get_db_type() == db_string); - CHECK(r1.get_properties(3).get_data_type() == dt_date); - CHECK(r1.get_properties(3).get_db_type() == db_date); - CHECK(r1.get_properties(4).get_data_type() == dt_string); - CHECK(r1.get_properties(4).get_db_type() == db_string); - CHECK(r1.get_properties("NUM_INT").get_data_type() == dt_integer); - CHECK(r1.get_properties("NUM_INT").get_db_type() == db_int32); - - // Data - - // Since we didn't specify order by in the above query, - // the 2 rows may be returned in either order - // (If we specify order by, we can't do it in a cross db - // compatible way, because the Oracle table for this has been - // created with lower case column names) - - std::string name = r1.get(2); - - if (name == "Johny") - { - ASSERT_EQUAL_APPROX(r1.get(0), 3.14); - CHECK(r1.get(1) == 123); - CHECK(r1.get(2) == "Johny"); - std::tm t1 = std::tm(); - t1 = r1.get(3); - CHECK(t1.tm_year == 105); - CHECK_EQUAL_PADDED(r1.get(4), "a"); - ASSERT_EQUAL_APPROX(r1.get("NUM_FLOAT"), 3.14); - CHECK(r1.get("NUM_INT") == 123); - CHECK(r1.get("NAME") == "Johny"); - CHECK_EQUAL_PADDED(r1.get("CHR"), "a"); - } - else if (name == "Robert") - { - ASSERT_EQUAL(r1.get(0), 6.28); - CHECK(r1.get(1) == 246); - CHECK(r1.get(2) == "Robert"); - std::tm t1 = r1.get(3); - CHECK(t1.tm_year == 104); - CHECK(r1.get(4) == "b"); - ASSERT_EQUAL(r1.get("NUM_FLOAT"), 6.28); - CHECK(r1.get("NUM_INT") == 246); - CHECK(r1.get("NAME") == "Robert"); - CHECK_EQUAL_PADDED(r1.get("CHR"), "b"); - } - else - { - CAPTURE(name); - FAIL("expected \"Johny\" or \"Robert\""); - } - - // - // Iterate to second row - // - ++it; - CHECK(it != rs.end()); - - // - // Second row - // - row const & r2 = (*it); - - // Properties - CHECK(r2.size() == 5); - CHECK(r2.get_properties(0).get_data_type() == dt_double); - CHECK(r2.get_properties(0).get_db_type() == db_double); - CHECK(r2.get_properties(1).get_data_type() == dt_integer); - CHECK(r2.get_properties(1).get_db_type() == db_int32); - CHECK(r2.get_properties(2).get_data_type() == dt_string); - CHECK(r2.get_properties(2).get_db_type() == db_string); - CHECK(r2.get_properties(3).get_data_type() == dt_date); - CHECK(r2.get_properties(3).get_db_type() == db_date); - CHECK(r2.get_properties(4).get_data_type() == dt_string); - CHECK(r2.get_properties(4).get_db_type() == db_string); - CHECK(r2.get_properties("NUM_INT").get_data_type() == dt_integer); - CHECK(r2.get_properties("NUM_INT").get_db_type() == db_int32); - - std::string newName = r2.get(2); - CHECK(name != newName); - - if (newName == "Johny") - { - ASSERT_EQUAL_APPROX(r2.get(0), 3.14); - CHECK(r2.get(1) == 123); - CHECK(r2.get(2) == "Johny"); - std::tm t2 = r2.get(3); - CHECK(t2.tm_year == 105); - CHECK(r2.get(4) == "a"); - ASSERT_EQUAL_APPROX(r2.get("NUM_FLOAT"), 3.14); - CHECK(r2.get("NUM_INT") == 123); - CHECK(r2.get("NAME") == "Johny"); - CHECK(r2.get("CHR") == "a"); - } - else if (newName == "Robert") - { - ASSERT_EQUAL_APPROX(r2.get(0), 6.28); - CHECK(r2.get(1) == 246); - CHECK(r2.get(2) == "Robert"); - std::tm t2 = r2.get(3); - CHECK(t2.tm_year == 104); - CHECK_EQUAL_PADDED(r2.get(4), "b"); - ASSERT_EQUAL_APPROX(r2.get("NUM_FLOAT"), 6.28); - CHECK(r2.get("NUM_INT") == 246); - CHECK(r2.get("NAME") == "Robert"); - CHECK_EQUAL_PADDED(r2.get("CHR"), "b"); - } - else - { - CAPTURE(newName); - FAIL("expected \"Johny\" or \"Robert\""); - } - } - - { - // Non-empty rowset with NULL values - sql << "insert into soci_test " - << "(num_int, num_float , name, sometime, chr) " - << "values (0, NULL, NULL, NULL, NULL)"; - - rowset rs = (sql.prepare - << "select num_int, num_float, name, sometime, chr " - << "from soci_test where num_int = 0"); - - rowset::const_iterator it = rs.begin(); - CHECK(it != rs.end()); - - // - // First row - // - row const& r1 = (*it); - - // Properties - CHECK(r1.size() == 5); - CHECK(r1.get_properties(0).get_data_type() == dt_integer); - CHECK(r1.get_properties(0).get_db_type() == db_int32); - CHECK(r1.get_properties(1).get_data_type() == dt_double); - CHECK(r1.get_properties(1).get_db_type() == db_double); - CHECK(r1.get_properties(2).get_data_type() == dt_string); - CHECK(r1.get_properties(2).get_db_type() == db_string); - CHECK(r1.get_properties(3).get_data_type() == dt_date); - CHECK(r1.get_properties(3).get_db_type() == db_date); - CHECK(r1.get_properties(4).get_data_type() == dt_string); - CHECK(r1.get_properties(4).get_db_type() == db_string); - - // Data - CHECK(r1.get_indicator(0) == soci::i_ok); - CHECK(r1.get(0) == 0); - CHECK(r1.get_indicator(1) == soci::i_null); - CHECK(r1.get_indicator(2) == soci::i_null); - CHECK(r1.get_indicator(3) == soci::i_null); - CHECK(r1.get_indicator(4) == soci::i_null); - } - } -} - -// test for reading rowset using iterator -TEST_CASE_METHOD(common_tests, "Reading ints from rowset", "[core][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(id) values(1)"; - sql << "insert into soci_test(id) values(2)"; - sql << "insert into soci_test(id) values(3)"; - sql << "insert into soci_test(id) values(4)"; - sql << "insert into soci_test(id) values(5)"; - { - rowset rs = (sql.prepare << "select id from soci_test order by id asc"); - - // 1st row - rowset::const_iterator pos = rs.begin(); - CHECK(1 == (*pos)); - - // 3rd row - std::advance(pos, 2); - CHECK(3 == (*pos)); - - // 5th row - std::advance(pos, 2); - CHECK(5 == (*pos)); - - // The End - ++pos; - CHECK(pos == rs.end()); - } - } - -} - -// test for handling 'use' and reading rowset using iterator -TEST_CASE_METHOD(common_tests, "Reading strings from rowset", "[core][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(str) values('abc')"; - sql << "insert into soci_test(str) values('def')"; - sql << "insert into soci_test(str) values('ghi')"; - sql << "insert into soci_test(str) values('jkl')"; - { - // Expected result in numbers - std::string idle("def"); - rowset rs1 = (sql.prepare - << "select str from soci_test where str = :idle", - use(idle)); - - CHECK(1 == std::distance(rs1.begin(), rs1.end())); - - // Expected result in value - idle = "jkl"; - rowset rs2 = (sql.prepare - << "select str from soci_test where str = :idle", - use(idle)); - - CHECK(idle == *(rs2.begin())); - } - } - -} - -// test for handling troublemaker -TEST_CASE_METHOD(common_tests, "Rowset expected exception", "[core][exception][rowset]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - sql << "insert into soci_test(str) values('abc')"; - - std::string troublemaker; - CHECK_THROWS_AS( - rowset((sql.prepare << "select str from soci_test", into(troublemaker))), - soci_error - ); -} - -// functor for next test -struct THelper -{ - THelper() - : val_() - { - } - void operator()(int i) - { - val_ = i; - } - int val_; -}; - -// test for handling NULL values with expected exception: -// "Null value fetched and no indicator defined." -TEST_CASE_METHOD(common_tests, "NULL expected exception", "[core][exception][null]") -{ - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - sql << "insert into soci_test(val) values(1)"; - sql << "insert into soci_test(val) values(2)"; - sql << "insert into soci_test(val) values(NULL)"; - sql << "insert into soci_test(val) values(3)"; - - rowset rs = (sql.prepare << "select val from soci_test order by val asc"); - - CHECK_THROWS_AS( std::for_each(rs.begin(), rs.end(), THelper()), soci_error ); -} - -TEST_CASE_METHOD(common_tests, "soci_error is nothrow", "[core][exception][nothrow]") -{ - CHECK(std::is_nothrow_copy_assignable::value == true); - CHECK(std::is_nothrow_copy_constructible::value == true); - CHECK(std::is_nothrow_destructible::value == true); -} - -// This is like the first dynamic binding test but with rowset and iterators use -TEST_CASE_METHOD(common_tests, "Dynamic binding with rowset", "[core][dynamic][type_conversion]") -{ - soci::session sql(backEndFactory_, connectString_); - - sql.uppercase_column_names(true); - - { - auto_table_creator tableCreator(tc_.table_creator_3(sql)); - - PhonebookEntry p1; - sql << "select * from soci_test", into(p1); - CHECK(p1.name == ""); - CHECK(p1.phone == ""); - - p1.name = "david"; - - sql << "insert into soci_test values(:NAME, :PHONE)", use(p1); - sql << "insert into soci_test values('john', '(404)123-4567')"; - sql << "insert into soci_test values('doe', '(404)123-4567')"; - - rowset rs = (sql.prepare << "select * from soci_test"); - - int count = 0; - for (rowset::const_iterator it = rs.begin(); it != rs.end(); ++it) - { - ++count; - PhonebookEntry const& p2 = (*it); - if (p2.name == "david") - { - // see type_conversion - CHECK(p2.phone ==""); - } - else - { - CHECK(p2.phone == "(404)123-4567"); - } - } - - CHECK(3 == count); - } -} - -#ifdef SOCI_HAVE_BOOST - -// test for handling NULL values with boost::optional -// (both into and use) -TEST_CASE_METHOD(common_tests, "NULL with optional", "[core][boost][null]") -{ - - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator0(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(val) values(7)"; - - { - // verify non-null value is fetched correctly - boost::optional opt; - sql << "select val from soci_test", into(opt); - CHECK(opt.is_initialized()); - CHECK(opt.get() == 7); - - // indicators can be used with optional - // (although that's just a consequence of implementation, - // not an intended feature - but let's test it anyway) - indicator ind; - opt.reset(); - sql << "select val from soci_test", into(opt, ind); - CHECK(opt.is_initialized()); - CHECK(opt.get() == 7); - CHECK(ind == i_ok); - - // verify null value is fetched correctly - sql << "select i1 from soci_test", into(opt); - CHECK(opt.is_initialized() == false); - - // and with indicator - opt = 5; - sql << "select i1 from soci_test", into(opt, ind); - CHECK(opt.is_initialized() == false); - CHECK(ind == i_null); - - // verify non-null is inserted correctly - opt = 3; - sql << "update soci_test set val = :v", use(opt); - int j = 0; - sql << "select val from soci_test", into(j); - CHECK(j == 3); - - // verify null is inserted correctly - opt.reset(); - sql << "update soci_test set val = :v", use(opt); - ind = i_ok; - sql << "select val from soci_test", into(j, ind); - CHECK(ind == i_null); - } - - // vector tests (select) - - { - sql << "delete from soci_test"; - - // simple readout of non-null data - - sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; - sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; - sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; - sql << "insert into soci_test(id, val, str) values(4, 8, null)"; - sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; - - std::vector > v(10); - sql << "select val from soci_test order by val", into(v); - - CHECK(v.size() == 5); - CHECK(v[0].is_initialized()); - CHECK(v[0].get() == 5); - CHECK(v[1].is_initialized()); - CHECK(v[1].get() == 6); - CHECK(v[2].is_initialized()); - CHECK(v[2].get() == 7); - CHECK(v[3].is_initialized()); - CHECK(v[3].get() == 8); - CHECK(v[4].is_initialized()); - CHECK(v[4].get() == 9); - - // readout of nulls - - sql << "update soci_test set val = null where id = 2 or id = 4"; - - std::vector ids(5); - sql << "select id, val from soci_test order by id", into(ids), into(v); - - CHECK(v.size() == 5); - CHECK(ids.size() == 5); - CHECK(v[0].is_initialized()); - CHECK(v[0].get() == 5); - CHECK(v[1].is_initialized() == false); - CHECK(v[2].is_initialized()); - CHECK(v[2].get() == 7); - CHECK(v[3].is_initialized() == false); - CHECK(v[4].is_initialized()); - CHECK(v[4].get() == 9); - - // readout with statement preparation - - int id = 1; - - ids.resize(3); - v.resize(3); - statement st = (sql.prepare << - "select id, val from soci_test order by id", into(ids), into(v)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != v.size(); ++i) - { - CHECK(id == ids[i]); - - if (id == 2 || id == 4) - { - CHECK(v[i].is_initialized() == false); - } - else - { - CHECK(v[i].is_initialized()); - CHECK(v[i].get() == id + 4); - } - - ++id; - } - - ids.resize(3); - v.resize(3); - } - CHECK(id == 6); - } - - // and why not stress iterators and the dynamic binding, too! - - { - rowset rs = (sql.prepare << "select id, val, str from soci_test order by id"); - - rowset::const_iterator it = rs.begin(); - CHECK(it != rs.end()); - - row const& r1 = (*it); - - CHECK(r1.size() == 3); - - // Note: for the reason of differences between number(x,y) type and - // binary representation of integers, the following commented assertions - // do not work for Oracle. - // The problem is that for this single table the data type used in Oracle - // table creator for the id column is number(10,0), - // which allows to insert all int values. - // On the other hand, the column description scheme used in the Oracle - // backend figures out that the natural type for such a column - // is eUnsignedInt - this makes the following assertions fail. - // Other database backends (like PostgreSQL) use other types like int - // and this not only allows to insert all int values (obviously), - // but is also recognized as int (obviously). - // There is a similar problem with stream-like extraction, - // where internally get is called and the type mismatch is detected - // for the id column - that's why the code below skips this column - // and tests the remaining column only. - - //CHECK(r1.get_properties(0).get_data_type() == dt_integer); - //CHECK(r1.get_properties(0).get_exchnage_data_type() == db_int32); - CHECK(r1.get_properties(1).get_data_type() == dt_integer); - CHECK(r1.get_properties(1).get_db_type() == db_int32); - CHECK(r1.get_properties(2).get_data_type() == dt_string); - CHECK(r1.get_properties(2).get_db_type() == db_string); - //CHECK(r1.get(0) == 1); - CHECK(r1.get(1) == 5); - CHECK(r1.get(2) == "abc"); - CHECK(r1.get >(1).is_initialized()); - CHECK(r1.get >(1).get() == 5); - CHECK(r1.get >(2).is_initialized()); - CHECK(r1.get >(2).get() == "abc"); - - ++it; - - row const& r2 = (*it); - - CHECK(r2.size() == 3); - - // CHECK(r2.get_properties(0).get_data_type() == dt_integer); - // CHECK(r2.get_properties(0).get_db_type() == db_int32); - CHECK(r2.get_properties(1).get_data_type() == dt_integer); - CHECK(r2.get_properties(1).get_db_type() == db_int32); - CHECK(r2.get_properties(2).get_data_type() == dt_string); - CHECK(r2.get_properties(2).get_db_type() == db_string); - //CHECK(r2.get(0) == 2); - try - { - // expect exception here, this is NULL value - (void)r1.get(1); - FAIL("expected exception not thrown"); - } - catch (soci_error const &) {} - - // but we can read it as optional - CHECK(r2.get >(1).is_initialized() == false); - - // stream-like data extraction - - ++it; - row const &r3 = (*it); - - boost::optional io; - boost::optional so; - - r3.skip(); // move to val and str columns - r3 >> io >> so; - - CHECK(io.is_initialized()); - CHECK(io.get() == 7); - CHECK(so.is_initialized()); - CHECK(so.get() == "ghi"); - - ++it; - row const &r4 = (*it); - - r3.skip(); // move to val and str columns - r4 >> io >> so; - - CHECK(io.is_initialized() == false); - CHECK(so.is_initialized() == false); - } - - // inserts of non-null const data - { - sql << "delete from soci_test"; - - const int id = 10; - const boost::optional val = 11; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(id, "id"), - use(val, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 11); - } - - // bulk inserts of non-null data - - { - sql << "delete from soci_test"; - - std::vector ids; - std::vector > v; - - ids.push_back(10); v.push_back(20); - ids.push_back(11); v.push_back(21); - ids.push_back(12); v.push_back(22); - ids.push_back(13); v.push_back(23); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(ids, "id"), use(v, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 86); - - // bulk inserts of some-null data - - sql << "delete from soci_test"; - - v[2].reset(); - v[3].reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(ids, "id"), use(v, "val"); - - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 41); - } - - - // bulk inserts of non-null data with const vector - - { - sql << "delete from soci_test"; - - std::vector ids; - std::vector > v; - - ids.push_back(10); v.push_back(20); - ids.push_back(11); v.push_back(21); - ids.push_back(12); v.push_back(22); - ids.push_back(13); v.push_back(23); - - const std::vector& cref_ids = ids; - const std::vector >& cref_v = v; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(cref_ids, "id"), - use(cref_v, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 86); - - // bulk inserts of some-null data - - sql << "delete from soci_test"; - - v[2].reset(); - v[3].reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(cref_ids, "id"), - use(cref_v, "val"); - - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 41); - } - - // composability with user conversions - - { - sql << "delete from soci_test"; - - boost::optional omi1; - boost::optional omi2; - - omi1 = MyInt(125); - omi2.reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(omi1), use(omi2); - - sql << "select id, val from soci_test", into(omi2), into(omi1); - - CHECK(omi1.is_initialized() == false); - CHECK(omi2.is_initialized()); - CHECK(omi2.get().get() == 125); - } - - // use with const optional and user conversions - - { - sql << "delete from soci_test"; - - boost::optional omi1; - boost::optional omi2; - - omi1 = MyInt(125); - omi2.reset(); - - boost::optional const & comi1 = omi1; - boost::optional const & comi2 = omi2; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(comi1), use(comi2); - - sql << "select id, val from soci_test", into(omi2), into(omi1); - - CHECK(omi1.is_initialized() == false); - CHECK(omi2.is_initialized()); - CHECK(omi2.get().get() == 125); - } - - // use with rowset and table containing null values - - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(id, val) values(1, 10)"; - sql << "insert into soci_test(id, val) values(2, 11)"; - sql << "insert into soci_test(id, val) values(3, NULL)"; - sql << "insert into soci_test(id, val) values(4, 13)"; - - rowset > rs = (sql.prepare << - "select val from soci_test order by id asc"); - - // 1st row - rowset >::const_iterator pos = rs.begin(); - CHECK((*pos).is_initialized()); - CHECK(10 == (*pos).get()); - - // 2nd row - ++pos; - CHECK((*pos).is_initialized()); - CHECK(11 == (*pos).get()); - - // 3rd row - ++pos; - CHECK((*pos).is_initialized() == false); - - // 4th row - ++pos; - CHECK((*pos).is_initialized()); - CHECK(13 == (*pos).get()); - } - - // inserting using an i_null indicator with a boost::optional should - // insert null, even if the optional is valid, just as with standard - // types - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - { - indicator ind = i_null; - boost::optional v1(10); - sql << "insert into soci_test(id, val) values(1, :val)", - use(v1, ind); - } - - // verify the value is fetched correctly as null - { - indicator ind; - boost::optional opt; - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 1", into(opt, ind); - CHECK(ind == i_null); - CHECK(!opt.is_initialized()); - } - } - - // prepared statement inserting non-null and null values alternatively - // (without passing an explicit indicator) - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - { - int id; - boost::optional val; - statement st = (sql.prepare - << "insert into soci_test(id, val) values (:id, :val)", - use(id), use(val)); - - id = 1; - val = 10; - st.execute(true); - - id = 2; - val = boost::optional(); - st.execute(true); - - id = 3; - val = 11; - st.execute(true); - } - - // verify values are fetched correctly - { - indicator ind; - boost::optional opt; - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 1", into(opt, ind); - CHECK(ind == i_ok); - CHECK(opt.is_initialized()); - CHECK(opt.get() == 10); - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 2", into(opt, ind); - CHECK(ind == i_null); - CHECK(!opt.is_initialized()); - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 3", into(opt, ind); - CHECK(ind == i_ok); - REQUIRE(opt.is_initialized()); - CHECK(opt.get() == 11); - } - } - } -} - -#endif // SOCI_HAVE_BOOST - -#ifdef SOCI_HAVE_CXX17 - -// test for handling NULL values with std::optional -// (both into and use) -TEST_CASE_METHOD(common_tests, "NULL with std optional", "[core][null]") -{ - - soci::session sql(backEndFactory_, connectString_); - - // create and populate the test table - auto_table_creator tableCreator0(tc_.table_creator_1(sql)); - { - sql << "insert into soci_test(val) values(7)"; - - { - // verify non-null value is fetched correctly - std::optional opt; - sql << "select val from soci_test", into(opt); - CHECK(opt.has_value()); - CHECK(opt.value() == 7); - - // indicators can be used with optional - // (although that's just a consequence of implementation, - // not an intended feature - but let's test it anyway) - indicator ind; - opt.reset(); - sql << "select val from soci_test", into(opt, ind); - CHECK(opt.has_value()); - CHECK(opt.value() == 7); - CHECK(ind == i_ok); - - // verify null value is fetched correctly - sql << "select i1 from soci_test", into(opt); - CHECK(opt.has_value() == false); - - // and with indicator - opt = 5; - sql << "select i1 from soci_test", into(opt, ind); - CHECK(opt.has_value() == false); - CHECK(ind == i_null); - - // verify non-null is inserted correctly - opt = 3; - sql << "update soci_test set val = :v", use(opt); - int j = 0; - sql << "select val from soci_test", into(j); - CHECK(j == 3); - - // verify null is inserted correctly - opt.reset(); - sql << "update soci_test set val = :v", use(opt); - ind = i_ok; - sql << "select val from soci_test", into(j, ind); - CHECK(ind == i_null); - } - - // vector tests (select) - - { - sql << "delete from soci_test"; - - // simple readout of non-null data - - sql << "insert into soci_test(id, val, str) values(1, 5, \'abc\')"; - sql << "insert into soci_test(id, val, str) values(2, 6, \'def\')"; - sql << "insert into soci_test(id, val, str) values(3, 7, \'ghi\')"; - sql << "insert into soci_test(id, val, str) values(4, 8, null)"; - sql << "insert into soci_test(id, val, str) values(5, 9, \'mno\')"; - - std::vector > v(10); - sql << "select val from soci_test order by val", into(v); - - CHECK(v.size() == 5); - CHECK(v[0].has_value()); - CHECK(v[0].value() == 5); - CHECK(v[1].has_value()); - CHECK(v[1].value() == 6); - CHECK(v[2].has_value()); - CHECK(v[2].value() == 7); - CHECK(v[3].has_value()); - CHECK(v[3].value() == 8); - CHECK(v[4].has_value()); - CHECK(v[4].value() == 9); - - // readout of nulls - - sql << "update soci_test set val = null where id = 2 or id = 4"; - - std::vector ids(5); - sql << "select id, val from soci_test order by id", into(ids), into(v); - - CHECK(v.size() == 5); - CHECK(ids.size() == 5); - CHECK(v[0].has_value()); - CHECK(v[0].value() == 5); - CHECK(v[1].has_value() == false); - CHECK(v[2].has_value()); - CHECK(v[2].value() == 7); - CHECK(v[3].has_value() == false); - CHECK(v[4].has_value()); - CHECK(v[4].value() == 9); - - // readout with statement preparation - - int id = 1; - - ids.resize(3); - v.resize(3); - statement st = (sql.prepare << - "select id, val from soci_test order by id", into(ids), into(v)); - st.execute(); - while (st.fetch()) - { - for (std::size_t i = 0; i != v.size(); ++i) - { - CHECK(id == ids[i]); - - if (id == 2 || id == 4) - { - CHECK(v[i].has_value() == false); - } - else - { - CHECK(v[i].has_value()); - CHECK(v[i].value() == id + 4); - } - - ++id; - } - - ids.resize(3); - v.resize(3); - } - CHECK(id == 6); - } - - // and why not stress iterators and the dynamic binding, too! - - { - rowset rs = (sql.prepare << "select id, val, str from soci_test order by id"); - - rowset::const_iterator it = rs.begin(); - CHECK(it != rs.end()); - - row const& r1 = (*it); - - CHECK(r1.size() == 3); - - // Note: for the reason of differences between number(x,y) type and - // binary representation of integers, the following commented assertions - // do not work for Oracle. - // The problem is that for this single table the data type used in Oracle - // table creator for the id column is number(10,0), - // which allows to insert all int values. - // On the other hand, the column description scheme used in the Oracle - // backend figures out that the natural type for such a column - // is eUnsignedInt - this makes the following assertions fail. - // Other database backends (like PostgreSQL) use other types like int - // and this not only allows to insert all int values (obviously), - // but is also recognized as int (obviously). - // There is a similar problem with stream-like extraction, - // where internally get is called and the type mismatch is detected - // for the id column - that's why the code below skips this column - // and tests the remaining column only. - - //CHECK(r1.get_properties(0).get_data_type() == dt_integer); - //CHECK(r1.get_properties(0).get_db_type() == db_int32); - CHECK(r1.get_properties(1).get_data_type() == dt_integer); - CHECK(r1.get_properties(1).get_db_type() == db_int32); - CHECK(r1.get_properties(2).get_data_type() == dt_string); - CHECK(r1.get_properties(2).get_db_type() == db_string); - //CHECK(r1.get(0) == 1); - CHECK(r1.get(1) == 5); - CHECK(r1.get(2) == "abc"); - CHECK(r1.get >(1).has_value()); - CHECK(r1.get >(1).value() == 5); - CHECK(r1.get >(2).has_value()); - CHECK(r1.get >(2).value() == "abc"); - - ++it; - - row const& r2 = (*it); - - CHECK(r2.size() == 3); - - // CHECK(r2.get_properties(0).get_data_type() == dt_integer); - // CHECK(r2.get_properties(0).get_db_type() == db_int32); - CHECK(r2.get_properties(1).get_data_type() == dt_integer); - CHECK(r2.get_properties(1).get_db_type() == db_int32); - CHECK(r2.get_properties(2).get_data_type() == dt_string); - CHECK(r2.get_properties(2).get_db_type() == db_string); - //CHECK(r2.get(0) == 2); - try - { - // expect exception here, this is NULL value - (void)r1.get(1); - FAIL("expected exception not thrown"); - } - catch (soci_error const &) {} - - // but we can read it as optional - CHECK(r2.get >(1).has_value() == false); - - // stream-like data extraction - - ++it; - row const &r3 = (*it); - - std::optional io; - std::optional so; - - r3.skip(); // move to val and str columns - r3 >> io >> so; - - CHECK(io.has_value()); - CHECK(io.value() == 7); - CHECK(so.has_value()); - CHECK(so.value() == "ghi"); - - ++it; - row const &r4 = (*it); - - r3.skip(); // move to val and str columns - r4 >> io >> so; - - CHECK(io.has_value() == false); - CHECK(so.has_value() == false); - } - - // inserts of non-null const data - { - sql << "delete from soci_test"; - - const int id = 10; - const std::optional val = 11; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(id, "id"), - use(val, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 11); - } - - // bulk inserts of non-null data - - { - sql << "delete from soci_test"; - - std::vector ids; - std::vector > v; - - ids.push_back(10); v.push_back(20); - ids.push_back(11); v.push_back(21); - ids.push_back(12); v.push_back(22); - ids.push_back(13); v.push_back(23); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(ids, "id"), use(v, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 86); - - // bulk inserts of some-null data - - sql << "delete from soci_test"; - - v[2].reset(); - v[3].reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(ids, "id"), use(v, "val"); - - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 41); - } - - - // bulk inserts of non-null data with const vector - - { - sql << "delete from soci_test"; - - std::vector ids; - std::vector > v; - - ids.push_back(10); v.push_back(20); - ids.push_back(11); v.push_back(21); - ids.push_back(12); v.push_back(22); - ids.push_back(13); v.push_back(23); - - const std::vector& cref_ids = ids; - const std::vector >& cref_v = v; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(cref_ids, "id"), - use(cref_v, "val"); - - int sum; - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 86); - - // bulk inserts of some-null data - - sql << "delete from soci_test"; - - v[2].reset(); - v[3].reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(cref_ids, "id"), - use(cref_v, "val"); - - sql << "select sum(val) from soci_test", into(sum); - CHECK(sum == 41); - } - - // composability with user conversions - - { - sql << "delete from soci_test"; - - std::optional omi1; - std::optional omi2; - - omi1 = MyInt(125); - omi2.reset(); - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(omi1), use(omi2); - - sql << "select id, val from soci_test", into(omi2), into(omi1); - - CHECK(omi1.has_value() == false); - CHECK(omi2.has_value()); - CHECK(omi2.value().get() == 125); - } - - // use with const optional and user conversions - - { - sql << "delete from soci_test"; - - std::optional omi1; - std::optional omi2; - - omi1 = MyInt(125); - omi2.reset(); - - std::optional const & comi1 = omi1; - std::optional const & comi2 = omi2; - - sql << "insert into soci_test(id, val) values(:id, :val)", - use(comi1), use(comi2); - - sql << "select id, val from soci_test", into(omi2), into(omi1); - - CHECK(omi1.has_value() == false); - CHECK(omi2.has_value()); - CHECK(omi2.value().get() == 125); - } - - // use with rowset and table containing null values - - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - sql << "insert into soci_test(id, val) values(1, 10)"; - sql << "insert into soci_test(id, val) values(2, 11)"; - sql << "insert into soci_test(id, val) values(3, NULL)"; - sql << "insert into soci_test(id, val) values(4, 13)"; - - rowset > rs = (sql.prepare << - "select val from soci_test order by id asc"); - - // 1st row - rowset >::const_iterator pos = rs.begin(); - CHECK((*pos).has_value()); - CHECK(10 == (*pos).value()); - - // 2nd row - ++pos; - CHECK((*pos).has_value()); - CHECK(11 == (*pos).value()); - - // 3rd row - ++pos; - CHECK((*pos).has_value() == false); - - // 4th row - ++pos; - CHECK((*pos).has_value()); - CHECK(13 == (*pos).value()); - } - - // inserting using an i_null indicator with a std::optional should - // insert null, even if the optional is valid, just as with standard - // types - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - { - indicator ind = i_null; - std::optional v1(10); - sql << "insert into soci_test(id, val) values(1, :val)", - use(v1, ind); - } - - // verify the value is fetched correctly as null - { - indicator ind; - std::optional opt; - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 1", into(opt, ind); - CHECK(ind == i_null); - CHECK(!opt.has_value()); - } - } - - // prepared statement inserting non-null and null values alternatively - // (without passing an explicit indicator) - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - { - int id; - std::optional val; - statement st = (sql.prepare - << "insert into soci_test(id, val) values (:id, :val)", - use(id), use(val)); - - id = 1; - val = 10; - st.execute(true); - - id = 2; - val = std::optional(); - st.execute(true); - - id = 3; - val = 11; - st.execute(true); - } - - // verify values are fetched correctly - { - indicator ind; - std::optional opt; - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 1", into(opt, ind); - CHECK(ind == i_ok); - CHECK(opt.has_value()); - CHECK(opt.value() == 10); - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 2", into(opt, ind); - CHECK(ind == i_null); - CHECK(!opt.has_value()); - - ind = i_truncated; - opt = 0; - sql << "select val from soci_test where id = 3", into(opt, ind); - CHECK(ind == i_ok); - REQUIRE(opt.has_value()); - CHECK(opt.value() == 11); - } - } - } -} -#endif - -// connection and reconnection tests -TEST_CASE_METHOD(common_tests, "Connection and reconnection", "[core][connect]") -{ - { - // empty session - soci::session sql; - - CHECK(!sql.is_connected()); - - // idempotent: - sql.close(); - - try - { - sql.reconnect(); - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "Cannot reconnect without previous connection."); - } - - // open from empty session - sql.open(backEndFactory_, connectString_); - CHECK(sql.is_connected()); - sql.close(); - CHECK(!sql.is_connected()); - - // reconnecting from closed session - sql.reconnect(); - CHECK(sql.is_connected()); - - // opening already connected session - try - { - sql.open(backEndFactory_, connectString_); - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "Cannot open already connected session."); - } - - CHECK(sql.is_connected()); - sql.close(); - - // open from closed - sql.open(backEndFactory_, connectString_); - CHECK(sql.is_connected()); - - // reconnect from already connected session - sql.reconnect(); - CHECK(sql.is_connected()); - } - - { - soci::session sql; - - try - { - sql << "this statement cannot execute"; - FAIL("expected exception not thrown"); - } - catch (soci_error const &e) - { - CHECK(e.get_error_message() == - "Session is not connected."); - } - } - - { - // check move semantics of session - - #if __GNUC__ >= 13 || defined (__clang__) - SOCI_GCC_WARNING_SUPPRESS(self-move) - #endif - - soci::session sql_0; - soci::session sql_1 = std::move(sql_0); - - CHECK(!sql_0.is_connected()); - CHECK(!sql_1.is_connected()); - - sql_0.open(backEndFactory_, connectString_); - CHECK(sql_0.is_connected()); - CHECK(sql_0.get_backend()); - - sql_1 = std::move(sql_0); - CHECK(!sql_0.is_connected()); - CHECK(!sql_0.get_backend()); - CHECK(sql_1.is_connected()); - CHECK(sql_1.get_backend()); - - sql_1 = std::move(sql_1); - CHECK(sql_1.is_connected()); - CHECK(sql_1.get_backend()); - - #if __GNUC__ >= 13 || defined (__clang__) - SOCI_GCC_WARNING_RESTORE(self-move) - #endif - } -} - -#ifdef SOCI_HAVE_BOOST - -TEST_CASE_METHOD(common_tests, "Boost tuple", "[core][boost][tuple]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_2(sql)); - { - boost::tuple t1(3.5, 7, "Joe Hacker"); - ASSERT_EQUAL(t1.get<0>(), 3.5); - CHECK(t1.get<1>() == 7); - CHECK(t1.get<2>() == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // basic query - - boost::tuple t2; - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(t2.get<0>(), 3.5); - CHECK(t2.get<1>() == 7); - CHECK(t2.get<2>() == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // composability with boost::optional - - // use: - boost::tuple, std::string> t1( - 3.5, boost::optional(7), "Joe Hacker"); - ASSERT_EQUAL(t1.get<0>(), 3.5); - CHECK(t1.get<1>().is_initialized()); - CHECK(t1.get<1>().get() == 7); - CHECK(t1.get<2>() == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::tuple, std::string> t2; - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(t2.get<0>(), 3.5); - CHECK(t2.get<1>().is_initialized()); - CHECK(t2.get<1>().get() == 7); - CHECK(t2.get<2>() == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // composability with user-provided conversions - - // use: - boost::tuple t1(3.5, 7, "Joe Hacker"); - ASSERT_EQUAL(t1.get<0>(), 3.5); - CHECK(t1.get<1>().get() == 7); - CHECK(t1.get<2>() == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::tuple t2; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(t2.get<0>(), 3.5); - CHECK(t2.get<1>().get() == 7); - CHECK(t2.get<2>() == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // let's have fun - composition of tuple, optional and user-defined type - - // use: - boost::tuple, std::string> t1( - 3.5, boost::optional(7), "Joe Hacker"); - ASSERT_EQUAL(t1.get<0>(), 3.5); - CHECK(t1.get<1>().is_initialized()); - CHECK(t1.get<1>().get().get() == 7); - CHECK(t1.get<2>() == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::tuple, std::string> t2; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(t2.get<0>(), 3.5); - CHECK(t2.get<1>().is_initialized()); - CHECK(t2.get<1>().get().get() == 7); - CHECK(t2.get<2>() == "Joe Hacker"); - - sql << "update soci_test set num_int = NULL"; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(t2.get<0>(), 3.5); - CHECK(t2.get<1>().is_initialized() == false); - CHECK(t2.get<2>() == "Joe Hacker"); - } - - { - // rowset - - sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; - sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; - sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; - - typedef boost::tuple, std::string> T; - - rowset rs = (sql.prepare - << "select num_float, num_int, name from soci_test order by num_float asc"); - - rowset::const_iterator pos = rs.begin(); - - ASSERT_EQUAL(pos->get<0>(), 3.5); - CHECK(pos->get<1>().is_initialized() == false); - CHECK(pos->get<2>() == "Joe Hacker"); - - ++pos; - ASSERT_EQUAL(pos->get<0>(), 4.0); - CHECK(pos->get<1>().is_initialized()); - CHECK(pos->get<1>().get() == 8); - CHECK(pos->get<2>() == "Tony Coder"); - - ++pos; - ASSERT_EQUAL(pos->get<0>(), 4.5); - CHECK(pos->get<1>().is_initialized() == false); - CHECK(pos->get<2>() == "Cecile Sharp"); - - ++pos; - ASSERT_EQUAL(pos->get<0>(), 5.0); - CHECK(pos->get<1>().is_initialized()); - CHECK(pos->get<1>().get() == 10); - CHECK(pos->get<2>() == "Djhava Ravaa"); - - ++pos; - CHECK(pos == rs.end()); - } -} - -#if defined(BOOST_VERSION) && BOOST_VERSION >= 103500 - -TEST_CASE_METHOD(common_tests, "Boost fusion", "[core][boost][fusion]") -{ - - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_2(sql)); - { - boost::fusion::vector t1(3.5, 7, "Joe Hacker"); - ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); - CHECK(boost::fusion::at_c<1>(t1) == 7); - CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // basic query - - boost::fusion::vector t2; - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); - CHECK(boost::fusion::at_c<1>(t2) == 7); - CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // composability with boost::optional - - // use: - boost::fusion::vector, std::string> t1( - 3.5, boost::optional(7), "Joe Hacker"); - ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); - CHECK(boost::fusion::at_c<1>(t1).is_initialized()); - CHECK(boost::fusion::at_c<1>(t1).get() == 7); - CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::fusion::vector, std::string> t2; - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); - CHECK(boost::fusion::at_c<1>(t2).is_initialized()); - CHECK(boost::fusion::at_c<1>(t2) == 7); - CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // composability with user-provided conversions - - // use: - boost::fusion::vector t1(3.5, 7, "Joe Hacker"); - ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); - CHECK(boost::fusion::at_c<1>(t1).get() == 7); - CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::fusion::vector t2; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); - CHECK(boost::fusion::at_c<1>(t2).get() == 7); - CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); - - sql << "delete from soci_test"; - } - - { - // let's have fun - composition of tuple, optional and user-defined type - - // use: - boost::fusion::vector, std::string> t1( - 3.5, boost::optional(7), "Joe Hacker"); - ASSERT_EQUAL(boost::fusion::at_c<0>(t1), 3.5); - CHECK(boost::fusion::at_c<1>(t1).is_initialized()); - CHECK(boost::fusion::at_c<1>(t1).get().get() == 7); - CHECK(boost::fusion::at_c<2>(t1) == "Joe Hacker"); - - sql << "insert into soci_test(num_float, num_int, name) values(:d, :i, :s)", use(t1); - - // into: - boost::fusion::vector, std::string> t2; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); - CHECK(boost::fusion::at_c<1>(t2).is_initialized()); - CHECK(boost::fusion::at_c<1>(t2).get().get() == 7); - CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); - - sql << "update soci_test set num_int = NULL"; - - sql << "select num_float, num_int, name from soci_test", into(t2); - - ASSERT_EQUAL(boost::fusion::at_c<0>(t2), 3.5); - CHECK(boost::fusion::at_c<1>(t2).is_initialized() == false); - CHECK(boost::fusion::at_c<2>(t2) == "Joe Hacker"); - } - - { - // rowset - - sql << "insert into soci_test(num_float, num_int, name) values(4.0, 8, 'Tony Coder')"; - sql << "insert into soci_test(num_float, num_int, name) values(4.5, NULL, 'Cecile Sharp')"; - sql << "insert into soci_test(num_float, num_int, name) values(5.0, 10, 'Djhava Ravaa')"; - - typedef boost::fusion::vector, std::string> T; - - rowset rs = (sql.prepare - << "select num_float, num_int, name from soci_test order by num_float asc"); - - rowset::const_iterator pos = rs.begin(); - - ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 3.5); - CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); - CHECK(boost::fusion::at_c<2>(*pos) == "Joe Hacker"); - - ++pos; - ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.0); - CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); - CHECK(boost::fusion::at_c<1>(*pos).get() == 8); - CHECK(boost::fusion::at_c<2>(*pos) == "Tony Coder"); - - ++pos; - ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 4.5); - CHECK(boost::fusion::at_c<1>(*pos).is_initialized() == false); - CHECK(boost::fusion::at_c<2>(*pos) == "Cecile Sharp"); - - ++pos; - ASSERT_EQUAL(boost::fusion::at_c<0>(*pos), 5.0); - CHECK(boost::fusion::at_c<1>(*pos).is_initialized()); - CHECK(boost::fusion::at_c<1>(*pos).get() == 10); - CHECK(boost::fusion::at_c<2>(*pos) == "Djhava Ravaa"); - - ++pos; - CHECK(pos == rs.end()); - } -} - -#endif // defined(BOOST_VERSION) && BOOST_VERSION >= 103500 - -// test for boost::gregorian::date -TEST_CASE_METHOD(common_tests, "Boost date", "[core][boost][datetime]") -{ - soci::session sql(backEndFactory_, connectString_); - - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - std::tm nov15 = std::tm(); - nov15.tm_year = 105; - nov15.tm_mon = 10; - nov15.tm_mday = 15; - nov15.tm_hour = 0; - nov15.tm_min = 0; - nov15.tm_sec = 0; - - sql << "insert into soci_test(tm) values(:tm)", use(nov15); - - boost::gregorian::date bgd; - sql << "select tm from soci_test", into(bgd); - - CHECK(bgd.year() == 2005); - CHECK(bgd.month() == 11); - CHECK(bgd.day() == 15); - - sql << "update soci_test set tm = NULL"; - try - { - sql << "select tm from soci_test", into(bgd); - FAIL("expected exception not thrown"); - } - catch (soci_error const & e) - { - CHECK(e.get_error_message() == - "Null value not allowed for this type"); - } - } - - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - boost::gregorian::date bgd(2008, boost::gregorian::May, 5); - - sql << "insert into soci_test(tm) values(:tm)", use(bgd); - - std::tm t = std::tm(); - sql << "select tm from soci_test", into(t); - - CHECK(t.tm_year == 108); - CHECK(t.tm_mon == 4); - CHECK(t.tm_mday == 5); - } - -} - -#endif // SOCI_HAVE_BOOST - -// connection pool - simple sequential test, no multiple threads -TEST_CASE_METHOD(common_tests, "Connection pool", "[core][connection][pool]") -{ - // phase 1: preparation - const size_t pool_size = 10; - connection_pool pool(pool_size); - - for (std::size_t i = 0; i != pool_size; ++i) - { - session & sql = pool.at(i); - sql.open(backEndFactory_, connectString_); - } - - // phase 2: usage - for (std::size_t i = 0; i != pool_size; ++i) - { - // poor man way to lease more than one connection - soci::session sql_unused1(pool); - soci::session sql(pool); - soci::session sql_unused2(pool); - { - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - char c('a'); - sql << "insert into soci_test(c) values(:c)", use(c); - sql << "select c from soci_test", into(c); - CHECK(c == 'a'); - } - } -} - -// Issue 66 - test query transformation callback feature -static std::string no_op_transform(std::string query) -{ - return query; -} - -static std::string lower_than_g(std::string query) -{ - return query + " WHERE c < 'g'"; -} - -struct where_condition -{ - where_condition(std::string const& where) - : where_(where) - {} - - std::string operator()(std::string const& query) const - { - return query + " WHERE " + where_; - } - - std::string where_; -}; - - -void run_query_transformation_test(test_context_base const& tc, session& sql) -{ - // create and populate the test table - auto_table_creator tableCreator(tc.table_creator_1(sql)); - - for (char c = 'a'; c <= 'z'; ++c) - { - sql << "insert into soci_test(c) values(\'" << c << "\')"; - } - - char const* query = "select count(*) from soci_test"; - - // free function, no-op - { - sql.set_query_transformation(no_op_transform); - int count; - sql << query, into(count); - CHECK(count == 'z' - 'a' + 1); - } - - // free function - { - sql.set_query_transformation(lower_than_g); - int count; - sql << query, into(count); - CHECK(count == 'g' - 'a'); - } - - // function object with state - { - sql.set_query_transformation(where_condition("c > 'g' AND c < 'j'")); - int count = 0; - sql << query, into(count); - CHECK(count == 'j' - 'h'); - count = 0; - sql.set_query_transformation(where_condition("c > 's' AND c <= 'z'")); - sql << query, into(count); - CHECK(count == 'z' - 's'); - } - -#if 0 - // lambda is just presented as an example to curious users - { - sql.set_query_transformation( - [](std::string const& query) { - return query + " WHERE c > 'g' AND c < 'j'"; - }); - - int count = 0; - sql << query, into(count); - CHECK(count == 'j' - 'h'); - } -#endif - - // prepared statements - - // constant effect (pre-prepare set transformation) - { - // set transformation after statement is prepared - sql.set_query_transformation(lower_than_g); - // prepare statement - int count; - statement st = (sql.prepare << query, into(count)); - // observe transformation effect - st.execute(true); - CHECK(count == 'g' - 'a'); - // reset transformation - sql.set_query_transformation(no_op_transform); - // observe the same transformation, no-op set above has no effect - count = 0; - st.execute(true); - CHECK(count == 'g' - 'a'); - } - - // no effect (post-prepare set transformation) - { - // reset - sql.set_query_transformation(no_op_transform); - - // prepare statement - int count; - statement st = (sql.prepare << query, into(count)); - // set transformation after statement is prepared - sql.set_query_transformation(lower_than_g); - // observe no effect of WHERE clause injection - st.execute(true); - CHECK(count == 'z' - 'a' + 1); - } -} - -TEST_CASE_METHOD(common_tests, "Query transformation", "[core][query-transform]") -{ - soci::session sql(backEndFactory_, connectString_); - run_query_transformation_test(tc_, sql); -} - -TEST_CASE_METHOD(common_tests, "Query transformation with connection pool", "[core][query-transform][pool]") -{ - // phase 1: preparation - const size_t pool_size = 10; - connection_pool pool(pool_size); - - for (std::size_t i = 0; i != pool_size; ++i) - { - session & sql = pool.at(i); - sql.open(backEndFactory_, connectString_); - } - - soci::session sql(pool); - run_query_transformation_test(tc_, sql); -} - -// Originally, submitted to SQLite3 backend and later moved to common test. -// Test commit b394d039530f124802d06c3b1a969c3117683152 -// Author: Mika Fischer -// Date: Thu Nov 17 13:28:07 2011 +0100 -// Implement get_affected_rows for SQLite3 backend -TEST_CASE_METHOD(common_tests, "Get affected rows", "[core][affected-rows]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_4(sql)); - if (!tableCreator.get()) - { - std::cout << "test get_affected_rows skipped (function not implemented)" << std::endl; - return; - } - - - for (int i = 0; i != 10; i++) - { - sql << "insert into soci_test(val) values(:val)", use(i); - } - - int step = 2; - statement st1 = (sql.prepare << - "update soci_test set val = val + :step where val = 5", use(step, "step")); - st1.execute(true); - CHECK(st1.get_affected_rows() == 1); - - // attempts to run the query again, no rows should be affected - st1.execute(true); - CHECK(st1.get_affected_rows() == 0); - - statement st2 = (sql.prepare << - "update soci_test set val = val + 1"); - st2.execute(true); - - CHECK(st2.get_affected_rows() == 10); - - statement st3 = (sql.prepare << - "delete from soci_test where val <= 5"); - st3.execute(true); - - CHECK(st3.get_affected_rows() == 5); - - statement st4 = (sql.prepare << - "update soci_test set val = val + 1"); - st4.execute(true); - - CHECK(st4.get_affected_rows() == 5); - - std::vector v(5, 0); - for (std::size_t i = 0; i < v.size(); ++i) - { - v[i] = (7 + static_cast(i)); - } - - // test affected rows for bulk operations. - statement st5 = (sql.prepare << - "delete from soci_test where val = :v", use(v)); - st5.execute(true); - - CHECK(st5.get_affected_rows() == 5); - - std::vector w(2, "1"); - w[1] = "a"; // this invalid value may cause an exception. - statement st6 = (sql.prepare << - "insert into soci_test(val) values(:val)", use(w)); - try { st6.execute(true); } - catch(...) {} - - // confirm the partial insertion. - int val = 0; - sql << "select count(val) from soci_test", into(val); - if(val != 0) - { - // Notice that some ODBC drivers don't return the number of updated - // rows at all in the case of partially executed statement like this - // one, while MySQL ODBC driver wrongly returns 2 affected rows even - // though only one was actually inserted. - // - // So we can't check for "get_affected_rows() == val" here, it would - // fail in too many cases -- just check that the backend doesn't lie to - // us about no rows being affected at all (even if it just honestly - // admits that it has no idea by returning -1). - CHECK(st6.get_affected_rows() != 0); - } -} - -// test fix for: Backend is not set properly with connection pool (pull #5) -TEST_CASE_METHOD(common_tests, "Backend with connection pool", "[core][pool]") -{ - const size_t pool_size = 1; - connection_pool pool(pool_size); - - for (std::size_t i = 0; i != pool_size; ++i) - { - session & sql = pool.at(i); - sql.open(backEndFactory_, connectString_); - } - - soci::session sql(pool); - sql.reconnect(); - sql.begin(); // no crash expected -} - -// test fix for: Session from connection pool not set backend properly when call open -TEST_CASE_METHOD(common_tests, "Session from connection pool call open reset backend", "[core][pool]") -{ - const size_t pool_size = 1; - connection_pool pool(pool_size); - - soci::session sql(pool); - sql.open(backEndFactory_, connectString_); - REQUIRE_NOTHROW( sql.begin() ); -} - -// issue 67 - Allocated statement backend memory leaks on exception -// If the test runs under memory debugger and it passes, then -// soci::details::statement_impl::backEnd_ must not leak -TEST_CASE_METHOD(common_tests, "Backend memory leak", "[core][leak]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - try - { - rowset rs1 = (sql.prepare << "select * from soci_testX"); - - // TODO: On Linux, no exception thrown; neither from prepare, nor from execute? - // soci_odbc_test_postgresql: - // /home/travis/build/SOCI/soci/src/core/test/common-tests.h:3505: - // void soci::tests::common_tests::test_issue67(): Assertion `!"exception expected"' failed. - //FAIL("exception expected"); // relax temporarily - } - catch (soci_error const &e) - { - (void)e; - } -} - -// issue 154 - Calling undefine_and_bind and then define_and_bind causes a leak. -// If the test runs under memory debugger and it passes, then -// soci::details::standard_use_type_backend and vector_use_type_backend must not leak -TEST_CASE_METHOD(common_tests, "Bind memory leak", "[core][leak]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - sql << "insert into soci_test(id) values (1)"; - { - int id = 1; - int val = 0; - statement st(sql); - st.exchange(use(id)); - st.alloc(); - st.prepare("select id from soci_test where id = :1"); - st.define_and_bind(); - st.undefine_and_bind(); - st.exchange(soci::into(val)); - st.define_and_bind(); - st.execute(true); - CHECK(val == 1); - } - // vector variation - { - std::vector ids(1, 2); - std::vector vals(1, 1); - int val = 0; - statement st(sql); - st.exchange(use(ids)); - st.alloc(); - st.prepare("insert into soci_test(id, val) values (:1, :2)"); - st.define_and_bind(); - st.undefine_and_bind(); - st.exchange(use(vals)); - st.define_and_bind(); - st.execute(true); - sql << "select val from soci_test where id = 2", into(val); - CHECK(val == 1); - } -} - -// Helper functions for issue 723 test -namespace { - - // Creates a std::tm with UK DST threshold 31st March 2019 01:00:00 - std::tm create_uk_dst_threshold() - { - std::tm dst_threshold = std::tm(); - dst_threshold.tm_year = 119; // 2019 - dst_threshold.tm_mon = 2; // March - dst_threshold.tm_mday = 31; // 31st - dst_threshold.tm_hour = 1; // 1AM - dst_threshold.tm_min = 0; - dst_threshold.tm_sec = 0; - dst_threshold.tm_isdst = -1; // Determine DST from OS - return dst_threshold; - } - - // Sanity check to verify that the DST threshold causes mktime to modify - // the input hour (the condition that causes issue 723). - // This check really shouldn't fail but since it is the basis of the test - // it is worth verifying. - bool does_mktime_modify_input_hour() - { - std::tm dst_threshold = create_uk_dst_threshold(); - std::tm verify_mktime = dst_threshold; - mktime(&verify_mktime); - return verify_mktime.tm_hour != dst_threshold.tm_hour; - } - - // We don't have any way to change the time zone for just this process - // under MSW, so we just skip this test when not running in UK time-zone - // there. Under Unix systems we can however switch to UK time zone - // temporarily by just setting the TZ environment variable. -#ifndef _WIN32 - // Helper RAII class changing time zone to the specified one in its ctor - // and restoring the original time zone in its dtor. - class tz_setter - { - public: - explicit tz_setter(const std::string& time_zone) - { - char* tz_value = getenv("TZ"); - if (tz_value != NULL) - { - original_tz_value_ = tz_value; - } - - setenv("TZ", time_zone.c_str(), 1 /* overwrite */); - tzset(); - } - - ~tz_setter() - { - // Restore TZ value so other tests aren't affected. - if (original_tz_value_.empty()) - unsetenv("TZ"); - else - setenv("TZ", original_tz_value_.c_str(), 1); - tzset(); - } - - private: - std::string original_tz_value_; - }; -#endif // !_WIN32 -} - -// Issue 723 - std::tm timestamp problem with DST. -// When reading date/time on Daylight Saving Time threshold, hour value is -// silently changed. -TEST_CASE_METHOD(common_tests, "std::tm timestamp problem with DST", "[core][into][tm][dst]") -{ -#ifdef _WIN32 - if (!does_mktime_modify_input_hour()) - { - WARN("The DST test can only be run in the UK time zone, please switch to it manually."); - return; - } -#else // !_WIN32 - // Set UK timezone for this test scope. - tz_setter switch_to_UK_tz("Europe/London"); - - if (!does_mktime_modify_input_hour()) - { - WARN("Switching to the UK time zone unexpectedly failed, skipping the DST test."); - return; - } -#endif // _WIN32/!_WIN32 - - // Open session and create table with a date/time column. - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - // Round trip dst threshold time to database. - std::tm write_time = create_uk_dst_threshold(); - sql << "insert into soci_test(tm) values(:tm)", use(write_time); - std::tm read_time = std::tm(); - sql << "select tm from soci_test", soci::into(read_time); - - // Check that the round trip was consistent. - std::tm dst_threshold = create_uk_dst_threshold(); - CHECK(read_time.tm_year == dst_threshold.tm_year); - CHECK(read_time.tm_mon == dst_threshold.tm_mon); - CHECK(read_time.tm_mday == dst_threshold.tm_mday); - CHECK(read_time.tm_hour == dst_threshold.tm_hour); - CHECK(read_time.tm_min == dst_threshold.tm_min); - CHECK(read_time.tm_sec == dst_threshold.tm_sec); -} - -TEST_CASE_METHOD(common_tests, "Insert error", "[core][insert][exception]") -{ - soci::session sql(backEndFactory_, connectString_); - - struct pk_table_creator : table_creator_base - { - explicit pk_table_creator(session& sql) : table_creator_base(sql) - { - // For some backends (at least Firebird), it is important to - // execute the DDL statements in a separate transaction, so start - // one here and commit it before using the new table below. - sql.begin(); - sql << "create table soci_test(" - "name varchar(100) not null primary key, " - "age integer not null" - ")"; - sql.commit(); - } - } table_creator(sql); - - SECTION("literal SQL queries appear in the error message") - { - sql << "insert into soci_test(name, age) values ('John', 74)"; - sql << "insert into soci_test(name, age) values ('Paul', 72)"; - sql << "insert into soci_test(name, age) values ('George', 72)"; - - try - { - // Oops, this should have been 'Ringo' - sql << "insert into soci_test(name, age) values ('John', 74)"; - - FAIL("exception expected on unique constraint violation not thrown"); - } - catch (soci_error const &e) - { - std::string const msg = e.what(); - CAPTURE(msg); - - CHECK(msg.find("John") != std::string::npos); - } - } - - SECTION("SQL queries parameters appear in the error message") - { - char const* const names[] = { "John", "Paul", "George", "John", NULL }; - int const ages[] = { 74, 72, 72, 74, 0 }; - - std::string name; - int age; - - statement st = (sql.prepare << - "insert into soci_test(name, age) values (:name, :age)", - use(name), use(age)); - try - { - int const *a = ages; - for (char const* const* n = names; *n; ++n, ++a) - { - name = *n; - age = *a; - st.execute(true); - } - - FAIL("exception expected on unique constraint violation with prepared statement not thrown"); - } - catch (soci_error const &e) - { - std::string const msg = e.what(); - CAPTURE(msg); - - CHECK(msg.find("John") != std::string::npos); - } - } -} - -namespace -{ - -// This is just a helper to avoid duplicating the same code in two sections in -// the test below, it's logically part of it. -void check_for_exception_on_truncation(session& sql) -{ - // As the name column has length 20, inserting a longer string into it - // shouldn't work, unless we're dealing with a database that doesn't - // respect column types at all (hello SQLite). - try - { - std::string const long_name("George Raymond Richard Martin"); - sql << "insert into soci_test(name) values(:name)", use(long_name); - - // If insert didn't throw, it should have at least preserved the data - // (only SQLite does this currently). - std::string name; - sql << "select name from soci_test", into(name); - CHECK(name == long_name); - } - catch (soci_error const &) - { - // Unfortunately the contents of the message differ too much between - // the backends (most give an error about value being "too long", - // Oracle says "too large" while SQL Server (via ODBC) just says that - // it "would be truncated"), so we can't really check that we received - // the right error here -- be optimistic and hope that we did. - } -} - -// And another helper for the test below. -void check_for_no_truncation(session& sql, bool with_padding) -{ - const std::string str20 = "exactly of length 20"; - - sql << "delete from soci_test"; - - // Also check that there is no truncation when inserting a string of - // the same length as the column size. - CHECK_NOTHROW( (sql << "insert into soci_test(name) values(:s)", use(str20)) ); - - std::string s; - sql << "select name from soci_test", into(s); - - // Firebird can pad CHAR(N) columns when using UTF-8 encoding. - // the result will be padded to 80 bytes (UTF-8 max for 20 chars) - if (with_padding) - CHECK_EQUAL_PADDED(s, str20) - else - CHECK( s == str20 ); -} - -} // anonymous namespace - -TEST_CASE_METHOD(common_tests, "Truncation error", "[core][insert][truncate][exception]") -{ - soci::session sql(backEndFactory_, connectString_); - - if (tc_.has_silent_truncate_bug(sql)) - { - WARN("Database is broken and silently truncates input data."); - return; - } - - SECTION("Error given for char column") - { - struct fixed_name_table_creator : table_creator_base - { - fixed_name_table_creator(session& sql) - : table_creator_base(sql) - { - sql << "create table soci_test(name char(20))"; - } - } tableCreator(sql); - - tc_.on_after_ddl(sql); - - check_for_exception_on_truncation(sql); - - // Firebird can pad CHAR(N) columns when using UTF-8 encoding. - check_for_no_truncation(sql, sql.get_backend_name() == "firebird"); - } - - SECTION("Error given for varchar column") - { - // Reuse one of the standard tables which has a varchar(20) column. - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - check_for_exception_on_truncation(sql); - - check_for_no_truncation(sql, false); - } -} - -TEST_CASE_METHOD(common_tests, "Blank padding", "[core][insert][exception]") -{ - soci::session sql(backEndFactory_, connectString_); - if (!tc_.enable_std_char_padding(sql)) - { - WARN("This backend doesn't pad CHAR(N) correctly, skipping test."); - return; - } - - struct fixed_name_table_creator : table_creator_base - { - fixed_name_table_creator(session& sql) - : table_creator_base(sql) - { - sql.begin(); - sql << "create table soci_test(sc char, name char(10), name2 varchar(10))"; - sql.commit(); - } - } tableCreator(sql); - - std::string test1 = "abcde "; - std::string singleChar = "a"; - sql << "insert into soci_test(sc, name,name2) values(:sc,:name,:name2)", - use(singleChar), use(test1), use(test1); - - std::string sc, tchar,tvarchar; - sql << "select sc,name,name2 from soci_test", - into(sc), into(tchar), into(tvarchar); - - // Firebird can pad "a" to "a " when using UTF-8 encoding. - CHECK_EQUAL_PADDED(sc, singleChar); - CHECK_EQUAL_PADDED(tchar, test1); - CHECK(tvarchar == test1); - - // Check 10-space string - same as inserting empty string since spaces will - // be padded up to full size of the column. - test1 = " "; - singleChar = " "; - sql << "update soci_test set sc=:sc, name=:name, name2=:name2", - use(singleChar), use(test1), use(test1); - sql << "select sc, name,name2 from soci_test", - into(sc), into(tchar), into(tvarchar); - - CHECK_EQUAL_PADDED(sc, singleChar); - CHECK_EQUAL_PADDED(tchar, test1); - CHECK(tvarchar == test1); -} - -TEST_CASE_METHOD(common_tests, "Select without table", "[core][select][dummy_from]") -{ - soci::session sql(backEndFactory_, connectString_); - - int plus17; - sql << ("select abs(-17)" + sql.get_dummy_from_clause()), - into(plus17); - - CHECK(plus17 == 17); -} - -TEST_CASE_METHOD(common_tests, "String length", "[core][string][length]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - std::string s("123"); - REQUIRE_NOTHROW(( - sql << "insert into soci_test(str) values(:s)", use(s) - )); - - std::string sout; - size_t slen; - REQUIRE_NOTHROW(( - sql << "select str," + tc_.sql_length("str") + " from soci_test", - into(sout), into(slen) - )); - CHECK(slen == 3); - CHECK(sout.length() == 3); - CHECK(sout == s); - - sql << "delete from soci_test"; - - - std::vector v; - v.push_back("Hello"); - v.push_back(""); - v.push_back("whole of varchar(20)"); - - REQUIRE_NOTHROW(( - sql << "insert into soci_test(str) values(:s)", use(v) - )); - - std::vector vout(10); - // Although none of the strings here is really null, Oracle handles the - // empty string as being null, so to avoid an error about not providing - // the indicator when retrieving a null value, we must provide it here. - std::vector vind(10); - std::vector vlen(10); - - REQUIRE_NOTHROW(( - sql << "select str," + tc_.sql_length("str") + " from soci_test" - " order by " + tc_.sql_length("str"), - into(vout, vind), into(vlen) - )); - - REQUIRE(vout.size() == 3); - REQUIRE(vlen.size() == 3); - - CHECK(vlen[0] == 0); - CHECK(vout[0].length() == 0); - - CHECK(vlen[1] == 5); - CHECK(vout[1].length() == 5); - - CHECK(vlen[2] == 20); - CHECK(vout[2].length() == 20); -} - -// Helper function used in some tests below. Generates an XML sample about -// approximateSize bytes long. -static std::string make_long_xml_string(int approximateSize = 5000) -{ - const int tagsSize = 6 + 7; - const int patternSize = 26; - const int patternsCount = approximateSize / patternSize + 1; - - std::string s; - s.reserve(tagsSize + patternsCount * patternSize); - - std::ostringstream ss; - ss << ""; - for (int i = 0; i != patternsCount; ++i) - { - ss << "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - } - ss << ""; - - return ss.str(); -} - -// The helper function to remove trailing \n from a given string. -// Used for XML strings, returned from the DB. -// The returned XML value doesn't need to be identical to the original one as -// string, only structurally equal as XML. In particular, extra whitespace -// can be added and this does happen with Oracle, for example, which adds -// an extra new line, so remove it if it's present. -static std::string remove_trailing_nl(std::string str) -{ - if (!str.empty() && *str.rbegin() == '\n') - { - str.resize(str.length() - 1); - } - - return str; -} - -TEST_CASE_METHOD(common_tests, "CLOB", "[core][clob]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_clob(sql)); - if (!tableCreator.get()) - { - WARN("CLOB type not supported by the database, skipping the test."); - return; - } - - long_string s1; // empty - sql << "insert into soci_test(id, s) values (1, :s)", use(s1); - - long_string s2; - s2.value = "hello"; - sql << "select s from soci_test where id = 1", into(s2); - - CHECK(s2.value.size() == 0); - - s1.value = make_long_xml_string(); - - sql << "update soci_test set s = :s where id = 1", use(s1); - - sql << "select s from soci_test where id = 1", into(s2); - - CHECK(s2.value == s1.value); - - // Check that trailing new lines are preserved. - s1.value = "multi\nline\nstring\n\n"; - sql << "update soci_test set s = :s where id = 1", use(s1); - sql << "select s from soci_test where id = 1", into(s2); - CHECK(tc_.fix_crlf_if_necessary(s2.value) == s1.value); -} - -TEST_CASE_METHOD(common_tests, "CLOB vector", "[core][clob][vector]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_clob(sql)); - if (!tableCreator.get()) - { - WARN("CLOB type not supported by the database, skipping the test."); - return; - } - - std::vector ids(2); - ids[0] = 1; - ids[1] = 2; - std::vector s1(2); // empty values - sql << "insert into soci_test(id, s) values (:id, :s)", use(ids), use(s1); - - std::vector s2(2); - s2[0].value = "hello_1"; - s2[1].value = "hello_2"; - sql << "select s from soci_test", into(s2); - - REQUIRE(s2.size() == 2); - CHECK(s2[0].value.empty()); - CHECK(s2[1].value.empty()); - - s1[0].value = make_long_xml_string(); - s1[1].value = make_long_xml_string(10000); - - sql << "update soci_test set s = :s where id = :id", use(s1), use(ids); - - sql << "select s from soci_test", into(s2); - - REQUIRE(s2.size() == 2); - CHECK(s2[0].value == s1[0].value); - CHECK(s2[1].value == s1[1].value); -} - -TEST_CASE_METHOD(common_tests, "XML", "[core][xml]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_xml(sql)); - if (!tableCreator.get()) - { - WARN("XML type not supported by the database, skipping the test."); - return; - } - - int id = 1; - xml_type xml; - xml.value = make_long_xml_string(); - - sql << "insert into soci_test (id, x) values (:1, " - << tc_.to_xml(":2") - << ")", - use(id), use(xml); - - xml_type xml2; - - sql << "select " - << tc_.from_xml("x") - << " from soci_test where id = :1", - into(xml2), use(id); - - CHECK(xml.value == remove_trailing_nl(xml2.value)); - - sql << "update soci_test set x = null where id = :1", use(id); - - indicator ind; - sql << "select " - << tc_.from_xml("x") - << " from soci_test where id = :1", - into(xml2, ind), use(id); - - CHECK(ind == i_null); - - // Inserting malformed XML into an XML column must fail but some backends - // (e.g. Firebird) don't have real XML support, so exclude them from this - // test. - if (tc_.has_real_xml_support()) - { - xml.value = ""; - CHECK_THROWS_AS( - (sql << "insert into soci_test(id, x) values (2, " - + tc_.to_xml(":1") + ")", - use(xml) - ), soci_error - ); - } } -// Tha same test as above, but using vectors of xml_type values. -TEST_CASE_METHOD(common_tests, "XML vector", "[core][xml][vector]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_xml(sql)); - if (!tableCreator.get()) - { - WARN("XML type not supported by the database, skipping the test."); - return; - } - - std::vector id(2); - id[0] = 1; - id[1] = 1; // Use the same ID to select both objects by ID. - std::vector xml(2); - xml[0].value = make_long_xml_string(); - // Check long strings handling. - xml[1].value = make_long_xml_string(10000); - - sql << "insert into soci_test (id, x) values (:1, " - << tc_.to_xml(":2") - << ")", - use(id), use(xml); - - std::vector xml2(2); - - sql << "select " - << tc_.from_xml("x") - << " from soci_test where id = :1", - into(xml2), use(id.at(0)); - - CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); - CHECK(xml.at(1).value == remove_trailing_nl(xml2.at(1).value)); - - sql << "update soci_test set x = null where id = :1", use(id.at(0)); - - std::vector ind(2); - sql << "select " - << tc_.from_xml("x") - << " from soci_test where id = :1", - into(xml2, ind), use(id.at(0)); - - CHECK(ind.at(0) == i_null); - CHECK(ind.at(1) == i_null); -} - -TEST_CASE_METHOD(common_tests, "XML and int vectors", "[core][xml][vector]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_xml(sql)); - if (!tableCreator.get()) - { - WARN("XML type not supported by the database, skipping the test."); - return; - } - - std::vector id(3); - id[0] = 0; - id[1] = 1; - id[2] = 2; - std::vector xml(3); - std::vector ind(3); - xml[0].value = make_long_xml_string(); - ind[0] = i_ok; - ind[1] = i_null; - // Check long strings handling. - xml[2].value = make_long_xml_string(10000); - ind[2] = i_ok; - - sql << "insert into soci_test (id, x) values (:1, " - << tc_.to_xml(":2") - << ")", - use(id), use(xml, ind); - - std::vector id2(3); - std::vector xml2(3); - std::vector ind2(3); - - sql << "select id, " - << tc_.from_xml("x") - << " from soci_test order by id", - into(id2), into(xml2, ind2); - - CHECK(id.at(0) == id2.at(0)); - CHECK(id.at(1) == id2.at(1)); - CHECK(id.at(2) == id2.at(2)); - - CHECK(xml.at(0).value == remove_trailing_nl(xml2.at(0).value)); - CHECK(xml.at(2).value == remove_trailing_nl(xml2.at(2).value)); - - CHECK(ind.at(0) == ind2.at(0)); - CHECK(ind.at(1) == ind2.at(1)); - CHECK(ind.at(2) == ind2.at(2)); -} - -TEST_CASE_METHOD(common_tests, "Into XML vector with several fetches", "[core][xml][into][vector][statement]") -{ - int stringSize = 0; - SECTION("short string") - { - stringSize = 100; - } - SECTION("long string") - { - stringSize = 10000; - } - - // Skip the rest when not executing the current section. - if (!stringSize) - return; - - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_xml(sql)); - if (!tableCreator.get()) - { - WARN("XML type not supported by the database, skipping the test."); - return; - } - - int const count = 5; - std::vector values(count); - for (int i = 0; i != count; ++i) - values[i].value = make_long_xml_string(stringSize + i*100); - - sql << "insert into soci_test (x) values (" - << tc_.to_xml(":2") - << ")", - use(values); - - std::vector result(3); - soci::statement st = (sql.prepare << - "select " << tc_.from_xml("x") << " from soci_test", into(result)); - - st.execute(true); - REQUIRE(result.size() == 3); - CHECK(remove_trailing_nl(result[0].value) == values[0].value); - CHECK(remove_trailing_nl(result[1].value) == values[1].value); - CHECK(remove_trailing_nl(result[2].value) == values[2].value); - - REQUIRE(st.fetch()); - REQUIRE(result.size() == 2); - CHECK(remove_trailing_nl(result[0].value) == values[3].value); - CHECK(remove_trailing_nl(result[1].value) == values[4].value); - - REQUIRE(!st.fetch()); -} - -TEST_CASE_METHOD(common_tests, "BLOB", "[core][blob]") -{ - soci::session sql(backEndFactory_, connectString_); - - auto_table_creator tableCreator(tc_.table_creator_blob(sql)); - - if (!tableCreator.get()) - { - try - { - soci::blob blob(sql); - FAIL("BLOB creation should throw, if backend doesn't support BLOBs"); - } - catch (const soci_error &) - { - // Throwing is expected if the backend doesn't support BLOBs - } - WARN("BLOB type not supported by the database, skipping the test."); - return; - } - - const char dummy_data[] = "abcdefghijklmnopqrstuvwxyz"; - - // Cross-DB usage of BLOBs is only possible if the entire lifetime of the blob object - // is covered in an active transaction. - soci::transaction transaction(sql); - SECTION("Read-access on just-constructed blob") - { - soci::blob blob(sql); - - CHECK(blob.get_len() == 0); - - char buf[5]; - std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf)); - - // There should be no data that could be read - CHECK(read_bytes == 0); - - // Reading from any offset other than zero is invalid - CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), 1), soci_error); - } - SECTION("validity & initialization") - { - soci::blob blob; - CHECK_FALSE(blob.is_valid()); - blob.initialize(sql); - CHECK(blob.is_valid()); - - soci::blob other; - CHECK_FALSE(other.is_valid()); - other = std::move(blob); - CHECK(other.is_valid()); - CHECK_FALSE(blob.is_valid()); - } - SECTION("BLOB I/O") - { - soci::blob blob(sql); - - std::size_t written_bytes = blob.write_from_start(dummy_data, 5); - - CHECK(written_bytes == 5); - CHECK(blob.get_len() == 5); - - char buf[5]; - static_assert(sizeof(buf) <= sizeof(dummy_data), "Underlying assumption violated"); - - std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf)); - - CHECK(read_bytes == sizeof(buf)); - - for (std::size_t i = 0; i < sizeof(buf); ++i) - { - CHECK(buf[i] == dummy_data[i]); - } - - written_bytes = blob.append(dummy_data + 5, 3); - - CHECK(written_bytes == 3); - CHECK(blob.get_len() == 8); - - read_bytes = blob.read_from_start(buf, sizeof(buf), 3); - - CHECK(read_bytes == 5); - - for (std::size_t i = 0; i < sizeof(buf); ++i) - { - CHECK(buf[i] == dummy_data[i + 3]); - } - - blob.trim(2); - - CHECK(blob.get_len() == 2); - - read_bytes = blob.read_from_start(buf, sizeof(buf)); - - CHECK(read_bytes == 2); - - for (std::size_t i = 0; i < read_bytes; ++i) - { - CHECK(buf[i] == dummy_data[i]); - } - - // Reading from an offset >= the current length of the blob is invalid - CHECK_THROWS_AS(blob.read_from_start(buf, sizeof(buf), blob.get_len()), soci_error); - - written_bytes = blob.append("z", 1); - - CHECK(written_bytes == 1); - CHECK(blob.get_len() == 3); - - read_bytes = blob.read_from_start(buf, 1, 2); - - CHECK(read_bytes == 1); - CHECK(buf[0] == 'z'); - - // Writing more than one position beyond the blob is invalid - // (Writing exactly one position beyond is the same as appending) - CHECK_THROWS_AS(blob.write_from_start(dummy_data, 2, blob.get_len() + 1), soci_error); - } - SECTION("Inserting/Reading default-constructed blob") - { - { - soci::blob input_blob(sql); - - sql << "insert into soci_test (id, b) values(5, :b)", soci::use(input_blob); - } - - soci::blob output_blob(sql); - soci::indicator ind; - - sql << "select b from soci_test where id = 5", soci::into(output_blob, ind); - - CHECK(ind == soci::i_ok); - CHECK(output_blob.get_len() == 0); - } - SECTION("Ensure reading into blob overwrites previous contents") - { - soci::blob blob(sql); - blob.write_from_start("hello kitty", 10); - - CHECK(blob.get_len() == 10); - - { - soci::blob write_blob(sql); - write_blob.write_from_start("test", 4); - sql << "insert into soci_test (id, b) values (5, :b)", soci::use(write_blob); - } - - sql << "select b from soci_test where id = 5", soci::into(blob); - - CHECK(blob.get_len() == 4); - char buf[5]; - - std::size_t read_bytes = blob.read_from_start(buf, sizeof(buf)); - CHECK(read_bytes == 4); - - CHECK(buf[0] == 't'); - CHECK(buf[1] == 'e'); - CHECK(buf[2] == 's'); - CHECK(buf[3] == 't'); - } - SECTION("Blob-DB interaction") - { - soci::blob write_blob(sql); - - static_assert(sizeof(dummy_data) >= 10, "Underlying assumption violated"); - write_blob.write_from_start(dummy_data, 10); - - const int first_id = 42; - - // Write and retrieve blob from/into database - sql << "insert into soci_test (id, b) values(:id, :b)", soci::use(first_id), soci::use(write_blob); - - // Append to write_blob - these changes must not reflect in the BLOB stored in the DB - write_blob.append("ay", 2); - CHECK(write_blob.get_len() == 12); - char buf[15]; - std::size_t read_bytes = write_blob.read_from_start(buf, sizeof(buf)); - CHECK(read_bytes == 12); - for (std::size_t i = 0; i < 10; ++i) - { - CHECK(buf[i] == dummy_data[i]); - } - CHECK(buf[10] == 'a'); - CHECK(buf[11] == 'y'); - - - soci::blob read_blob(sql); - sql << "select b from soci_test where id = :id", soci::use(first_id), soci::into(read_blob); - CHECK(sql.got_data()); - - CHECK(read_blob.get_len() == 10); - - std::size_t bytes_read = read_blob.read_from_start(buf, sizeof(buf)); - CHECK(bytes_read == read_blob.get_len()); - CHECK(bytes_read == 10); - for (std::size_t i = 0; i < bytes_read; ++i) - { - CHECK(buf[i] == dummy_data[i]); - } - - // Update original blob and insert new db-entry (must not change previous entry) - const int second_id = first_id + 1; - write_blob.trim(0); - static_assert(sizeof(dummy_data) >= 15 + 5, "Underlying assumption violated"); - write_blob.write_from_start(dummy_data + 15, 5); - - sql << "insert into soci_test (id, b) values (:id, :b)", soci::use(second_id), soci::use(write_blob); - - // First, check that the original entry has not been changed - sql << "select b from soci_test where id = :id", soci::use(first_id), soci::into(read_blob); - CHECK(read_blob.get_len() == 10); - - // Then check new entry can be read - sql << "select b from soci_test where id = :id", soci::use(second_id), soci::into(read_blob); - - bytes_read = read_blob.read_from_start(buf, sizeof(buf)); - CHECK(bytes_read == read_blob.get_len()); - CHECK(bytes_read == 5); - for (std::size_t i = 0; i < bytes_read; ++i) - { - CHECK(buf[i] == dummy_data[i + 15]); - } - } - SECTION("Binary data") - { - const std::uint8_t binary_data[12] = {0, 1, 2, 3, 4, 5, 6, 7, 22, 255, 250 }; - - soci::blob write_blob(sql); - - std::size_t bytes_written = write_blob.write_from_start(binary_data, sizeof(binary_data)); - CHECK(bytes_written == sizeof(binary_data)); - - sql << "insert into soci_test (id, b) values (1, :b)", soci::use(write_blob); - - soci::blob read_blob(sql); - - sql << "select b from soci_test where id = 1", soci::into(read_blob); - - CHECK(read_blob.get_len() == sizeof(binary_data)); - - std::uint8_t buf[20]; - std::size_t bytes_read = read_blob.read_from_start(buf, sizeof(buf)); - - CHECK(bytes_read == sizeof(binary_data)); - for (std::size_t i = 0; i < sizeof(binary_data); ++i) - { - CHECK(buf[i] == binary_data[i]); - } - } - SECTION("Rowset Blob recognition") - { - soci::blob blob(sql); - - // Write and retrieve blob from/into database - int id = 1; - sql << "insert into soci_test (id, b) values(:id, :b)", soci::use(id), soci::use(blob); - - soci::rowset< soci::row > rowSet = sql.prepare << "select id, b from soci_test"; - bool containedData = false; - for (auto it = rowSet.begin(); it != rowSet.end(); ++it) - { - containedData = true; - const soci::row ¤tRow = *it; - - CHECK(currentRow.get_properties(1).get_data_type() == soci::dt_blob); - } - CHECK(containedData); - } - SECTION("Blob binding") - { - // Add data - soci::blob blob(sql); - static_assert(10 <= sizeof(dummy_data), "Underlying assumption violated"); - blob.write_from_start(dummy_data, 10); - const int id = 42; - sql << "insert into soci_test (id, b) values(:id, :b)", soci::use(id), soci::use(blob); - - SECTION("into") - { - soci::blob intoBlob(sql); - - sql << "select b from soci_test where id=:id", soci::use(id), soci::into(intoBlob); - - char buffer[20]; - std::size_t written = intoBlob.read_from_start(buffer, sizeof(buffer)); - CHECK(written == 10); - for (std::size_t i = 0; i < 10; ++i) - { - CHECK(buffer[i] == dummy_data[i]); - } - } - SECTION("move_as") - { - soci::rowset< soci::row > rowSet = (sql.prepare << "select b from soci_test where id=:id", soci::use(id)); - bool containedData = false; - for (auto it = rowSet.begin(); it != rowSet.end(); ++it) - { - containedData = true; - const soci::row ¤tRow = *it; - - soci::blob intoBlob = currentRow.move_as(0); - - CHECK(intoBlob.get_len() == 10); - char buffer[20]; - std::size_t written = intoBlob.read_from_start(buffer, sizeof(buffer)); - CHECK(written == 10); - for (std::size_t i = 0; i < 10; ++i) - { - CHECK(buffer[i] == dummy_data[i]); - } - } - CHECK(containedData); - } - SECTION("reusing bound blob") - { - int secondID = id + 1; - sql << "insert into soci_test(id, b) values(:id, :b)", soci::use(secondID), soci::use(blob); - - // Selecting the blob associated with secondID should yield the same result as selecting the one for id - soci::blob intoBlob(sql); - sql << "select b from soci_test where id=:id", soci::use(secondID), soci::into(intoBlob); - char buffer[20]; - std::size_t written = intoBlob.read_from_start(buffer, sizeof(buffer)); - CHECK(written == 10); - for (std::size_t i = 0; i < 10; ++i) - { - CHECK(buffer[i] == dummy_data[i]); - } - } - } - SECTION("Statements") - { - unsigned int id; - soci::blob myBlob(sql); - soci::statement insert_stmt = (sql.prepare << "insert into soci_test (id, b) values (:id, :b)", soci::use(id), soci::use(myBlob)); - - id = 1; - myBlob.write_from_start(dummy_data + id, id); - insert_stmt.execute(true); - - id = 5; - myBlob.write_from_start(dummy_data + id, id); - insert_stmt.execute(true); - - - soci::statement select_stmt = (sql.prepare << "select id, b from soci_test order by id asc", soci::into(id), soci::into(myBlob)); - char contents[16]; - - select_stmt.execute(); - CHECK(select_stmt.fetch()); - CHECK(id == 1); - std::size_t blob_size = myBlob.get_len(); - CHECK(blob_size == id); - std::size_t read = myBlob.read_from_start(contents, blob_size); - CHECK(read == blob_size); - for (unsigned int i = 0; i < blob_size; ++i) - { - CHECK(contents[i] == dummy_data[id + i]); - } - - CHECK(select_stmt.fetch()); - CHECK(id == 5); - blob_size = myBlob.get_len(); - CHECK(blob_size == id); - read = myBlob.read_from_start(contents, blob_size); - CHECK(read == blob_size); - for (unsigned int i = 0; i < blob_size; ++i) - { - CHECK(contents[i] == dummy_data[id + i]); - } - - CHECK(!select_stmt.fetch()); - } -} - -TEST_CASE_METHOD(common_tests, "Logger", "[core][log]") -{ - // Logger class used for testing: appends all queries to the provided - // buffer. - class test_log_impl : public soci::logger_impl - { - public: - explicit test_log_impl(std::vector& logbuf) - : m_logbuf(logbuf) - { - } - - virtual void start_query(std::string const & query) - { - m_logbuf.push_back(query); - } - - private: - virtual logger_impl* do_clone() const - { - return new test_log_impl(m_logbuf); - } - - std::vector& m_logbuf; - }; - - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - soci::logger const logger_orig = sql.get_logger(); - - std::vector logbuf; - sql.set_logger(new test_log_impl(logbuf)); - - int count; - sql << "select count(*) from soci_test", into(count); - - REQUIRE( logbuf.size() == 1 ); - CHECK( logbuf.front() == "select count(*) from soci_test" ); - - sql.set_logger(logger_orig); -} - -// These tests are disabled by default, as they require manual intevention, but -// can be run by explicitly giving their names on the command line. - -// Check if reconnecting to the database after losing connection to it works. -TEST_CASE_METHOD(common_tests, "Reconnect", "[keep-alive][.]") -{ - soci::session sql(backEndFactory_, connectString_); - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - int id = 17; - sql << "insert into soci_test (id) values (:id)", use(id); - - REQUIRE_NOTHROW( sql.commit() ); - CHECK( sql.is_connected() ); - - std::cout << "Please break connection to the database " - "(stop the server, unplug the network cable, ...) " - "and press Enter" << std::endl; - std::cin.get(); - - try - { - CHECK( !sql.is_connected() ); - - int id2; - sql << "select id from soci_test", into(id2); - - FAIL("Connection to the database still available"); - return; - } - catch (soci_error const& e) - { - if ( sql.get_backend_name() == "odbc" || - e.get_error_category() == soci_error::unknown ) - { - WARN( "Skipping error check because ODBC driver returned " - "unknown error: " << e.what() ); - } - else - { - INFO( "Exception message: " << e.what() ); - CHECK( e.get_error_category() == soci_error::connection_error ); - } - } - - std::cout << "Please undo the previous action " - "(restart the server, plug the cable back, ...) " - "and press Enter" << std::endl; - std::cin.get(); - - REQUIRE_NOTHROW( sql.reconnect() ); - CHECK( sql.is_connected() ); - - int id2 = 1234; - sql << "select id from soci_test", into(id2); - CHECK( id2 == id ); -} - -// Check if automatically reconnecting to the database works. -// -// Note: this test doesn't work at all, failover doesn't happen neither with -// Oracle nor with PostgreSQL (which are the only backends for which it's -// implemented at all) and it's not clear how is it even supposed to work. -TEST_CASE_METHOD(common_tests, "Failover", "[keep-alive][.]") -{ - soci::session sql(backEndFactory_, connectString_); - - class MyCallback : public soci::failover_callback - { - public: - MyCallback() : attempted_(false), reconnected_(false) - { - } - - bool did_reconnect() const { return reconnected_; } - - void started() override - { - std::cout << "Please undo the previous action " - "(restart the server, plug the cable back, ...) " - "and press Enter" << std::endl; - std::cin.get(); - } - - void failed(bool& retry, std::string&) override - { - // We only retry once. - retry = !attempted_; - attempted_ = true; - } - - void finished(soci::session&) override - { - reconnected_ = true; - } - - void aborted() override - { - FAIL( "Failover aborted" ); - } - - private: - bool attempted_; - bool reconnected_; - } myCallback; - - sql.set_failover_callback(myCallback); - - auto_table_creator tableCreator(tc_.table_creator_1(sql)); - - int id = 17; - sql << "insert into soci_test (id) values (:id)", use(id); - REQUIRE_NOTHROW( sql.commit() ); - - std::cout << "Please break connection to the database " - "(stop the server, unplug the network cable, ...) " - "and press Enter" << std::endl; - std::cin.get(); - - int id2; - sql << "select id from soci_test", into(id2); - CHECK( id2 == id ); - - CHECK( myCallback.did_reconnect() ); -} - -} // namespace test_cases - -} // namespace tests - -} // namespace soci - -#endif // SOCI_COMMON_TESTS_H_INCLUDED +#endif // SOCI_COMMON_TESTS_H_ diff --git a/tests/db2/CMakeLists.txt b/tests/db2/CMakeLists.txt index 594eacca1..e5e6d098c 100644 --- a/tests/db2/CMakeLists.txt +++ b/tests/db2/CMakeLists.txt @@ -1,16 +1,10 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(db2_tests OBJECT db2_tests.cpp) +target_link_libraries(db2_tests PUBLIC soci_common_tests soci_db2_interface) -soci_backend_test( - BACKEND DB2 - DEPENDS DB2 - SOURCE test-db2.cpp ${SOCI_TESTS_COMMON} - CONNSTR "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off") +soci_make_tests( + OBJECT_LIB db2_tests + CONNECTION_STRING "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off" + SHARED_NAME "soci_db2_test" + STATIC_NAME "soci_db2_test_static" + SOCI_DEP_ALIAS "DB2" +) diff --git a/tests/db2/test-db2.cpp b/tests/db2/db2_tests.cpp similarity index 92% rename from tests/db2/test-db2.cpp rename to tests/db2/db2_tests.cpp index 5501f1da1..b52709e2d 100644 --- a/tests/db2/test-db2.cpp +++ b/tests/db2/db2_tests.cpp @@ -6,9 +6,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/db2/soci-db2.h" -#include "common-tests.h" + #include #include #include @@ -387,38 +391,21 @@ TEST_CASE("DB2 test 3", "[db2]") sql.commit(); } - -int main(int argc, char** argv) +namespace soci +{ +namespace tests { -#ifdef _MSC_VER - // Redirect errors, unrecoverable problems, and assert() failures to STDERR, - // instead of debug message window. - // This hack is required to run assert()-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]; - - argv[1] = argv[0]; - - argc--; - argv++; - } - else - { - std::cout << "usage: " << argv[0] - << " connectstring [test-arguments...]\n" - << "example: " << argv[0] - << " \'DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off\'\n"; - std::exit(1); - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/empty/CMakeLists.txt b/tests/empty/CMakeLists.txt index 38d95b83e..30d391ed6 100644 --- a/tests/empty/CMakeLists.txt +++ b/tests/empty/CMakeLists.txt @@ -1,15 +1,11 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(empty_tests OBJECT empty_tests.cpp) +target_link_libraries(empty_tests PUBLIC soci_empty_interface) +target_include_directories(empty_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") -soci_backend_test( - BACKEND Empty - SOURCE test-empty.cpp ${SOCI_TESTS_COMMON} - CONNSTR "dummy") +soci_make_tests( + OBJECT_LIB empty_tests + CONNECTION_STRING "dummy" + SHARED_NAME "soci_empty_test" + STATIC_NAME "soci_empty_test_static" + SOCI_DEP_ALIAS "Empty" +) diff --git a/tests/empty/test-empty.cpp b/tests/empty/empty_tests.cpp similarity index 100% rename from tests/empty/test-empty.cpp rename to tests/empty/empty_tests.cpp index fd1f0b560..fd28ab66f 100644 --- a/tests/empty/test-empty.cpp +++ b/tests/empty/empty_tests.cpp @@ -5,14 +5,14 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include "soci/soci.h" -#include "soci/empty/soci-empty.h" - // Normally the tests would include common-tests.h here, but we can't run any // of the tests registered there, so instead include CATCH header directly. #define CATCH_CONFIG_RUNNER #include +#include "soci/soci.h" +#include "soci/empty/soci-empty.h" + #include #include #include diff --git a/tests/firebird/CMakeLists.txt b/tests/firebird/CMakeLists.txt index 283df9b0a..442e2695b 100644 --- a/tests/firebird/CMakeLists.txt +++ b/tests/firebird/CMakeLists.txt @@ -1,16 +1,10 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(firebird_tests OBJECT firebird_tests.cpp) +target_link_libraries(firebird_tests PUBLIC soci_common_tests soci_firebird_interface) -soci_backend_test( - BACKEND Firebird - DEPENDS Firebird - SOURCE test-firebird.cpp ${SOCI_TESTS_COMMON} - CONNSTR "service=/tmp/soci_test.fdb user=SYSDBA password=masterkey") +soci_make_tests( + OBJECT_LIB firebird_tests + CONNECTION_STRING "service=/tmp/soci_test.fdb user=SYSDBA password=masterkey" + SHARED_NAME "soci_firebird_test" + STATIC_NAME "soci_firebird_test_static" + SOCI_DEP_ALIAS "Firebird" +) diff --git a/tests/firebird/test-firebird.cpp b/tests/firebird/firebird_tests.cpp similarity index 96% rename from tests/firebird/test-firebird.cpp rename to tests/firebird/firebird_tests.cpp index cfe688957..aac635d44 100644 --- a/tests/firebird/test-firebird.cpp +++ b/tests/firebird/firebird_tests.cpp @@ -6,12 +6,17 @@ // // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/firebird/soci-firebird.h" #include "soci-compiler.h" + #include "firebird/error-firebird.h" // soci::details::Firebird::throw_iscerror() #include "firebird/common.h" -#include "common-tests.h" + #include #include #include @@ -1254,40 +1259,21 @@ class test_context : public tests::test_context_base } }; - -int main(int argc, char** argv) +namespace soci +{ +namespace tests { -#ifdef _MSC_VER - // Redirect errors, unrecoverable problems, and assert() failures to STDERR, - // instead of debug message window. - // This hack is required to run assert()-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]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; - - argc--; - argv++; - } - else - { - std::cout << "usage: " << argv[0] - << " connectstring [test-arguments...]\n" - << "example: " << argv[0] - << " \"service=/usr/local/firebird/db/test.fdb user=SYSDBA password=masterkey\"\n"; - return EXIT_FAILURE; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/mysql/CMakeLists.txt b/tests/mysql/CMakeLists.txt index 533b61f68..c4006ac24 100644 --- a/tests/mysql/CMakeLists.txt +++ b/tests/mysql/CMakeLists.txt @@ -1,16 +1,10 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(mysql_tests OBJECT mysql_tests.cpp) +target_link_libraries(mysql_tests PUBLIC soci_common_tests soci_mysql_interface) -soci_backend_test( - BACKEND MySQL - DEPENDS MySQL - SOURCE test-mysql.cpp ${SOCI_TESTS_COMMON} - CONNSTR "db=soci_test") +soci_make_tests( + OBJECT_LIB mysql_tests + CONNECTION_STRING "db=soci_test" + SHARED_NAME "soci_mysql_test" + STATIC_NAME "soci_mysql_test_static" + SOCI_DEP_ALIAS "MySQL" +) diff --git a/tests/mysql/test-mysql.cpp b/tests/mysql/mysql_tests.cpp similarity index 97% rename from tests/mysql/test-mysql.cpp rename to tests/mysql/mysql_tests.cpp index cc5c50c5f..af9e8ebd8 100644 --- a/tests/mysql/test-mysql.cpp +++ b/tests/mysql/mysql_tests.cpp @@ -6,11 +6,14 @@ // http://www.boost.org/LICENSE_1_0.txt) // -#include "soci/soci.h" +#include + +#include "mysql/mysql_tests.h" +#include "soci/soci.h" #include "soci-compiler.h" #include "soci/mysql/soci-mysql.h" -#include "mysql/test-mysql.h" + #include #include #include @@ -794,29 +797,21 @@ void test15() std::cout << "test 15 passed" << std::endl; } -int main(int argc, char** argv) +namespace soci +{ +namespace tests { - if (argc >= 2) - { - connectString = argv[1]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; - argc--; - argv++; - } - else - { - std::cout << "usage: " << argv[0] - << " connectstring [test-arguments...]\n" - << "example: " << argv[0] - << " \"dbname=test user=root password=\'Ala ma kota\'\"\n"; - std::exit(1); - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/mysql/test-mysql.h b/tests/mysql/mysql_tests.h similarity index 100% rename from tests/mysql/test-mysql.h rename to tests/mysql/mysql_tests.h diff --git a/tests/oracle/CMakeLists.txt b/tests/oracle/CMakeLists.txt index da6767ba9..17cd32e82 100644 --- a/tests/oracle/CMakeLists.txt +++ b/tests/oracle/CMakeLists.txt @@ -1,16 +1,10 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(oracle_tests OBJECT oracle_tests.cpp) +target_link_libraries(oracle_tests PUBLIC soci_common_tests soci_oracle_interface) -soci_backend_test( - BACKEND Oracle - DEPENDS Oracle - SOURCE test-oracle.cpp ${SOCI_TESTS_COMMON} - CONNSTR "service=orcl user=scott password=tiger") +soci_make_tests( + OBJECT_LIB oracle_tests + CONNECTION_STRING "service=orcl user=scott password=tiger" + SHARED_NAME "soci_oracle_test" + STATIC_NAME "soci_oracle_test_static" + SOCI_DEP_ALIAS "Oracle" +) diff --git a/tests/oracle/test-oracle.cpp b/tests/oracle/oracle_tests.cpp similarity index 97% rename from tests/oracle/test-oracle.cpp rename to tests/oracle/oracle_tests.cpp index 8af746c13..6443391db 100644 --- a/tests/oracle/test-oracle.cpp +++ b/tests/oracle/oracle_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + #include "common-tests.h" + #include "soci/soci.h" #include "soci/oracle/soci-oracle.h" + #include #include #include @@ -142,7 +146,7 @@ TEST_CASE("Oracle datetime", "[oracle][datetime]") CHECK(t4.tm_year == t2.tm_year); CHECK((1900 + t4.tm_year) == i); } - } + } } // explicit calls test @@ -1536,44 +1540,21 @@ class test_context :public test_context_base } }; -int main(int argc, char** argv) +namespace soci +{ +namespace tests { -#ifdef _MSC_VER - // Redirect errors, unrecoverable problems, and assert() failures to STDERR, - // instead of debug message window. - // This hack is required to run assert()-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]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; - - argc--; - argv++; - } - else - { - std::cout << "usage: " << argv[0] - << " connectstring [test-arguments...]\n" - << "example: " << argv[0] - << " \'service=orcl user=scott password=tiger\'\n"; - std::exit(1); - } - if (!std::getenv("ORACLE_HOME")) - { - std::cerr << "ORACLE_HOME environment variable must be defined for Oracle tests.\n"; - std::exit(1); - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/postgresql/CMakeLists.txt b/tests/postgresql/CMakeLists.txt index 0fac5737b..8b3eff40f 100644 --- a/tests/postgresql/CMakeLists.txt +++ b/tests/postgresql/CMakeLists.txt @@ -1,16 +1,10 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(postgresql_tests OBJECT postgresql_tests.cpp) +target_link_libraries(postgresql_tests PUBLIC soci_common_tests soci_postgresql_interface) -soci_backend_test( - BACKEND PostgreSQL - DEPENDS PostgreSQL - SOURCE test-postgresql.cpp ${SOCI_TESTS_COMMON} - CONNSTR "dbname=soci_test") +soci_make_tests( + OBJECT_LIB postgresql_tests + CONNECTION_STRING "dbname=soci_test" + SHARED_NAME "soci_postgresql_test" + STATIC_NAME "soci_postgresql_test_static" + SOCI_DEP_ALIAS "PostgreSQL" +) diff --git a/tests/postgresql/test-postgresql.cpp b/tests/postgresql/postgresql_tests.cpp similarity index 97% rename from tests/postgresql/test-postgresql.cpp rename to tests/postgresql/postgresql_tests.cpp index 16ce3fc8d..df9d6d024 100644 --- a/tests/postgresql/test-postgresql.cpp +++ b/tests/postgresql/postgresql_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/postgresql/soci-postgresql.h" -#include "common-tests.h" + #include #include #include @@ -1401,39 +1405,21 @@ class test_context : public test_context_base } }; -int main(int argc, char** argv) +namespace soci +{ +namespace tests { -#ifdef _MSC_VER - // Redirect errors, unrecoverable problems, and assert() failures to STDERR, - // instead of debug message window. - // This hack is required to run assert()-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]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; - - argc--; - argv++; - } - else - { - std::cout << "usage: " << argv[0] - << " connectstring [test-arguments...]\n" - << "example: " << argv[0] - << " \'connect_string_for_PostgreSQL\'\n"; - return EXIT_FAILURE; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/sqlite3/CMakeLists.txt b/tests/sqlite3/CMakeLists.txt index 0fe8e7738..42bdc518c 100644 --- a/tests/sqlite3/CMakeLists.txt +++ b/tests/sqlite3/CMakeLists.txt @@ -1,16 +1,11 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### +add_library(sqlite_tests OBJECT sqlite3_tests.cpp) +target_link_libraries(sqlite_tests PUBLIC soci_common_tests soci_sqlite3_interface) + +soci_make_tests( + OBJECT_LIB sqlite_tests + CONNECTION_STRING ":memory:" + SHARED_NAME "soci_sqlite3_test" + STATIC_NAME "soci_sqlite3_test_static" + SOCI_DEP_ALIAS "SQLite3" +) -soci_backend_test( - BACKEND SQLite3 - DEPENDS SQLite3 - SOURCE test-sqlite3.cpp ${SOCI_TESTS_COMMON} - CONNSTR ":memory:") diff --git a/tests/sqlite3/test-sqlite3.cpp b/tests/sqlite3/sqlite3_tests.cpp similarity index 96% rename from tests/sqlite3/test-sqlite3.cpp rename to tests/sqlite3/sqlite3_tests.cpp index 3af80d86a..95fc5dc5c 100644 --- a/tests/sqlite3/test-sqlite3.cpp +++ b/tests/sqlite3/sqlite3_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include #include -#include "common-tests.h" + #include #include #include @@ -15,6 +19,7 @@ #include #include #include +#include using namespace soci; using namespace soci::tests; @@ -887,36 +892,22 @@ class test_context : public test_context_base } }; -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 assert()-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 && argv[1][0] != '-') - { - connectString = argv[1]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - // If no file name is specfied then work in-memory - connectString = ":memory:"; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } From bb86d445f4563c1602ae742e5e267bdc133b9a40 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 15:58:58 +0100 Subject: [PATCH 12/79] Add options to configure test connection strings --- tests/db2/CMakeLists.txt | 4 +++- tests/firebird/CMakeLists.txt | 4 +++- tests/mysql/CMakeLists.txt | 4 +++- tests/oracle/CMakeLists.txt | 4 +++- tests/postgresql/CMakeLists.txt | 4 +++- tests/sqlite3/CMakeLists.txt | 4 +++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/db2/CMakeLists.txt b/tests/db2/CMakeLists.txt index e5e6d098c..a6e1e9af1 100644 --- a/tests/db2/CMakeLists.txt +++ b/tests/db2/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(db2_tests OBJECT db2_tests.cpp) target_link_libraries(db2_tests PUBLIC soci_common_tests soci_db2_interface) +set(SOCI_DB2_TEST_CONNSTR "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off" CACHE STRING "The connection string to use for DB2 tests") + soci_make_tests( OBJECT_LIB db2_tests - CONNECTION_STRING "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off" + CONNECTION_STRING "${SOCI_DB2_TEST_CONNSTR}" SHARED_NAME "soci_db2_test" STATIC_NAME "soci_db2_test_static" SOCI_DEP_ALIAS "DB2" diff --git a/tests/firebird/CMakeLists.txt b/tests/firebird/CMakeLists.txt index 442e2695b..eba55c027 100644 --- a/tests/firebird/CMakeLists.txt +++ b/tests/firebird/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(firebird_tests OBJECT firebird_tests.cpp) target_link_libraries(firebird_tests PUBLIC soci_common_tests soci_firebird_interface) +set(SOCI_FIREBIRD_TEST_CONNSTR "service=/tmp/soci_test.fdb user=SYSDBA password=masterkey" CACHE STRING "The connection string to use for Firebird tests") + soci_make_tests( OBJECT_LIB firebird_tests - CONNECTION_STRING "service=/tmp/soci_test.fdb user=SYSDBA password=masterkey" + CONNECTION_STRING "${SOCI_FIREBIRD_TEST_CONNSTR}" SHARED_NAME "soci_firebird_test" STATIC_NAME "soci_firebird_test_static" SOCI_DEP_ALIAS "Firebird" diff --git a/tests/mysql/CMakeLists.txt b/tests/mysql/CMakeLists.txt index c4006ac24..0022c6886 100644 --- a/tests/mysql/CMakeLists.txt +++ b/tests/mysql/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(mysql_tests OBJECT mysql_tests.cpp) target_link_libraries(mysql_tests PUBLIC soci_common_tests soci_mysql_interface) +set(SOCI_MYSQL_TEST_CONNSTR "db=soci_test" CACHE STRING "The connection string to use for MySQL tests") + soci_make_tests( OBJECT_LIB mysql_tests - CONNECTION_STRING "db=soci_test" + CONNECTION_STRING "${SOCI_MYSQL_TEST_CONNSTR}" SHARED_NAME "soci_mysql_test" STATIC_NAME "soci_mysql_test_static" SOCI_DEP_ALIAS "MySQL" diff --git a/tests/oracle/CMakeLists.txt b/tests/oracle/CMakeLists.txt index 17cd32e82..49f6ec0da 100644 --- a/tests/oracle/CMakeLists.txt +++ b/tests/oracle/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(oracle_tests OBJECT oracle_tests.cpp) target_link_libraries(oracle_tests PUBLIC soci_common_tests soci_oracle_interface) +set(SOCI_ORACLE_TEST_CONNSTR "service=orcl user=scott password=tiger" CACHE STRING "The connection string to use for Oracle tests") + soci_make_tests( OBJECT_LIB oracle_tests - CONNECTION_STRING "service=orcl user=scott password=tiger" + CONNECTION_STRING "${SOCI_ORACLE_TEST_CONNSTR}" SHARED_NAME "soci_oracle_test" STATIC_NAME "soci_oracle_test_static" SOCI_DEP_ALIAS "Oracle" diff --git a/tests/postgresql/CMakeLists.txt b/tests/postgresql/CMakeLists.txt index 8b3eff40f..89cd142b6 100644 --- a/tests/postgresql/CMakeLists.txt +++ b/tests/postgresql/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(postgresql_tests OBJECT postgresql_tests.cpp) target_link_libraries(postgresql_tests PUBLIC soci_common_tests soci_postgresql_interface) +set(SOCI_POSTGRESQL_TEST_CONNSTR "dbname=soci_test" CACHE STRING "The connection string to use for PostgreSQL tests") + soci_make_tests( OBJECT_LIB postgresql_tests - CONNECTION_STRING "dbname=soci_test" + CONNECTION_STRING "${SOCI_POSTGRESQL_TEST_CONNSTR}" SHARED_NAME "soci_postgresql_test" STATIC_NAME "soci_postgresql_test_static" SOCI_DEP_ALIAS "PostgreSQL" diff --git a/tests/sqlite3/CMakeLists.txt b/tests/sqlite3/CMakeLists.txt index 42bdc518c..16b253d2a 100644 --- a/tests/sqlite3/CMakeLists.txt +++ b/tests/sqlite3/CMakeLists.txt @@ -1,9 +1,11 @@ add_library(sqlite_tests OBJECT sqlite3_tests.cpp) target_link_libraries(sqlite_tests PUBLIC soci_common_tests soci_sqlite3_interface) +set(SOCI_SQLITE3_TEST_CONNSTR ":memory:" CACHE STRING "The connection string to use for SQLite3 tests") + soci_make_tests( OBJECT_LIB sqlite_tests - CONNECTION_STRING ":memory:" + CONNECTION_STRING "${SOCI_SQLITE3_TEST_CONNSTR}" SHARED_NAME "soci_sqlite3_test" STATIC_NAME "soci_sqlite3_test_static" SOCI_DEP_ALIAS "SQLite3" From 0e5c799acf709d4a57d5c32a67ef5dbe77769b18 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 16:05:53 +0100 Subject: [PATCH 13/79] CI: ODBC now also uses the default runner Fixes #1005 --- .github/workflows/ci.yml | 2 -- scripts/ci/build_odbc.sh | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74390eebd..f378ae76f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,8 +40,6 @@ jobs: - backend: valgrind name: Valgrind - backend: odbc - # There are many leak reports under Ubuntu 22.04, see #1008. - container: ubuntu:18.04 name: ODBC - backend: firebird name: Firebird diff --git a/scripts/ci/build_odbc.sh b/scripts/ci/build_odbc.sh index 0d64965c4..66d1b1412 100755 --- a/scripts/ci/build_odbc.sh +++ b/scripts/ci/build_odbc.sh @@ -11,7 +11,9 @@ if test ! -d ${ODBC_TEST}; then exit 1 fi +# Disable ASAN -> see https://github.com/SOCI/soci/issues/1008 cmake ${SOCI_DEFAULT_CMAKE_OPTIONS} \ + -DSOCI_ASAN=OFF \ -DSOCI_ODBC=ON \ .. From f49841cdecdb08099613abe9640061bbc7d9d1fe Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 16:25:32 +0100 Subject: [PATCH 14/79] Bring ODBC tests up to speed --- tests/odbc/CMakeLists.txt | 109 ++++++++++-------- .../{test-odbc-db2.cpp => odbc_db2_tests.cpp} | 49 +++----- ...bc-access.cpp => odbc_ms_access_tests.cpp} | 47 +++----- ...st-odbc-mssql.cpp => odbc_mssql_tests.cpp} | 46 +++----- ...st-odbc-mysql.cpp => odbc_mysql_tests.cpp} | 47 +++----- ...stgresql.cpp => odbc_postgresql_tests.cpp} | 47 +++----- 6 files changed, 155 insertions(+), 190 deletions(-) rename tests/odbc/{test-odbc-db2.cpp => odbc_db2_tests.cpp} (86%) rename tests/odbc/{test-odbc-access.cpp => odbc_ms_access_tests.cpp} (76%) rename tests/odbc/{test-odbc-mssql.cpp => odbc_mssql_tests.cpp} (87%) rename tests/odbc/{test-odbc-mysql.cpp => odbc_mysql_tests.cpp} (56%) rename tests/odbc/{test-odbc-postgresql.cpp => odbc_postgresql_tests.cpp} (89%) diff --git a/tests/odbc/CMakeLists.txt b/tests/odbc/CMakeLists.txt index c57db6750..3b6aa9fa4 100644 --- a/tests/odbc/CMakeLists.txt +++ b/tests/odbc/CMakeLists.txt @@ -1,69 +1,80 @@ -############################################################################### -# -# This file is part of CMake configuration for SOCI library -# -# Copyright (C) 2010-2013 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) -# -############################################################################### - -if (WIN32) - # MDBTools driver seems unreliable - soci_backend_test( - NAME access - BACKEND ODBC - DEPENDS ODBC - SOURCE test-odbc-access.cpp ${SOCI_TESTS_COMMON} - CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-access.dsn") -else() - message(STATUS "MS Access test disabled on non-Windows platform") -endif() - set(MSSQL_VER $ENV{MSSQL_VER}) if (NOT MSSQL_VER) # Use the same value that was used before by default. set(MSSQL_VER "2014") endif() -configure_file("test-mssql.dsn.in" "test-mssql.dsn" @ONLY) +configure_file("test-mssql.dsn.in" "${CMAKE_CURRENT_BINARY_DIR}/test-mssql.dsn" @ONLY) + + +if (WIN32) + add_library(odbc_ms_access_tests OBJECT odbc_ms_access_tests.cpp) + target_link_libraries(odbc_ms_access_tests PUBLIC soci_common_tests soci_odbc_interface) + + soci_make_tests( + OBJECT_LIB odbc_ms_access_tests + CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" + SHARED_NAME "soci_odbc_ms_access_test" + STATIC_NAME "soci_odbc_ms_access_test_static" + SOCI_DEP_ALIAS "ODBC" + ) +endif() -soci_backend_test( - NAME mssql - BACKEND ODBC - DEPENDS ODBC - SOURCE test-odbc-mssql.cpp ${SOCI_TESTS_COMMON} - CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-mssql.dsn") -soci_backend_test( - NAME mysql - BACKEND ODBC - DEPENDS ODBC - SOURCE test-odbc-mysql.cpp ${SOCI_TESTS_COMMON} - CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-mysql.dsn") +add_library(odbc_mssql_tests OBJECT odbc_mssql_tests.cpp) +target_link_libraries(odbc_mssql_tests PUBLIC soci_common_tests soci_odbc_interface) + +soci_make_tests( + OBJECT_LIB odbc_mssql_tests + CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" + SHARED_NAME "soci_odbc_mssql_test" + STATIC_NAME "soci_odbc_mssql_test_static" + SOCI_DEP_ALIAS "ODBC" +) + + +add_library(odbc_mysql_tests OBJECT odbc_mysql_tests.cpp) +target_link_libraries(odbc_mysql_tests PUBLIC soci_common_tests soci_odbc_interface) +target_include_directories(odbc_mysql_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") + +soci_make_tests( + OBJECT_LIB odbc_mysql_tests + CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-mysql.dsn" + SHARED_NAME "soci_odbc_mysql_test" + STATIC_NAME "soci_odbc_mysql_test_static" + SOCI_DEP_ALIAS "ODBC" +) + if(WIN32) set(TEST_PGSQL_DSN "test-postgresql-win64.dsn") else() set(TEST_PGSQL_DSN "test-postgresql.dsn") endif() -soci_backend_test( - NAME postgresql - BACKEND ODBC - DEPENDS ODBC - SOURCE test-odbc-postgresql.cpp ${SOCI_TESTS_COMMON} - CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/${TEST_PGSQL_DSN}") + +add_library(odbc_postgresql_tests OBJECT odbc_postgresql_tests.cpp) +target_link_libraries(odbc_postgresql_tests PUBLIC soci_common_tests soci_odbc_interface) + +soci_make_tests( + OBJECT_LIB odbc_postgresql_tests + CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/${TEST_PGSQL_DSN}" + SHARED_NAME "soci_odbc_postgresql_test" + STATIC_NAME "soci_odbc_postgresql_test_static" + SOCI_DEP_ALIAS "ODBC" +) # 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}) option(WITH_ODBC_TEST_DB2 "Build ODBC DB2 test" OFF) if (WITH_ODBC_TEST_DB2) - soci_backend_test( - NAME db2 - BACKEND ODBC - SOURCE test-odbc-db2.cpp - CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-db2.dsn") -else() - message(STATUS "ODBC DB2 test disabled.") + add_library(odbc_db2_tests OBJECT odbc_db2_tests.cpp) + target_link_libraries(odbc_db2_tests PUBLIC soci_common_tests soci_odbc_interface) + + soci_make_tests( + OBJECT_LIB odbc_db2_tests + CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-db2.dsn" + SHARED_NAME "soci_odbc_db2_test" + STATIC_NAME "soci_odbc_db2_test_static" + SOCI_DEP_ALIAS "ODBC" + ) endif() diff --git a/tests/odbc/test-odbc-db2.cpp b/tests/odbc/odbc_db2_tests.cpp similarity index 86% rename from tests/odbc/test-odbc-db2.cpp rename to tests/odbc/odbc_db2_tests.cpp index 91788949e..b3a1c4af0 100644 --- a/tests/odbc/test-odbc-db2.cpp +++ b/tests/odbc/odbc_db2_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/odbc/soci-odbc.h" -#include "common-tests.h" + #include #include #include @@ -251,37 +255,22 @@ TEST_CASE("ODBC/DB2 vector unsigned long long", "[odbc][db2][vector][unsigned][l 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 assert()-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]; - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - std::cerr << std::endl << - "usage: test-odbc-db2 \"DSN=;Uid=;Pwd=\"" << - std::endl << std::endl; - return EXIT_FAILURE; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/odbc/test-odbc-access.cpp b/tests/odbc/odbc_ms_access_tests.cpp similarity index 76% rename from tests/odbc/test-odbc-access.cpp rename to tests/odbc/odbc_ms_access_tests.cpp index 2783646f4..43d58bd0b 100644 --- a/tests/odbc/test-odbc-access.cpp +++ b/tests/odbc/odbc_ms_access_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/odbc/soci-odbc.h" -#include "common-tests.h" + #include #include #include @@ -114,35 +118,22 @@ class test_context : public test_context_base } }; -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 assert()-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 && argv[1][0] != '-') - { - connectString = argv[1]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - connectString = "FILEDSN=./test-access.dsn"; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/odbc/test-odbc-mssql.cpp b/tests/odbc/odbc_mssql_tests.cpp similarity index 87% rename from tests/odbc/test-odbc-mssql.cpp rename to tests/odbc/odbc_mssql_tests.cpp index 8b3ecd740..0ec3e3e6b 100644 --- a/tests/odbc/test-odbc-mssql.cpp +++ b/tests/odbc/odbc_mssql_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/odbc/soci-odbc.h" -#include "common-tests.h" + #include #include #include @@ -216,34 +220,22 @@ class test_context : public test_context_base } }; -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 assert()-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 && argv[1][0] != '-') - { - connectString = argv[1]; - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - connectString = "FILEDSN=./test-mssql.dsn"; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/odbc/test-odbc-mysql.cpp b/tests/odbc/odbc_mysql_tests.cpp similarity index 56% rename from tests/odbc/test-odbc-mysql.cpp rename to tests/odbc/odbc_mysql_tests.cpp index fa8eb7499..9afb20ae6 100644 --- a/tests/odbc/test-odbc-mysql.cpp +++ b/tests/odbc/odbc_mysql_tests.cpp @@ -5,15 +5,18 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "mysql/mysql_tests.h" + #include "soci/soci.h" #include "soci/odbc/soci-odbc.h" + #include #include #include #include -#include "mysql/test-mysql.h" - std::string connectString; backend_factory const &backEnd = *soci::factory_odbc(); @@ -40,34 +43,22 @@ class test_context_odbc : public test_context } }; -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 assert()-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 && argv[1][0] != '-') - { - connectString = argv[1]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - connectString = "FILEDSN=./test-mysql.dsn"; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context_odbc tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } diff --git a/tests/odbc/test-odbc-postgresql.cpp b/tests/odbc/odbc_postgresql_tests.cpp similarity index 89% rename from tests/odbc/test-odbc-postgresql.cpp rename to tests/odbc/odbc_postgresql_tests.cpp index 424f27f25..206ef1466 100644 --- a/tests/odbc/test-odbc-postgresql.cpp +++ b/tests/odbc/odbc_postgresql_tests.cpp @@ -5,9 +5,13 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#include + +#include "common-tests.h" + #include "soci/soci.h" #include "soci/odbc/soci-odbc.h" -#include "common-tests.h" + #include #include #include @@ -282,35 +286,22 @@ class test_context : public test_context_base odbc_version const m_verDriver; }; -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 assert()-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 && argv[1][0] != '-') - { - connectString = argv[1]; - - // Replace the connect string with the process name to ensure that - // CATCH uses the correct name in its messages. - argv[1] = argv[0]; +namespace soci +{ +namespace tests +{ - argc--; - argv++; - } - else - { - connectString = "FILEDSN=./test-postgresql.dsn"; - } +std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) +{ + connectString = connection_string; + return std::make_unique(backend, connection_string); +} - test_context tc(backEnd, connectString); +const backend_factory &create_backend_factory() +{ + return backEnd; +} - return Catch::Session().run(argc, argv); +} } From b593a29cf2ee64b6f9149ea7adbd2cd1585211b5 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 17:02:02 +0100 Subject: [PATCH 15/79] Expose ODBC test connector strings --- tests/odbc/CMakeLists.txt | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/odbc/CMakeLists.txt b/tests/odbc/CMakeLists.txt index 3b6aa9fa4..282082b2f 100644 --- a/tests/odbc/CMakeLists.txt +++ b/tests/odbc/CMakeLists.txt @@ -10,9 +10,11 @@ if (WIN32) add_library(odbc_ms_access_tests OBJECT odbc_ms_access_tests.cpp) target_link_libraries(odbc_ms_access_tests PUBLIC soci_common_tests soci_odbc_interface) + set(SOCI_ODBC_TEST_ACCESS_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MS Access test") + soci_make_tests( OBJECT_LIB odbc_ms_access_tests - CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" + CONNECTION_STRING "${SOCI_ODBC_TEST_ACCESS_CONNSTR}" SHARED_NAME "soci_odbc_ms_access_test" STATIC_NAME "soci_odbc_ms_access_test_static" SOCI_DEP_ALIAS "ODBC" @@ -23,9 +25,11 @@ endif() add_library(odbc_mssql_tests OBJECT odbc_mssql_tests.cpp) target_link_libraries(odbc_mssql_tests PUBLIC soci_common_tests soci_odbc_interface) +set(SOCI_ODBC_TEST_MSSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MSSQL test") + soci_make_tests( OBJECT_LIB odbc_mssql_tests - CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" + CONNECTION_STRING "${SOCI_ODBC_TEST_MSSQL_CONNSTR}" SHARED_NAME "soci_odbc_mssql_test" STATIC_NAME "soci_odbc_mssql_test_static" SOCI_DEP_ALIAS "ODBC" @@ -36,9 +40,11 @@ add_library(odbc_mysql_tests OBJECT odbc_mysql_tests.cpp) target_link_libraries(odbc_mysql_tests PUBLIC soci_common_tests soci_odbc_interface) target_include_directories(odbc_mysql_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") +set(SOCI_ODBC_TEST_MYSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-mysql.dsn" CACHE STRING "Connection string for the ODBC MySQL test") + soci_make_tests( OBJECT_LIB odbc_mysql_tests - CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-mysql.dsn" + CONNECTION_STRING "${SOCI_ODBC_TEST_MYSQL_CONNSTR}" SHARED_NAME "soci_odbc_mysql_test" STATIC_NAME "soci_odbc_mysql_test_static" SOCI_DEP_ALIAS "ODBC" @@ -54,9 +60,11 @@ endif() add_library(odbc_postgresql_tests OBJECT odbc_postgresql_tests.cpp) target_link_libraries(odbc_postgresql_tests PUBLIC soci_common_tests soci_odbc_interface) +set(SOCI_ODBC_TEST_POSTGRESQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/${TEST_PGSQL_DSN}" CACHE STRING "Connection string for the ODBC PostgreSQL test") + soci_make_tests( OBJECT_LIB odbc_postgresql_tests - CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/${TEST_PGSQL_DSN}" + CONNECTION_STRING "${SOCI_ODBC_TEST_POSTGRESQL_CONNSTR}" SHARED_NAME "soci_odbc_postgresql_test" STATIC_NAME "soci_odbc_postgresql_test_static" SOCI_DEP_ALIAS "ODBC" @@ -70,9 +78,11 @@ if (WITH_ODBC_TEST_DB2) add_library(odbc_db2_tests OBJECT odbc_db2_tests.cpp) target_link_libraries(odbc_db2_tests PUBLIC soci_common_tests soci_odbc_interface) + set(SOCI_ODBC_TEST_DB2_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-db2.dsn" CACHE STRING "Connection string for the ODBC DB2 test") + soci_make_tests( OBJECT_LIB odbc_db2_tests - CONNECTION_STRING "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-db2.dsn" + CONNECTION_STRING "${SOCI_ODBC_TEST_DB2_CONNSTR}" SHARED_NAME "soci_odbc_db2_test" STATIC_NAME "soci_odbc_db2_test_static" SOCI_DEP_ALIAS "ODBC" From 672213cd8df18e48b749e8a75a5e9997e1d103b9 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 17:07:15 +0100 Subject: [PATCH 16/79] Use dedicated test context --- tests/odbc/odbc_mysql_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/odbc/odbc_mysql_tests.cpp b/tests/odbc/odbc_mysql_tests.cpp index 9afb20ae6..8967cf89f 100644 --- a/tests/odbc/odbc_mysql_tests.cpp +++ b/tests/odbc/odbc_mysql_tests.cpp @@ -52,7 +52,7 @@ namespace tests std::unique_ptr instantiate_test_context(const soci::backend_factory &backend, const std::string &connection_string) { connectString = connection_string; - return std::make_unique(backend, connection_string); + return std::make_unique(backend, connection_string); } const backend_factory &create_backend_factory() From 207e17e052cbbaadc6673cf418ed129f927cc9f1 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 17 Feb 2024 17:17:47 +0100 Subject: [PATCH 17/79] Adapt ODBC test exclusion RegEx --- scripts/ci/test_odbc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/test_odbc.sh b/scripts/ci/test_odbc.sh index ebc011a85..27fa608c1 100755 --- a/scripts/ci/test_odbc.sh +++ b/scripts/ci/test_odbc.sh @@ -8,4 +8,4 @@ source ${SOCI_SOURCE_DIR}/scripts/ci/common.sh # Exclude the tests which can't be run due to the absence of ODBC drivers (MS # SQL and MySQL). -LSAN_OPTIONS=suppressions=${SOCI_SOURCE_DIR}/scripts/suppress_odbc.txt run_test -E 'soci_odbc_test_m.sql' +LSAN_OPTIONS=suppressions=${SOCI_SOURCE_DIR}/scripts/suppress_odbc.txt run_test -E 'soci_odbc_m.sql_test' From dd5e3dd97291461e2f837628188cdc7d78a80274 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 15:24:58 +0100 Subject: [PATCH 18/79] Build shared and static libs separately --- .github/workflows/ci.yml | 69 ++++++---------- CMakeLists.txt | 18 ++-- scripts/ci/common.sh | 4 + src/CMakeLists.txt | 19 ----- src/backends/CMakeLists.txt | 41 ---------- src/backends/db2/CMakeLists.txt | 43 +++++----- src/backends/empty/CMakeLists.txt | 38 ++++----- src/backends/firebird/CMakeLists.txt | 45 +++++----- src/backends/mysql/CMakeLists.txt | 45 +++++----- src/backends/odbc/CMakeLists.txt | 43 +++++----- src/backends/oracle/CMakeLists.txt | 46 +++++------ src/backends/postgresql/CMakeLists.txt | 52 ++++++------ src/backends/sqlite3/CMakeLists.txt | 44 +++++----- src/core/CMakeLists.txt | 109 +++++++++++-------------- tests/CMakeLists.txt | 46 +---------- tests/db2/CMakeLists.txt | 13 ++- tests/empty/CMakeLists.txt | 15 ++-- tests/firebird/CMakeLists.txt | 13 ++- tests/mysql/CMakeLists.txt | 13 ++- tests/odbc/CMakeLists.txt | 68 ++++++--------- tests/oracle/CMakeLists.txt | 13 ++- tests/postgresql/CMakeLists.txt | 13 ++- tests/sqlite3/CMakeLists.txt | 14 ++-- 23 files changed, 316 insertions(+), 508 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f378ae76f..1c756f972 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,52 +22,40 @@ on: jobs: build: - runs-on: ${{ matrix.runner || 'ubuntu-22.04' }} - container: ${{ matrix.container }} - name: ${{ matrix.name }} strategy: fail-fast: false matrix: + lib_type: [shared, static] + # Can't test DB2 as required db2exc package is no longer available after Ubuntu 14.04 + backend: [sqlite3, postgresql, mysql, firebird, oracle, odbc, empty, valgrind] + runner: [ubuntu-22.04] + cxxstd: [14] + test_release_package: [false] + build_examples: [false] include: - # Note: the jobs are ordered in the order of decreasing running - # time, as this should minimize the total run-time of all jobs. - - backend: postgresql + - backend: empty runner: macos-12 - name: PostgreSQL macOS - - backend: oracle - name: Oracle 11 - no_boost: true - - backend: valgrind - name: Valgrind - - backend: odbc - name: ODBC - - backend: firebird - name: Firebird - backend: postgresql - name: PostgreSQL Linux - - backend: mysql - name: MySQL - - backend: sqlite3 runner: macos-12 - name: SQLite3 macOS - backend: sqlite3 - name: SQLite3 C++17 - cxxstd: 17 - - backend: sqlite3 - name: SQLite3 - - backend: empty runner: macos-12 - name: Empty macOS - - backend: empty - name: Empty + - backend: oracle + no_boost: true + runner: ubuntu-22.04 + - name: SQLite3 Cxx17 + backend: sqlite3 + cxxstd: 17 + runner: ubuntu-22.04 + - name: Release package + backend: empty test_release_package: true - # Unsupported: db2exc package is only available in Ubuntu 14.04 not - # supported by GitHub Actions any longer, we'd need to run it in - # Docker container if we really need it. - # backend: db2 - - backend: empty - name: Examples + runner: ubuntu-22.04 + - name: Examples + backend: empty build_examples: true + runner: ubuntu-22.04 + + runs-on: ${{ matrix.runner }} env: SOCI_CI: true @@ -118,14 +106,6 @@ jobs: ;; esac - case "${{matrix.container}}" in - ubuntu:18.04) - # We need to use this option as GitHub certificate is not recognized by - # wget in this old container otherwise. - set_env_var SOCI_WGET_OPTIONS --no-check-certificate - ;; - esac - if [ -n "${{matrix.cxxstd}}" ]; then set_env_var SOCI_CXXSTD ${{matrix.cxxstd}} fi @@ -138,6 +118,9 @@ jobs: if [ "${{matrix.build_examples}}" = true ]; then set_env_var BUILD_EXAMPLES YES fi + if [ "${{matrix.lib_type}}" = "static" ]; then + set_env_var SOCI_BUILD_STATIC YES + fi - name: Install dependencies under Linux if: runner.os == 'Linux' diff --git a/CMakeLists.txt b/CMakeLists.txt index 05242b810..62f22899c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,13 @@ include(CheckCXXCompilerFlag) check_ipo_supported(RESULT LTO_AVAILABLE) -option(SOCI_SHARED "Enable building SOCI as a shared library" ON) -option(SOCI_STATIC "Enable building SOCI as a static library" ON) +if (SOCI_STATIC) + set(SHARED_DEFAULT OFF) +else() + set(SHARED_DEFAULT ON) +endif() + +option(SOCI_SHARED "Enable building SOCI as a shared library" ${SHARED_DEFAULT}) option(SOCI_TESTS "Enable building SOCI test cases" ${PROJECT_IS_TOP_LEVEL}) option(SOCI_ASAN "Enable building SOCI with enabled address sanitizers" OFF) cmake_dependent_option(SOCI_LTO "Enable link time optimizations in release builds" ON "LTO_AVAILABLE" OFF) @@ -61,11 +66,10 @@ 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) -# We need a dummy source file to add as sources for libs that otherwise -# only consist of object libraries (otherwise some toolchains may complain) -set(SOCI_CXX_DUMMY_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp") -if (NOT EXISTS "${SOCI_CXX_DUMMY_SOURCE}") - file(TOUCH "${SOCI_CXX_DUMMY_SOURCE}") +if (SOCI_SHARED) + set(SOCI_LIB_TYPE "SHARED") +else() + set(SOCI_LIB_TYPE "STATIC") endif() add_subdirectory(src) diff --git a/scripts/ci/common.sh b/scripts/ci/common.sh index 94a71201d..c29fdd658 100644 --- a/scripts/ci/common.sh +++ b/scripts/ci/common.sh @@ -63,6 +63,10 @@ if [ -n "${WITH_BOOST}" ]; then SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DWITH_BOOST=${WITH_BOOST}" fi +if [ -n "${SOCI_BUILD_STATIC}" ]; then + SOCI_COMMON_CMAKE_OPTIONS="${SOCI_COMMON_CMAKE_OPTIONS} -DSOCI_SHARED=OFF" +fi + # These options are defaults and used by most builds, but not Valgrind one. SOCI_DEFAULT_CMAKE_OPTIONS="${SOCI_COMMON_CMAKE_OPTIONS} -DSOCI_ASAN=ON diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54acfd04c..252bf4d92 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,24 +1,5 @@ -add_library(soci_shared_interface INTERFACE) -add_library(SOCI::shared::soci ALIAS soci_shared_interface) - -add_library(soci_static_interface INTERFACE) -add_library(SOCI::static::soci ALIAS soci_static_interface) - add_library(soci_interface INTERFACE) add_library(SOCI::soci ALIAS soci_interface) -if (SOCI_STATIC AND SOCI_SHARED) - if (BUILD_SHARED_LIBS) - target_link_libraries(soci_interface INTERFACE SOCI::shared::soci) - else() - target_link_libraries(soci_interface INTERFACE SOCI::static::soci) - endif() -elseif(SOCI_STATIC) - target_link_libraries(soci_interface INTERFACE SOCI::static::soci) -else() - target_link_libraries(soci_interface INTERFACE SOCI::shared::soci) -endif() - - add_subdirectory(core) add_subdirectory(backends) diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 052393683..6b49cd19a 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,5 +1,3 @@ -include(soci_utils) - set(SOCI_EMPTY ${PROJECT_IS_TOP_LEVEL} CACHE STRING "Include the 'empty' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") set(SOCI_DB2 "AsAvailable" CACHE STRING "Include the 'DB2' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") @@ -11,45 +9,6 @@ set(SOCI_POSTGRESQL "AsAvailable" CACHE STRING "Include the 'Postgresql' backend set(SOCI_SQLITE3 "AsAvailable" CACHE STRING "Include the 'SQLite3' backend. Can be bool-valued or one of 'Enabled', 'Disabled' and 'AsAvailable'") -function(soci_backend_objects_to_lib) - set(FLAGS "") - set(ONE_VAL_OPTIONS "OBJECT_LIB" "SHARED_TARGET_NAME" "STATIC_TARGET_NAME" "ALIAS_NAME") - set(MULTI_VAL_OPTIONS "SHARED_DEPS" "STATIC_DEPS") - cmake_parse_arguments(SOCI_BACKEND_LIB "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) - soci_verify_parsed_arguments( - PREFIX "SOCI_BACKEND_LIB" - FUNCTION_NAME "soci_backend_objects_to_lib" - REQUIRED "OBJECT_LIB" "SHARED_TARGET_NAME" "STATIC_TARGET_NAME" "ALIAS_NAME" - ) - - if (SOCI_SHARED) - add_library(${SOCI_BACKEND_LIB_SHARED_TARGET_NAME} SHARED "${SOCI_CXX_DUMMY_SOURCE}") - target_link_libraries(${SOCI_BACKEND_LIB_SHARED_TARGET_NAME} - PUBLIC - ${SOCI_BACKEND_LIB_OBJECT_LIB} - ${SOCI_BACKEND_LIB_SHARED_DEPS} - ) - - add_library(SOCI::shared::${SOCI_BACKEND_LIB_ALIAS_NAME} ALIAS ${SOCI_BACKEND_LIB_SHARED_TARGET_NAME}) - - target_link_libraries(soci_shared_interface INTERFACE SOCI::shared::${SOCI_BACKEND_LIB_ALIAS_NAME}) - endif() - - if (SOCI_STATIC) - add_library(${SOCI_BACKEND_LIB_STATIC_TARGET_NAME} STATIC "${SOCI_CXX_DUMMY_SOURCE}") - target_link_libraries(${SOCI_BACKEND_LIB_STATIC_TARGET_NAME} - PUBLIC - ${SOCI_BACKEND_LIB_OBJECT_LIB} - ${SOCI_BACKEND_LIB_STATIC_DEPS} - ) - - add_library(SOCI::static::${SOCI_BACKEND_LIB_ALIAS_NAME} ALIAS ${SOCI_BACKEND_LIB_STATIC_TARGET_NAME}) - - target_link_libraries(soci_static_interface INTERFACE SOCI::static::${SOCI_BACKEND_LIB_ALIAS_NAME}) - endif() -endfunction() - - foreach(CURRENT IN ITEMS "db2" "empty" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") string(TOUPPER "${CURRENT}" CURRENT_UPPER) diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index fe3ac0503..1cc4ad75c 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -12,34 +12,31 @@ if (NOT DB2_FOUND) return() endif() -add_library(soci_db2_interface INTERFACE) -target_link_libraries(soci_db2_interface INTERFACE DB2::DB2 soci_core_interface) - -add_library(soci_db2_objects OBJECT - "blob.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_db2 + ${SOCI_LIB_TYPE} + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_db2_objects PUBLIC soci_db2_interface) +target_link_libraries(soci_db2 + PUBLIC + DB2::DB2 + SOCI::Core +) -target_include_directories(soci_db2_objects +target_include_directories(soci_db2 PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_db2_objects - SHARED_TARGET_NAME soci_db2 - STATIC_TARGET_NAME soci_db2_static - ALIAS_NAME DB2 - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::DB2 ALIAS soci_db2) + +target_link_libraries(soci_interface INTERFACE SOCI::DB2) diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index daf0a5f21..b4780ad68 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -1,31 +1,25 @@ -add_library(soci_empty_interface INTERFACE) -target_link_libraries(soci_empty_interface INTERFACE soci_core_interface) -add_library(soci_empty_objects OBJECT - "blob.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_empty + ${SOCI_LIB_TYPE} + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_empty_objects PUBLIC soci_empty_interface) +target_link_libraries(soci_empty PUBLIC SOCI::Core) -target_include_directories(soci_empty_objects +target_include_directories(soci_empty PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_empty_objects - SHARED_TARGET_NAME soci_empty - STATIC_TARGET_NAME soci_empty_static - ALIAS_NAME Empty - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::Empty ALIAS soci_empty) + +target_link_libraries(soci_interface INTERFACE SOCI::Empty) diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index a7a93d04b..d70ab73af 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -18,35 +18,32 @@ if (NOT Firebird_FOUND) return() endif() -add_library(soci_firebird_interface INTERFACE) -target_link_libraries(soci_firebird_interface INTERFACE Firebird::Firebird soci_core_interface) - -add_library(soci_firebird_objects OBJECT - "blob.cpp" - "common.cpp" - "error-firebird.cpp" - "factory.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_firebird + ${SOCI_LIB_TYPE} + "blob.cpp" + "common.cpp" + "error-firebird.cpp" + "factory.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_firebird_objects PUBLIC Firebird::Firebird soci_firebird_interface) +target_link_libraries(soci_firebird + PUBLIC + Firebird::Firebird + SOCI::Core +) -target_include_directories(soci_firebird_objects +target_include_directories(soci_firebird PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_firebird_objects - SHARED_TARGET_NAME soci_firebird - STATIC_TARGET_NAME soci_firebird_static - ALIAS_NAME Firebird - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::Firebird ALIAS soci_firebird) + +target_link_libraries(soci_interface INTERFACE SOCI::Firebird) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 1aefac598..d1732c81d 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -12,35 +12,32 @@ if (NOT MySQL_FOUND) return() endif() -add_library(soci_mysql_interface INTERFACE) -target_link_libraries(soci_mysql_interface INTERFACE MySQL::MySQL soci_core_interface) - -add_library(soci_mysql_objects OBJECT - "blob.cpp" - "common.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_mysql + ${SOCI_LIB_TYPE} + "blob.cpp" + "common.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_mysql_objects PUBLIC soci_mysql_interface) +target_link_libraries(soci_mysql + PUBLIC + MySQL::MySQL + SOCI::Core +) -target_include_directories(soci_mysql_objects +target_include_directories(soci_mysql PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_mysql_objects - SHARED_TARGET_NAME soci_mysql - STATIC_TARGET_NAME soci_mysql_static - ALIAS_NAME MySQL - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::MySQL ALIAS soci_mysql) + +target_link_libraries(soci_interface INTERFACE SOCI::MySQL) diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index 7310f0347..be3d7d88f 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -12,34 +12,31 @@ if (NOT ODBC_FOUND) return() endif() -add_library(soci_odbc_interface INTERFACE) -target_link_libraries(soci_odbc_interface INTERFACE ODBC::ODBC soci_core_interface) - -add_library(soci_odbc_objects OBJECT - "blob.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_odbc + ${SOCI_LIB_TYPE} + "blob.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_odbc_objects PUBLIC soci_odbc_interface) +target_link_libraries(soci_odbc + PUBLIC + ODBC::ODBC + SOCI::Core +) -target_include_directories(soci_odbc_objects +target_include_directories(soci_odbc PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_odbc_objects - SHARED_TARGET_NAME soci_odbc - STATIC_TARGET_NAME soci_odbc_static - ALIAS_NAME ODBC - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::ODBC ALIAS soci_odbc) + +target_link_libraries(soci_interface INTERFACE SOCI::ODBC) diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 7dbd0c31e..f444eab10 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -12,35 +12,31 @@ if (NOT Oracle_FOUND) return() endif() -add_library(soci_oracle_interface INTERFACE) -target_link_libraries(soci_oracle_interface INTERFACE Oracle::Oracle soci_core_interface) - -add_library(soci_oracle_objects OBJECT - "blob.cpp" - "error.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_oracle + ${SOCI_LIB_TYPE} + "blob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" +) +target_link_libraries(soci_oracle + PUBLIC + Oracle::Oracle + SOCI::Core ) -target_link_libraries(soci_oracle_objects PUBLIC soci_oracle_interface) - -target_include_directories(soci_oracle_objects +target_include_directories(soci_oracle PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_oracle_objects - SHARED_TARGET_NAME soci_oracle - STATIC_TARGET_NAME soci_oracle_static - ALIAS_NAME Oracle - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::Oracle ALIAS soci_oracle) + +target_link_libraries(soci_interface INTERFACE SOCI::Oracle) diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index d927eca57..9589ff40d 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -20,39 +20,35 @@ if (POSTGRESQL_VERSION VERSION_LESS "9.3.0") set(SOCI_POSTGRESQL_NO_LO64 ON CACHE BOOL "Avoid using lo_xxx64() functions" FORCE) endif() -add_library(soci_postgresql_interface INTERFACE) -target_link_libraries(soci_postgresql_interface INTERFACE PostgreSQL::PostgreSQL soci_core_interface) - -add_library(soci_postgresql_objects OBJECT - "blob.cpp" - "error.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_postgresql + ${SOCI_LIB_TYPE} + "blob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_postgresql_objects PUBLIC soci_postgresql_interface) +target_link_libraries(soci_postgresql + PUBLIC + PostgreSQL::PostgreSQL + SOCI::Core +) -target_include_directories(soci_postgresql_objects +target_include_directories(soci_postgresql PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_postgresql_objects - SHARED_TARGET_NAME soci_postgresql - STATIC_TARGET_NAME soci_postgresql_static - ALIAS_NAME PostgreSQL - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) -if (SOCI_POSTGRESQL_NO_LO64) - target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) - target_compile_definitions(soci_postgresql_static INTERFACE SOCI_POSTGRESQL_NO_LO64) -endif() +target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) + +add_library(SOCI::PostgreSQL ALIAS soci_postgresql) + +target_link_libraries(soci_interface INTERFACE SOCI::PostgreSQL) + diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 31852f92d..e7d6ca1f0 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -12,35 +12,31 @@ if (NOT SQLite3_FOUND) return() endif() -add_library(soci_sqlite3_interface INTERFACE) -target_link_libraries(soci_sqlite3_interface INTERFACE SQLite::SQLite3 soci_core_interface) - -add_library(soci_sqlite3_objects OBJECT - "blob.cpp" - "error.cpp" - "factory.cpp" - "row-id.cpp" - "session.cpp" - "standard-into-type.cpp" - "standard-use-type.cpp" - "statement.cpp" - "vector-into-type.cpp" - "vector-use-type.cpp" +add_library(soci_sqlite3 + ${SOCI_LIB_TYPE} + "blob.cpp" + "error.cpp" + "factory.cpp" + "row-id.cpp" + "session.cpp" + "standard-into-type.cpp" + "standard-use-type.cpp" + "statement.cpp" + "vector-into-type.cpp" + "vector-use-type.cpp" ) -target_link_libraries(soci_sqlite3_objects PUBLIC soci_sqlite3_interface) +target_link_libraries(soci_sqlite3 + PUBLIC + SQLite::SQLite3 SOCI::Core +) -target_include_directories(soci_sqlite3_objects +target_include_directories(soci_sqlite3 PRIVATE "${PROJECT_SOURCE_DIR}/include/private" "${PROJECT_SOURCE_DIR}/include/soci" ) -soci_backend_objects_to_lib( - OBJECT_LIB soci_sqlite3_objects - SHARED_TARGET_NAME soci_sqlite3 - STATIC_TARGET_NAME soci_sqlite3_static - ALIAS_NAME SQLite3 - SHARED_DEPS SOCI::shared::Core - STATIC_DEPS SOCI::static::Core -) +add_library(SOCI::SQLite3 ALIAS soci_sqlite3) + +target_link_libraries(soci_interface INTERFACE SOCI::SQLite3) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d1aa7ff22..add3834a5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -14,74 +14,73 @@ check_cxx_source_compiles( # TODO: Actually populate this config file with something useful configure_file("${CMAKE_CURRENT_SOURCE_DIR}/soci-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/soci/soci-config.h") -add_library(soci_core_interface INTERFACE) -target_include_directories(soci_core_interface - INTERFACE +add_library(soci_core + ${SOCI_LIB_TYPE} + "backend-loader.cpp" + "blob.cpp" + "common.cpp" + "connection-parameters.cpp" + "connection-pool.cpp" + "error.cpp" + "into-type.cpp" + "logger.cpp" + "once-temp-type.cpp" + "prepare-temp-type.cpp" + "procedure.cpp" + "ref-counted-prepare-info.cpp" + "ref-counted-statement.cpp" + "row.cpp" + "rowid.cpp" + "session.cpp" + "soci-simple.cpp" + "statement.cpp" + "transaction.cpp" + "use-type.cpp" + "values.cpp" +) +add_library(SOCI::Core ALIAS soci_core) + +target_include_directories(soci_core + PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include" "${PROJECT_SOURCE_DIR}/include" + PRIVATE + "${PROJECT_SOURCE_DIR}/include/soci" + "${PROJECT_SOURCE_DIR}/include/private" ) -target_compile_definitions(soci_core_interface - INTERFACE - # Define the macro SOCI_DLL on Windows - $,SOCI_DLL,> -) -if (SOCI_VISIBILITY AND SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED) - target_compile_definitions(soci_core_interface - INTERFACE +if (SOCI_SHARED) + target_compile_definitions(soci_core + PUBLIC + # Define the macro SOCI_DLL on Windows + $,SOCI_DLL,> + ) +endif() + +if (SOCI_SHARED AND SOCI_VISIBILITY AND SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED) + target_compile_definitions(soci_core + PUBLIC SOCI_HAVE_VISIBILITY_SUPPORT ) endif() if (SOCI_BOOST) find_package(Boost REQUIRED) - target_link_libraries(soci_core_interface INTERFACE Boost::boost) - target_compile_definitions(soci_core_interface INTERFACE SOCI_HAVE_BOOST) + target_link_libraries(soci_core PUBLIC Boost::boost) + target_compile_definitions(soci_core PUBLIC SOCI_HAVE_BOOST) if (TARGET Boost::date_time) - target_link_libraries(soci_core_interface INTERFACE Boost::date_time) - target_compile_definitions(soci_core_interface INTERFACE SOCI_HAVE_BOOST_DATE_TIME) + target_link_libraries(soci_core PUBLIC Boost::date_time) + target_compile_definitions(soci_core PUBLIC SOCI_HAVE_BOOST_DATE_TIME) endif() endif() -add_library(soci_core_objects OBJECT - "backend-loader.cpp" - "blob.cpp" - "common.cpp" - "connection-parameters.cpp" - "connection-pool.cpp" - "error.cpp" - "into-type.cpp" - "logger.cpp" - "once-temp-type.cpp" - "prepare-temp-type.cpp" - "procedure.cpp" - "ref-counted-prepare-info.cpp" - "ref-counted-statement.cpp" - "row.cpp" - "rowid.cpp" - "session.cpp" - "soci-simple.cpp" - "statement.cpp" - "transaction.cpp" - "use-type.cpp" - "values.cpp" -) - -target_link_libraries(soci_core_objects - PUBLIC - soci_core_interface +target_link_libraries(soci_core PRIVATE Threads::Threads ) -target_include_directories(soci_core_objects - PRIVATE - "${PROJECT_SOURCE_DIR}/include/soci" - "${PROJECT_SOURCE_DIR}/include/private" -) - -target_compile_definitions(soci_core_objects +target_compile_definitions(soci_core PRIVATE DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" # TODO: Configure prefix and suffix properly @@ -89,15 +88,3 @@ target_compile_definitions(soci_core_objects SOCI_LIB_SUFFIX="" ) -if (SOCI_SHARED) - add_library(soci_core SHARED) - target_link_libraries(soci_core PUBLIC soci_core_objects) - add_library(SOCI::shared::Core ALIAS soci_core) - target_link_libraries(soci_shared_interface INTERFACE SOCI::shared::core) -endif() -if (SOCI_STATIC) - add_library(soci_core_static STATIC) - target_link_libraries(soci_core_static PUBLIC soci_core_objects) - add_library(SOCI::static::Core ALIAS soci_core_static) - target_link_libraries(soci_static_interface INTERFACE SOCI::static::core) -endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b7fe2e797..fcc4e6081 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,55 +1,11 @@ -include(soci_utils) - add_library(soci_common_tests STATIC common-tests.cpp) target_include_directories(soci_common_tests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" ) -target_link_libraries(soci_common_tests PUBLIC soci_core_interface) +target_link_libraries(soci_common_tests PUBLIC SOCI::Core) target_include_directories(soci_common_tests PUBLIC "${PROJECT_SOURCE_DIR}/include/private/") - -function(soci_make_tests) - set(FLAGS "") - set(ONE_VAL_OPTIONS "CONNECTION_STRING" "OBJECT_LIB" "SOCI_DEP_ALIAS" "SHARED_NAME" "STATIC_NAME") - set(MULTI_VAL_OPTIONS "") - cmake_parse_arguments(SOCI_MAKE_TESTS "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) - soci_verify_parsed_arguments( - PREFIX "SOCI_MAKE_TESTS" - FUNCTION_NAME "soci_make_tests" - REQUIRED "CONNECTION_STRING" "OBJECT_LIB" "SHARED_NAME" "STATIC_NAME" - ) - - set(CREATED_TESTS "") - - if (SOCI_SHARED) - add_executable(${SOCI_MAKE_TESTS_SHARED_NAME} "${SOCI_CXX_DUMMY_SOURCE}") - target_link_libraries(${SOCI_MAKE_TESTS_SHARED_NAME} PRIVATE ${SOCI_MAKE_TESTS_OBJECT_LIB}) - if (SOCI_MAKE_TESTS_SOCI_DEP_ALIAS) - target_link_libraries(${SOCI_MAKE_TESTS_SHARED_NAME} PRIVATE SOCI::shared::${SOCI_MAKE_TESTS_SOCI_DEP_ALIAS}) - endif() - - list(APPEND CREATED_TESTS "${SOCI_MAKE_TESTS_SHARED_NAME}") - endif() - - if (SOCI_STATIC) - add_executable(${SOCI_MAKE_TESTS_STATIC_NAME} "${SOCI_CXX_DUMMY_SOURCE}") - target_link_libraries(${SOCI_MAKE_TESTS_STATIC_NAME} PRIVATE ${SOCI_MAKE_TESTS_OBJECT_LIB}) - if (SOCI_MAKE_TESTS_SOCI_DEP_ALIAS) - target_link_libraries(${SOCI_MAKE_TESTS_STATIC_NAME} PRIVATE SOCI::static::${SOCI_MAKE_TESTS_SOCI_DEP_ALIAS}) - endif() - - list(APPEND CREATED_TESTS "${SOCI_MAKE_TESTS_STATIC_NAME}") - endif() - - foreach (CURRENT IN LISTS CREATED_TESTS) - add_test( - NAME "${CURRENT}" - COMMAND "${CURRENT}" "${SOCI_MAKE_TESTS_CONNECTION_STRING}" "--invisibles" - ) - endforeach() -endfunction() - foreach (CURRENT_BACKEND IN ITEMS "db2" "empty" "firebird" "mysql" "odbc" "oracle" "postgresql" "sqlite3") string(TOUPPER "${CURRENT_BACKEND}" CURRENT_BACKEND_UPPER) diff --git a/tests/db2/CMakeLists.txt b/tests/db2/CMakeLists.txt index a6e1e9af1..63839ceba 100644 --- a/tests/db2/CMakeLists.txt +++ b/tests/db2/CMakeLists.txt @@ -1,12 +1,9 @@ -add_library(db2_tests OBJECT db2_tests.cpp) -target_link_libraries(db2_tests PUBLIC soci_common_tests soci_db2_interface) +add_executable(soci_db2_tests OBJECT db2_tests.cpp) +target_link_libraries(soci_db2_tests PRIVATE soci_common_tests soci_db2_interface) set(SOCI_DB2_TEST_CONNSTR "DSN=SAMPLE;Uid=db2inst1;Pwd=db2inst1;autocommit=off" CACHE STRING "The connection string to use for DB2 tests") -soci_make_tests( - OBJECT_LIB db2_tests - CONNECTION_STRING "${SOCI_DB2_TEST_CONNSTR}" - SHARED_NAME "soci_db2_test" - STATIC_NAME "soci_db2_test_static" - SOCI_DEP_ALIAS "DB2" +add_test( + NAME soci_db2_tests + COMMAND soci_db2_tests "${SOCI_DB2_TEST_CONNSTR}" "--invisibles" ) diff --git a/tests/empty/CMakeLists.txt b/tests/empty/CMakeLists.txt index 30d391ed6..2ce04d63d 100644 --- a/tests/empty/CMakeLists.txt +++ b/tests/empty/CMakeLists.txt @@ -1,11 +1,8 @@ -add_library(empty_tests OBJECT empty_tests.cpp) -target_link_libraries(empty_tests PUBLIC soci_empty_interface) -target_include_directories(empty_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") +add_executable(soci_empty_tests empty_tests.cpp) +target_link_libraries(soci_empty_tests PRIVATE SOCI::Empty) +target_include_directories(soci_empty_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") -soci_make_tests( - OBJECT_LIB empty_tests - CONNECTION_STRING "dummy" - SHARED_NAME "soci_empty_test" - STATIC_NAME "soci_empty_test_static" - SOCI_DEP_ALIAS "Empty" +add_test( + NAME soci_empty_tests + COMMAND soci_empty_tests "--invisibles" ) diff --git a/tests/firebird/CMakeLists.txt b/tests/firebird/CMakeLists.txt index eba55c027..275c3cf17 100644 --- a/tests/firebird/CMakeLists.txt +++ b/tests/firebird/CMakeLists.txt @@ -1,12 +1,9 @@ -add_library(firebird_tests OBJECT firebird_tests.cpp) -target_link_libraries(firebird_tests PUBLIC soci_common_tests soci_firebird_interface) +add_executable(soci_firebird_tests firebird_tests.cpp) +target_link_libraries(soci_firebird_tests PRIVATE soci_common_tests SOCI::Firebird) set(SOCI_FIREBIRD_TEST_CONNSTR "service=/tmp/soci_test.fdb user=SYSDBA password=masterkey" CACHE STRING "The connection string to use for Firebird tests") -soci_make_tests( - OBJECT_LIB firebird_tests - CONNECTION_STRING "${SOCI_FIREBIRD_TEST_CONNSTR}" - SHARED_NAME "soci_firebird_test" - STATIC_NAME "soci_firebird_test_static" - SOCI_DEP_ALIAS "Firebird" +add_test( + NAME soci_firebird_tests + COMMAND soci_firebird_tests "${SOCI_FIREBIRD_TEST_CONNSTR}" "--invisibles" ) diff --git a/tests/mysql/CMakeLists.txt b/tests/mysql/CMakeLists.txt index 0022c6886..4081ba080 100644 --- a/tests/mysql/CMakeLists.txt +++ b/tests/mysql/CMakeLists.txt @@ -1,12 +1,9 @@ -add_library(mysql_tests OBJECT mysql_tests.cpp) -target_link_libraries(mysql_tests PUBLIC soci_common_tests soci_mysql_interface) +add_executable(soci_mysql_tests mysql_tests.cpp) +target_link_libraries(soci_mysql_tests PRIVATE soci_common_tests SOCI::MySQL) set(SOCI_MYSQL_TEST_CONNSTR "db=soci_test" CACHE STRING "The connection string to use for MySQL tests") -soci_make_tests( - OBJECT_LIB mysql_tests - CONNECTION_STRING "${SOCI_MYSQL_TEST_CONNSTR}" - SHARED_NAME "soci_mysql_test" - STATIC_NAME "soci_mysql_test_static" - SOCI_DEP_ALIAS "MySQL" +add_test( + NAME soci_mysql_tests + COMMAND soci_mysql_tests "${SOCI_MYSQL_TEST_CONNSTR}" "--invisibles" ) diff --git a/tests/odbc/CMakeLists.txt b/tests/odbc/CMakeLists.txt index 282082b2f..30a2ac6b4 100644 --- a/tests/odbc/CMakeLists.txt +++ b/tests/odbc/CMakeLists.txt @@ -7,47 +7,38 @@ configure_file("test-mssql.dsn.in" "${CMAKE_CURRENT_BINARY_DIR}/test-mssql.dsn" if (WIN32) - add_library(odbc_ms_access_tests OBJECT odbc_ms_access_tests.cpp) - target_link_libraries(odbc_ms_access_tests PUBLIC soci_common_tests soci_odbc_interface) + add_executable(soci_odbc_ms_access_tests odbc_ms_access_tests.cpp) + target_link_libraries(soci_odbc_ms_access_tests PRIVATE soci_common_tests SOCI::ODBC) set(SOCI_ODBC_TEST_ACCESS_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MS Access test") - soci_make_tests( - OBJECT_LIB odbc_ms_access_tests - CONNECTION_STRING "${SOCI_ODBC_TEST_ACCESS_CONNSTR}" - SHARED_NAME "soci_odbc_ms_access_test" - STATIC_NAME "soci_odbc_ms_access_test_static" - SOCI_DEP_ALIAS "ODBC" + add_test( + NAME soci_odbc_ms_access_tests + COMMAND soci_odbc_ms_access_tests "${SOCI_ODBC_TEST_ACCESS_CONNSTR}" "--invisibles" ) endif() -add_library(odbc_mssql_tests OBJECT odbc_mssql_tests.cpp) -target_link_libraries(odbc_mssql_tests PUBLIC soci_common_tests soci_odbc_interface) +add_executable(soci_odbc_mssql_tests odbc_mssql_tests.cpp) +target_link_libraries(soci_odbc_mssql_tests PRIVATE soci_common_tests SOCI::ODBC) set(SOCI_ODBC_TEST_MSSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MSSQL test") -soci_make_tests( - OBJECT_LIB odbc_mssql_tests - CONNECTION_STRING "${SOCI_ODBC_TEST_MSSQL_CONNSTR}" - SHARED_NAME "soci_odbc_mssql_test" - STATIC_NAME "soci_odbc_mssql_test_static" - SOCI_DEP_ALIAS "ODBC" +add_test( + NAME soci_odbc_mssql_tests + COMMAND soci_odbc_mssql_tests "${SOCI_ODBC_TEST_MSSQL_CONNSTR}" "--invisibles" ) -add_library(odbc_mysql_tests OBJECT odbc_mysql_tests.cpp) -target_link_libraries(odbc_mysql_tests PUBLIC soci_common_tests soci_odbc_interface) -target_include_directories(odbc_mysql_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") +add_executable(soci_odbc_mysql_tests odbc_mysql_tests.cpp) +target_link_libraries(soci_odbc_mysql_tests PRIVATE soci_common_tests SOCI::ODBC) +target_include_directories(soci_odbc_mysql_tests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../") set(SOCI_ODBC_TEST_MYSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-mysql.dsn" CACHE STRING "Connection string for the ODBC MySQL test") -soci_make_tests( - OBJECT_LIB odbc_mysql_tests - CONNECTION_STRING "${SOCI_ODBC_TEST_MYSQL_CONNSTR}" - SHARED_NAME "soci_odbc_mysql_test" - STATIC_NAME "soci_odbc_mysql_test_static" - SOCI_DEP_ALIAS "ODBC" +add_test( + NAME soci_odbc_mysql_tests + COMMAND soci_odbc_mysql_tests "${SOCI_ODBC_TEST_MYSQL_CONNSTR}" "--invisibles" ) @@ -57,34 +48,29 @@ else() set(TEST_PGSQL_DSN "test-postgresql.dsn") endif() -add_library(odbc_postgresql_tests OBJECT odbc_postgresql_tests.cpp) -target_link_libraries(odbc_postgresql_tests PUBLIC soci_common_tests soci_odbc_interface) +add_executable(soci_odbc_postgresql_tests odbc_postgresql_tests.cpp) +target_link_libraries(soci_odbc_postgresql_tests PRIVATE soci_common_tests SOCI::ODBC) set(SOCI_ODBC_TEST_POSTGRESQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/${TEST_PGSQL_DSN}" CACHE STRING "Connection string for the ODBC PostgreSQL test") -soci_make_tests( - OBJECT_LIB odbc_postgresql_tests - CONNECTION_STRING "${SOCI_ODBC_TEST_POSTGRESQL_CONNSTR}" - SHARED_NAME "soci_odbc_postgresql_test" - STATIC_NAME "soci_odbc_postgresql_test_static" - SOCI_DEP_ALIAS "ODBC" +add_test( + NAME soci_odbc_postgresql_tests + COMMAND soci_odbc_postgresql_tests "${SOCI_ODBC_TEST_POSTGRESQL_CONNSTR}" "--invisibles" ) + # 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}) option(WITH_ODBC_TEST_DB2 "Build ODBC DB2 test" OFF) if (WITH_ODBC_TEST_DB2) - add_library(odbc_db2_tests OBJECT odbc_db2_tests.cpp) - target_link_libraries(odbc_db2_tests PUBLIC soci_common_tests soci_odbc_interface) + add_executable(soci_odbc_db2_tests odbc_db2_tests.cpp) + target_link_libraries(osoci_dbc_db2_tests PRIVATE soci_common_tests SOCI::ODBC) set(SOCI_ODBC_TEST_DB2_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-db2.dsn" CACHE STRING "Connection string for the ODBC DB2 test") - soci_make_tests( - OBJECT_LIB odbc_db2_tests - CONNECTION_STRING "${SOCI_ODBC_TEST_DB2_CONNSTR}" - SHARED_NAME "soci_odbc_db2_test" - STATIC_NAME "soci_odbc_db2_test_static" - SOCI_DEP_ALIAS "ODBC" + add_test( + NAME soci_odbc_db2_tests + COMMAND soci_odbc_db2_tests "${SOCI_ODBC_TEST_DB2_CONNSTR}" "--invisibles" ) endif() diff --git a/tests/oracle/CMakeLists.txt b/tests/oracle/CMakeLists.txt index 49f6ec0da..22173e688 100644 --- a/tests/oracle/CMakeLists.txt +++ b/tests/oracle/CMakeLists.txt @@ -1,12 +1,9 @@ -add_library(oracle_tests OBJECT oracle_tests.cpp) -target_link_libraries(oracle_tests PUBLIC soci_common_tests soci_oracle_interface) +add_executable(soci_oracle_tests oracle_tests.cpp) +target_link_libraries(soci_oracle_tests PRIVATE soci_common_tests SOCI::Oracle) set(SOCI_ORACLE_TEST_CONNSTR "service=orcl user=scott password=tiger" CACHE STRING "The connection string to use for Oracle tests") -soci_make_tests( - OBJECT_LIB oracle_tests - CONNECTION_STRING "${SOCI_ORACLE_TEST_CONNSTR}" - SHARED_NAME "soci_oracle_test" - STATIC_NAME "soci_oracle_test_static" - SOCI_DEP_ALIAS "Oracle" +add_test( + NAME soci_oracle_tests + COMMAND soci_oracle_tests "${SOCI_ORACLE_TEST_CONNSTR}" "--invisibles" ) diff --git a/tests/postgresql/CMakeLists.txt b/tests/postgresql/CMakeLists.txt index 89cd142b6..f270731aa 100644 --- a/tests/postgresql/CMakeLists.txt +++ b/tests/postgresql/CMakeLists.txt @@ -1,12 +1,9 @@ -add_library(postgresql_tests OBJECT postgresql_tests.cpp) -target_link_libraries(postgresql_tests PUBLIC soci_common_tests soci_postgresql_interface) +add_executable(soci_postgresql_tests postgresql_tests.cpp) +target_link_libraries(soci_postgresql_tests PRIVATE soci_common_tests SOCI::PostgreSQL) set(SOCI_POSTGRESQL_TEST_CONNSTR "dbname=soci_test" CACHE STRING "The connection string to use for PostgreSQL tests") -soci_make_tests( - OBJECT_LIB postgresql_tests - CONNECTION_STRING "${SOCI_POSTGRESQL_TEST_CONNSTR}" - SHARED_NAME "soci_postgresql_test" - STATIC_NAME "soci_postgresql_test_static" - SOCI_DEP_ALIAS "PostgreSQL" +add_test( + NAME soci_postgresql_tests + COMMAND soci_postgresql_tests "${SOCI_POSTGRESQL_TEST_CONNSTR}" "--invisibles" ) diff --git a/tests/sqlite3/CMakeLists.txt b/tests/sqlite3/CMakeLists.txt index 16b253d2a..0bfdde594 100644 --- a/tests/sqlite3/CMakeLists.txt +++ b/tests/sqlite3/CMakeLists.txt @@ -1,13 +1,9 @@ -add_library(sqlite_tests OBJECT sqlite3_tests.cpp) -target_link_libraries(sqlite_tests PUBLIC soci_common_tests soci_sqlite3_interface) +add_executable(soci_sqlite3_tests sqlite3_tests.cpp) +target_link_libraries(soci_sqlite3_tests PRIVATE soci_common_tests SOCI::SQLite3) set(SOCI_SQLITE3_TEST_CONNSTR ":memory:" CACHE STRING "The connection string to use for SQLite3 tests") -soci_make_tests( - OBJECT_LIB sqlite_tests - CONNECTION_STRING "${SOCI_SQLITE3_TEST_CONNSTR}" - SHARED_NAME "soci_sqlite3_test" - STATIC_NAME "soci_sqlite3_test_static" - SOCI_DEP_ALIAS "SQLite3" +add_test( + NAME soci_sqlite3_tests + COMMAND soci_sqlite3_tests "${SOCI_SQLITE3_TEST_CONNSTR}" "--invisibles" ) - From cd401e4ba6852ed81a3929709536f4641cdd2004 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 17:57:39 +0100 Subject: [PATCH 19/79] Add missing include --- include/soci/callbacks.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/soci/callbacks.h b/include/soci/callbacks.h index 85825ad1b..ca2c2ac0d 100644 --- a/include/soci/callbacks.h +++ b/include/soci/callbacks.h @@ -8,6 +8,8 @@ #ifndef SOCI_CALLBACKS_H_INCLUDED #define SOCI_CALLBACKS_H_INCLUDED +#include "soci/soci-platform.h" + namespace soci { From 15645401efd80b373ca65a800ce6f50c232db71d Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:12:05 +0100 Subject: [PATCH 20/79] Fixed Windows build --- include/soci/callbacks.h | 14 +++++++++----- src/core/CMakeLists.txt | 1 + src/core/callbacks.cpp | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 src/core/callbacks.cpp diff --git a/include/soci/callbacks.h b/include/soci/callbacks.h index ca2c2ac0d..e08c24dee 100644 --- a/include/soci/callbacks.h +++ b/include/soci/callbacks.h @@ -10,6 +10,8 @@ #include "soci/soci-platform.h" +#include + namespace soci { @@ -21,26 +23,28 @@ class session; class SOCI_DECL failover_callback { public: + failover_callback() = default; + virtual ~failover_callback(); // Called when the failover operation has started, // after discovering connectivity problems. - virtual void started() {} + virtual void started(); // Called after successful failover and creating a new connection; // the sql parameter denotes the new connection and allows the user // to replay any initial sequence of commands (like session configuration). - virtual void finished(session & /* sql */) {} + virtual void finished(session& /* sql */); // Called when the attempt to reconnect failed, // if the user code sets the retry parameter to true, // then new connection will be attempted; // the newTarget connection string is a hint that can be ignored // by external means. - virtual void failed(bool & /* out */ /* retry */, - std::string & /* out */ /* newTarget */) {} + virtual void failed(bool& /* out */ /* retry */, + std::string& /* out */ /* newTarget */); // Called when there was a failure that prevents further failover attempts. - virtual void aborted() {} + virtual void aborted(); }; } // namespace soci diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index add3834a5..53fa1e4c1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(soci_core "backend-loader.cpp" "blob.cpp" "common.cpp" + "callbacks.cpp" "connection-parameters.cpp" "connection-pool.cpp" "error.cpp" diff --git a/src/core/callbacks.cpp b/src/core/callbacks.cpp new file mode 100644 index 000000000..8ba841984 --- /dev/null +++ b/src/core/callbacks.cpp @@ -0,0 +1,16 @@ +#define SOCI_SOURCE + +#include "soci/callbacks.h" +#include "soci/soci-platform.h" + +using namespace soci; + +failover_callback::~failover_callback() {} + +void failover_callback::started() {} + +void failover_callback::finished(session& /* sql */) {} + +void failover_callback::failed(bool& /* out */ /* retry */, std::string& /* out */ /* newTarget */) {} + +void failover_callback:: aborted() {} From 342bcb4388ee44abd88f631b54e035c19e0fdd17 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:22:22 +0100 Subject: [PATCH 21/79] Don't overwrite cxx standard --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f22899c..c3244e662 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,9 @@ project(SOCI LANGUAGES CXX ) -set(CMAKE_CXX_STANDARD 14) +if (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED OR CMAKE_CXX_STANDARD LESS 14) + set(CMAKE_CXX_STANDARD 14) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) From 646f2feee34e622daeab9faef3806a726e1c9b07 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:22:42 +0100 Subject: [PATCH 22/79] Enable unity builds on CI --- scripts/ci/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/common.sh b/scripts/ci/common.sh index c29fdd658..e2857216a 100644 --- a/scripts/ci/common.sh +++ b/scripts/ci/common.sh @@ -51,8 +51,8 @@ SOCI_COMMON_CMAKE_OPTIONS=' -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=ON -DSOCI_ENABLE_WERROR=ON - -DSOCI_STATIC=OFF -DSOCI_TESTS=ON + -DCMAKE_UNITY_BUILD=ON ' if [ -n "${SOCI_CXXSTD}" ]; then From 921ca59237490c01ce936a345f7f81897ccd69ef Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:25:33 +0100 Subject: [PATCH 23/79] Specify config for ctest on Windows --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 3b4cb4cae..4daea512f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -113,7 +113,7 @@ build_script: - cmake --build . --config %CONFIGURATION% -- %BUILD_TOOL_OPTIONS% test_script: - - ctest -V --timeout 300 --output-on-failure -R "soci_empty|soci_postgresql|soci_sqlite3|soci_odbc_test_mssql|soci_mysql%SOCI_ODBC_TESTS%" + - ctest -V --timeout 300 --output-on-failure --build-config %CONFIGURATION% # Uncomment this to wait for RDP connection after the build end. #on_finish: From 1353f18dc6571651c9d6603cb20ecd6e9ec4f28c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:44:08 +0100 Subject: [PATCH 24/79] Fix DSN file paths for ODBC tests --- tests/odbc/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/odbc/CMakeLists.txt b/tests/odbc/CMakeLists.txt index 30a2ac6b4..eb8be9375 100644 --- a/tests/odbc/CMakeLists.txt +++ b/tests/odbc/CMakeLists.txt @@ -10,7 +10,7 @@ if (WIN32) add_executable(soci_odbc_ms_access_tests odbc_ms_access_tests.cpp) target_link_libraries(soci_odbc_ms_access_tests PRIVATE soci_common_tests SOCI::ODBC) - set(SOCI_ODBC_TEST_ACCESS_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MS Access test") + set(SOCI_ODBC_TEST_ACCESS_CONNSTR "FILEDSN=${CMAKE_CURRENT_SOURCE_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MS Access test") add_test( NAME soci_odbc_ms_access_tests @@ -22,7 +22,7 @@ endif() add_executable(soci_odbc_mssql_tests odbc_mssql_tests.cpp) target_link_libraries(soci_odbc_mssql_tests PRIVATE soci_common_tests SOCI::ODBC) -set(SOCI_ODBC_TEST_MSSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-access.dsn" CACHE STRING "Connection string for the ODBC MSSQL test") +set(SOCI_ODBC_TEST_MSSQL_CONNSTR "FILEDSN=${CMAKE_CURRENT_BINARY_DIR}/test-mssql.dsn" CACHE STRING "Connection string for the ODBC MSSQL test") add_test( NAME soci_odbc_mssql_tests From eaf2994e77435021dba9dfa6e17e8ae9f8dcf4d7 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:47:26 +0100 Subject: [PATCH 25/79] Select ODBC tests based on available drivers --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4daea512f..f1c49676e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -59,11 +59,11 @@ install: Import-Module C:\projects\soci\scripts\windows\Get-ODBCList.ps1 $drivers_list = Get-ODBCList Write-Output $drivers_list - if ($drivers_list -clike '*MySQL*') { - $env:SOCI_ODBC_TESTS = $env:SOCI_ODBC_TESTS + '|soci_odbc_test_mysql' + if (-Not $drivers_list -clike '*MySQL*') { + $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_mysql_tests' } - if ($drivers_list -clike '*PostgreSQL*') { - $env:SOCI_ODBC_TESTS = $env:SOCI_ODBC_TESTS + '|soci_odbc_test_postgresql' + if (-Not $drivers_list -clike '*PostgreSQL*') { + $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_postgresql_tests' } - git clone https://github.com/snikulov/sqlite.cmake.build.git C:\projects\sqlite\src @@ -113,7 +113,7 @@ build_script: - cmake --build . --config %CONFIGURATION% -- %BUILD_TOOL_OPTIONS% test_script: - - ctest -V --timeout 300 --output-on-failure --build-config %CONFIGURATION% + - ctest -V --timeout 300 --output-on-failure --build-config %CONFIGURATION% --exclude-regex "%SOCI_ODBC_SKIP_TESTS%" # Uncomment this to wait for RDP connection after the build end. #on_finish: From 35407800d76e5d034157a09dfd92a213f4c3d4d1 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 18:53:36 +0100 Subject: [PATCH 26/79] Use unity builds also on Windows CI --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f1c49676e..1f85c2895 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -102,7 +102,7 @@ before_build: - set USER=root - mysql -e "create database soci_test;" --user=root - sqlcmd -U sa -P Password12! -S (local)\SQL%MSSQL_VER% -i C:\projects\soci\scripts\windows\mssql_db_create.sql - - cmake .. -G"%G%" -DSQLITE_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=%SQLITE_ROOT% + - cmake .. -G"%G%" -DSQLITE_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=%SQLITE_ROOT% -DCMAKE_UNITY_BUILD=ON - cmake --build . --config %CONFIGURATION% --target install build_script: From 980d4c9cb14c3b5f4abe2b613a730e198ef3ec94 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 19:01:51 +0100 Subject: [PATCH 27/79] Also skip MS Access ODBC test --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 1f85c2895..62af5137d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -59,12 +59,14 @@ install: Import-Module C:\projects\soci\scripts\windows\Get-ODBCList.ps1 $drivers_list = Get-ODBCList Write-Output $drivers_list + $env:SOCI_ODBC_SKIP_TESTS = "soci_odbc_ms_access_tests" if (-Not $drivers_list -clike '*MySQL*') { $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_mysql_tests' } if (-Not $drivers_list -clike '*PostgreSQL*') { $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_postgresql_tests' } + Write-Host "To be skipped ODBC tests: %SOCI_ODBC_SKIP_TESTS%" - git clone https://github.com/snikulov/sqlite.cmake.build.git C:\projects\sqlite\src before_build: From 5214951c0ab5a7ed4e4f685e0dbca45a8265a2f0 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 19:14:09 +0100 Subject: [PATCH 28/79] Fix print statement --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 62af5137d..2ca30648b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -66,7 +66,7 @@ install: if (-Not $drivers_list -clike '*PostgreSQL*') { $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_postgresql_tests' } - Write-Host "To be skipped ODBC tests: %SOCI_ODBC_SKIP_TESTS%" + Write-Output "To be skipped ODBC tests: $env:SOCI_ODBC_SKIP_TESTS" - git clone https://github.com/snikulov/sqlite.cmake.build.git C:\projects\sqlite\src before_build: From 501be49ce2369dc23b7afe367a4bd0ca5c985b1a Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Feb 2024 19:23:14 +0100 Subject: [PATCH 29/79] Fix driver detection --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 2ca30648b..db79deff4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -60,10 +60,10 @@ install: $drivers_list = Get-ODBCList Write-Output $drivers_list $env:SOCI_ODBC_SKIP_TESTS = "soci_odbc_ms_access_tests" - if (-Not $drivers_list -clike '*MySQL*') { + if (-Not ($drivers_list -contains 'MySQL')) { $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_mysql_tests' } - if (-Not $drivers_list -clike '*PostgreSQL*') { + if (-Not ($drivers_list -contains 'PostgreSQL')) { $env:SOCI_ODBC_SKIP_TESTS = $env:SOCI_ODBC_SKIP_TESTS + '|soci_odbc_postgresql_tests' } Write-Output "To be skipped ODBC tests: $env:SOCI_ODBC_SKIP_TESTS" From e96633a38fd147cf10769b1b6a9fe256efde0cfb Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Feb 2024 20:02:58 +0100 Subject: [PATCH 30/79] Properly generate soci-config.h --- src/CMakeLists.txt | 80 +++++++++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 29 +++----------- src/core/soci-config.h.in | 16 -------- 3 files changed, 85 insertions(+), 40 deletions(-) delete mode 100644 src/core/soci-config.h.in diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 252bf4d92..8d8883bd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,85 @@ add_library(soci_interface INTERFACE) add_library(SOCI::soci ALIAS soci_interface) +set(SOCI_GENERATED_INCLUDES_DIR "${CMAKE_BINARY_DIR}/include") +file(MAKE_DIRECTORY "${SOCI_GENERATED_INCLUDES_DIR}/soci") + add_subdirectory(core) add_subdirectory(backends) + + +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + " + __attribute__ (( visibility(\"default\") )) int f1() { return 0; } + __attribute__ (( visibility(\"hidden\") )) int f2() { return 1; } + + int main(int argc, char* argv[]) { f1(); f2(); return 0; } + " + SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED +) + + +# Generate the soci-config.h file + +set(CONFIG_LINES "#ifndef SOCI_SOCICONFIG_H_INCLUDED" "#define SOCI_SOCICONFIG_H_INCLUDED" "") + +set(CONFIG_VARS + SOCI_HAVE_BOOST + SOCI_HAVE_BOOST_DATE_TIME + SOCI_EMPTY + SOCI_DB2 + SOCI_FIREBIRD + SOCI_MYSQL + SOCI_ODBC + SOCI_ORACLE + SOCI_POSTGRESQL + SOCI_SQLITE3 + SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED +) +set(CONFIG_MACROS + SOCI_HAVE_BOOST + SOCI_HAVE_BOOST_DATE_TIME + SOCI_HAVE_EMPTY + SOCI_HAVE_DB2 + SOCI_HAVE_FIREBIRD + SOCI_HAVE_MYSQL + SOCI_HAVE_ODBC + SOCI_HAVE_ORACLE + SOCI_HAVE_POSTGRESQL + SOCI_HAVE_SQLITE3 + SOCI_HAVE_VISIBILITY_SUPPORT +) + +list(LENGTH CONFIG_MACROS N_CONFIGS) +math(EXPR LAST_CONFIG_IDX "${N_CONFIGS} - 1") + +foreach (I RANGE ${LAST_CONFIG_IDX}) + list(GET CONFIG_VARS ${I} CURRENT_VAR) + list(GET CONFIG_MACROS ${I} CURRENT_MACRO) + + if (${CURRENT_VAR}) + list(APPEND CONFIG_LINES "#define ${CURRENT_MACRO}") + else() + list(APPEND CONFIG_LINES "/* #define ${CURRENT_MACRO} */") + endif() + list(APPEND CONFIG_LINES "") +endforeach() + +list(APPEND CONFIG_LINES "#endif") + +list(JOIN CONFIG_LINES "\n" CONFIG_CONTENT) + +string(MD5 CONFIG_CONTENT_HASH "${CONFIG_CONTENT}") +if (EXISTS "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h") + file(MD5 "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h" CONFIG_FILE_HASH) +else() + set(CONFIG_FILE_HASH 0) +endif() + +# Only overwrite the soci-config.h file if the generated content is different from the +# file's content in order to avoid needless rebuilding +message(STATUS "${CONFIG_CONTENT_HASH} == ${CONFIG_FILE_HASH} ?") +if (NOT (CONFIG_CONTENT_HASH STREQUAL CONFIG_FILE_HASH)) + file(WRITE "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h" "${CONFIG_CONTENT}") +endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 53fa1e4c1..e53e48c73 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,19 +1,5 @@ find_package(Threads REQUIRED) -include(CheckCXXSourceCompiles) -check_cxx_source_compiles( - " - __attribute__ (( visibility(\"default\") )) int f1() { return 0; } - __attribute__ (( visibility(\"hidden\") )) int f2() { return 1; } - - int main(int argc, char* argv[]) { f1(); f2(); return 0; } - " - SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED -) - -# TODO: Actually populate this config file with something useful -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/soci-config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/soci/soci-config.h") - add_library(soci_core ${SOCI_LIB_TYPE} "backend-loader.cpp" @@ -43,7 +29,7 @@ add_library(SOCI::Core ALIAS soci_core) target_include_directories(soci_core PUBLIC - "${CMAKE_CURRENT_BINARY_DIR}/include" + "${SOCI_GENERATED_INCLUDES_DIR}" "${PROJECT_SOURCE_DIR}/include" PRIVATE "${PROJECT_SOURCE_DIR}/include/soci" @@ -58,21 +44,16 @@ if (SOCI_SHARED) ) endif() -if (SOCI_SHARED AND SOCI_VISIBILITY AND SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED) - target_compile_definitions(soci_core - PUBLIC - SOCI_HAVE_VISIBILITY_SUPPORT - ) -endif() - if (SOCI_BOOST) find_package(Boost REQUIRED) target_link_libraries(soci_core PUBLIC Boost::boost) - target_compile_definitions(soci_core PUBLIC SOCI_HAVE_BOOST) + set(SOCI_HAVE_BOOST TRUE CACHE INTERNAL "" FORCE) + + find_package(Boost COMPONENTS date_time) if (TARGET Boost::date_time) target_link_libraries(soci_core PUBLIC Boost::date_time) - target_compile_definitions(soci_core PUBLIC SOCI_HAVE_BOOST_DATE_TIME) + set(SOCI_HAVE_BOOST_DATE_TIME TRUE CACHE INTERNAL "" FORCE) endif() endif() diff --git a/src/core/soci-config.h.in b/src/core/soci-config.h.in deleted file mode 100644 index 77fff775d..000000000 --- a/src/core/soci-config.h.in +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (C) 2004-2008 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 SOCICONFIG_H_INCLUDED -#define SOCICONFIG_H_INCLUDED - -// -// SOCI has been build with support for: -// -@CONFIGURED_VARIABLES@ - -#endif // SOCICONFIG_H_INCLUDED From ec3984ede818d7cadc5d3eb907dfa56aad6d5cf3 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 27 Feb 2024 18:41:46 +0100 Subject: [PATCH 31/79] Install recent cmake version on AppVeyor CI --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index db79deff4..b2b0cf831 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,6 +68,9 @@ install: } Write-Output "To be skipped ODBC tests: $env:SOCI_ODBC_SKIP_TESTS" - git clone https://github.com/snikulov/sqlite.cmake.build.git C:\projects\sqlite\src + # Ensure we have a recent cmake version available + - choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' + - refreshenv before_build: - set SQLITE_ROOT=C:\projects\sqlite\sqlite From f5ab470f805d4e1bdb235bbb9230eca6379714e9 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:00:56 +0100 Subject: [PATCH 32/79] Remove unneeded debug message --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8d8883bd9..b0eb2a029 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -79,7 +79,6 @@ endif() # Only overwrite the soci-config.h file if the generated content is different from the # file's content in order to avoid needless rebuilding -message(STATUS "${CONFIG_CONTENT_HASH} == ${CONFIG_FILE_HASH} ?") if (NOT (CONFIG_CONTENT_HASH STREQUAL CONFIG_FILE_HASH)) file(WRITE "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h" "${CONFIG_CONTENT}") endif() From 241af7dcf124620f78a3dc737cb6b358505d5f07 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:02:06 +0100 Subject: [PATCH 33/79] Configure compiler options --- CMakeLists.txt | 9 ++++ cmake/soci_compiler_options.cmake | 78 +++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 7 ++- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 cmake/soci_compiler_options.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c3244e662..b111d62f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,15 @@ else() set(SOCI_LIB_TYPE "STATIC") endif() + +set(SOCI_LIB_PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}soci_" CACHE STRING "Specifies prefix for the lib directory") +set(SOCI_LIB_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE STRING "Specifies suffix for the lib directory") +set(SOCI_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}" CACHE STRING "Specifies suffix for the library file in debug mode") + + +include(soci_compiler_options) + + add_subdirectory(src) if (SOCI_TESTS) diff --git a/cmake/soci_compiler_options.cmake b/cmake/soci_compiler_options.cmake new file mode 100644 index 000000000..77f7f5af2 --- /dev/null +++ b/cmake/soci_compiler_options.cmake @@ -0,0 +1,78 @@ + + +add_library(soci_compiler_interface INTERFACE) + +# +# Force compilation flags and set desired warnings level +# + +option(SOCI_ENABLE_WERROR "Enables turning compiler warnings into errors" OFF) + +if (MSVC) + target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_DEPRECATE) + target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_WARNINGS) + target_compile_definitions(soci_compiler_interface INTERFACE _CRT_NONSTDC_NO_WARNING) + target_compile_definitions(soci_compiler_interface INTERFACE _SCL_SECURE_NO_WARNINGS) + + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /we4266") + endif() + + if (SOCI_ENABLE_WERROR) + target_compile_options(soci_compiler_interface INTERFACE "/WX") + endif() + + target_compile_options(soci_compiler_interface INTERFACE "/bigobj") +else() + + if (SOCI_ENABLE_WERROR) + target_compile_options(soci_compiler_interface INTERFACE "-Werror") + endif() + + + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER}" MATCHES "clang") + if(SOCI_ASAN AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) + # This can also be used to set a linker flag + target_link_libraries(soci_compiler_interface INTERFACE "-fsanitize=address") + target_compile_options(soci_compiler_interface INTERFACE "-fsanitize=address") + endif() + + set(SOCI_USE_STD_FLAGS ON) + elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if (SOCI_ASAN AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) + # This can also be used to set a linker flag + target_link_libraries(soci_compiler_interface INTERFACE "-fsanitize=address") + target_compile_options(soci_compiler_interface INTERFACE "-fsanitize=address") + endif() + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SOCI_GCC_CLANG_COMMON_FLAGS} ") + if (CMAKE_COMPILER_IS_GNUCXX) + if (NOT (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")) + target_compile_options(soci_compiler_interface INTERFACE "-Wno-variadic-macros") + endif() + endif() + + set(SOCI_USE_STD_FLAGS ON) + else() + message(WARNING "Unknown toolset - using default flags to build SOCI") + endif() + + if (SOCI_USE_STD_FLAGS) + target_compile_options(soci_compiler_interface + INTERFACE + "-pedantic" + "-Wno-error=parentheses" + "-Wall" + "-Wextra" + "-Wpointer-arith" + "-Wcast-align" + "-Wcast-qual" + "-Wfloat-equal" + "-Woverloaded-virtual" + "-Wredundant-decls" + "-Wno-long-long" + ) + endif() +endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e53e48c73..7f9a8165e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -58,6 +58,8 @@ if (SOCI_BOOST) endif() target_link_libraries(soci_core + PUBLIC + soci_compiler_interface PRIVATE Threads::Threads ) @@ -66,7 +68,8 @@ target_compile_definitions(soci_core PRIVATE DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" # TODO: Configure prefix and suffix properly - SOCI_LIB_PREFIX="" - SOCI_LIB_SUFFIX="" + SOCI_LIB_PREFIX="${SOCI_LIB_PREFIX}" + SOCI_LIB_SUFFIX="${SOCI_LIB_SUFFIX}" + SOCI_DEBUG_SUFFIX="${SOCI_DEBUG_SUFFIX}" ) From 5f65bb5a504ce3ef014656b0ffde3f6948d0ab30 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:22:25 +0100 Subject: [PATCH 34/79] Update CMake examples --- examples/connect/CMakeLists.txt | 14 +++----------- examples/subdir-include/CMakeLists.txt | 12 ++++-------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/examples/connect/CMakeLists.txt b/examples/connect/CMakeLists.txt index c315cc91f..22c66051d 100644 --- a/examples/connect/CMakeLists.txt +++ b/examples/connect/CMakeLists.txt @@ -7,20 +7,12 @@ # This is a very simple example of using SOCI in a CMake-based project. -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(SOCIExampleConnect) -find_package(Soci REQUIRED) +find_package(SOCI REQUIRED) add_executable(connect connect.cpp) -# Note that depending on the SOCI library automatically pulls in the required -# SOCI compilation options too, i.e. there is no need to explicitly use -# target_include_directories(). -# -# Linking with just soci_core is enough when using shared libraries, as the -# required backends will be loaded dynamically during run-time, but when using -# static libraries you would need to link with all the soci_ libraries -# needed too. -target_link_libraries(connect PRIVATE SOCI::soci_core) +target_link_libraries(connect PRIVATE SOCI::soci) diff --git a/examples/subdir-include/CMakeLists.txt b/examples/subdir-include/CMakeLists.txt index 6861bc560..e14b62365 100644 --- a/examples/subdir-include/CMakeLists.txt +++ b/examples/subdir-include/CMakeLists.txt @@ -2,7 +2,7 @@ # when placing SOCI in a subdirectory lib/soci/ of your project. # For this example, the SOCI backend called Empty is used. -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(SOCIExampleSubdirectoryInclude) @@ -10,7 +10,7 @@ set(SOCI_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/soci) # All compile options need to be set before the SOCI directory gets included. # The backend you want to use needs to be enabled here. -option(SOCI_EMPTY "Build the sample backend called Empty" ON) +set(SOCI_EMPTY ON CACHE INTERNAL "" FORCE) # This line needs to be changed to include the directory you placed SOCI in, e.g. # add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/soci ${SOCI_BINARY_DIR}) @@ -18,12 +18,8 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${SOCI_BINARY_DIR}) add_executable(subdir_include subdir-include.cpp) -# Link the soci_ libraries you want to use here. -# There is no need to explicitly use target_include_directories() for the -# main headers or the generated header (soci/soci-config.h), because both -# are automatically added as part of the Soci::core target. +# Link to SOCI (SOCI::soci will resolve all requested dependencies automatically) target_link_libraries(subdir_include PRIVATE - Soci::core - Soci::empty + SOCI::soci ) From 5150a5191a2ca373f0c95b69e2960ef66e0f767d Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:23:54 +0100 Subject: [PATCH 35/79] Get rid of deprecated Makefiles --- src/backends/empty/Makefile.basic | 88 ------------------- src/backends/firebird/Makefile.basic | 101 ---------------------- src/backends/mysql/Makefile.basic | 97 --------------------- src/backends/odbc/Makefile.basic | 89 ------------------- src/backends/odbc/makefile.msvc | 48 ----------- src/backends/oracle/Makefile.basic | 95 --------------------- src/backends/postgresql/Makefile.basic | 114 ------------------------- src/backends/sqlite3/Makefile.basic | 114 ------------------------- src/core/Makefile.basic | 94 -------------------- tests/oracle/Makefile.basic | 13 --- tests/postgresql/Makefile.basic | 12 --- tests/sqlite3/Makefile.basic | 12 --- 12 files changed, 877 deletions(-) delete mode 100644 src/backends/empty/Makefile.basic delete mode 100644 src/backends/firebird/Makefile.basic delete mode 100644 src/backends/mysql/Makefile.basic delete mode 100644 src/backends/odbc/Makefile.basic delete mode 100644 src/backends/odbc/makefile.msvc delete mode 100644 src/backends/oracle/Makefile.basic delete mode 100644 src/backends/postgresql/Makefile.basic delete mode 100644 src/backends/sqlite3/Makefile.basic delete mode 100644 src/core/Makefile.basic delete mode 100644 tests/oracle/Makefile.basic delete mode 100644 tests/postgresql/Makefile.basic delete mode 100644 tests/sqlite3/Makefile.basic diff --git a/src/backends/empty/Makefile.basic b/src/backends/empty/Makefile.basic deleted file mode 100644 index 7fe0f9107..000000000 --- a/src/backends/empty/Makefile.basic +++ /dev/null @@ -1,88 +0,0 @@ -# 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 diff --git a/src/backends/firebird/Makefile.basic b/src/backends/firebird/Makefile.basic deleted file mode 100644 index 5c0ffd15c..000000000 --- a/src/backends/firebird/Makefile.basic +++ /dev/null @@ -1,101 +0,0 @@ -# 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 diff --git a/src/backends/mysql/Makefile.basic b/src/backends/mysql/Makefile.basic deleted file mode 100644 index 0073fadd3..000000000 --- a/src/backends/mysql/Makefile.basic +++ /dev/null @@ -1,97 +0,0 @@ -# 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 diff --git a/src/backends/odbc/Makefile.basic b/src/backends/odbc/Makefile.basic deleted file mode 100644 index 5082f914f..000000000 --- a/src/backends/odbc/Makefile.basic +++ /dev/null @@ -1,89 +0,0 @@ -# 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 diff --git a/src/backends/odbc/makefile.msvc b/src/backends/odbc/makefile.msvc deleted file mode 100644 index dcc5a96be..000000000 --- a/src/backends/odbc/makefile.msvc +++ /dev/null @@ -1,48 +0,0 @@ -# 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 diff --git a/src/backends/oracle/Makefile.basic b/src/backends/oracle/Makefile.basic deleted file mode 100644 index 31b861f5e..000000000 --- a/src/backends/oracle/Makefile.basic +++ /dev/null @@ -1,95 +0,0 @@ -# The following variable is specific to this backend and its correct -# values might depend on your environment - feel free to set it accordingly. - -ORACLEINCLUDEDIR = -I${ORACLE_HOME}/rdbms/public - -# The rest of the Makefile is indepentent of the target environment. - -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -CXXFLAGSSO = ${CXXFLAGS} -fPIC -INCLUDEDIRS = -I../../../include -I../../../include/private ${ORACLEINCLUDEDIR} - -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.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-s.o - -libsoci_oracle.a : ${OBJECTS} - ar rv $@ $? - rm *.o - -soci-oracle.o : soci-oracle.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -blob.o : blob.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -error.o : error.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_oracle.so ${OBJECTSSO} - rm *.o - -blob-s.o : blob.cpp - ${COMPILER} -c -o $@ $? ${CXXFLAGSSO} ${INCLUDEDIRS} - -error-s.o : error.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_oracle.a libsoci_oracle.so diff --git a/src/backends/postgresql/Makefile.basic b/src/backends/postgresql/Makefile.basic deleted file mode 100644 index 473abfffd..000000000 --- a/src/backends/postgresql/Makefile.basic +++ /dev/null @@ -1,114 +0,0 @@ -# The following variable is specific to this backend and its correct -# values might depend on your environment - feel free to set it accordingly. - -PGSQLINCLUDEDIR = -I/usr/include/postgresql -PGSQLLIBDIR = -L/usr/lib -PGSQLLIBS = -lpq - -# The rest of the Makefile is indepentent of the target environment. - -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -SHARED_CXXFLAGS = ${CXXFLAGS} -fPIC -INCLUDEDIRS = -I../../../include -I../../../include/private ${PGSQLINCLUDEDIR} - -SHARED_LIBDIRS = ${PGSQLLIBDIR} -SHARED_LIBS = ${PGSQLLIBS} ../../core/libsoci_core.a - -UNAME = $(shell uname) -ifeq ($(UNAME),Darwin) - SHARED_LINK_FLAGS = -dynamiclib -flat_namespace -undefined suppress -else - SHARED_LINK_FLAGS = -shared -endif - - -OBJECTS = blob.o error.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 - -SHARED_OBJECTS = blob-s.o error-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_postgresql.a : ${OBJECTS} - ar rv $@ $? - rm *.o - - -blob.o : blob.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -error.o : error.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -common.o : ../../core/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 : ${SHARED_OBJECTS} - ${COMPILER} ${SHARED_LINK_FLAGS} -o libsoci_postgresql.so \ - ${SHARED_OBJECTS} ${SHARED_LIBDIRS} ${SHARED_LIBS} - rm *.o - -blob-s.o : blob.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -error-s.o : error.cpp - ${COMPILER} -c -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} - -common-s.o : ../../core/common.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -factory-s.o : factory.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -row-id-s.o : row-id.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -session-s.o : session.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -standard-into-type-s.o : standard-into-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -standard-use-type-s.o : standard-use-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -statement-s.o : statement.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -vector-into-type-s.o : vector-into-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -vector-use-type-s.o : vector-use-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - - -clean : - rm -f libsoci_postgresql.a libsoci_postgresql.so diff --git a/src/backends/sqlite3/Makefile.basic b/src/backends/sqlite3/Makefile.basic deleted file mode 100644 index 6594dd31e..000000000 --- a/src/backends/sqlite3/Makefile.basic +++ /dev/null @@ -1,114 +0,0 @@ -# The following variable is specific to this backend and its correct -# values might depend on your environment - feel free to set it accordingly. - -SQLITE3INCLUDEDIR = -I/usr/include -SQLITE3LIBDIR = -L/usr/lib -SQLITE3LIBS = -lsqlite3 - -# The rest of the Makefile is indepentent of the target environment. - -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -SHARED_CXXFLAGS = ${CXXFLAGS} -fPIC -INCLUDEDIRS = -I../../../include -I../../../include/private ${SQLITE3INCLUDEDIR} - -SHARED_LIBDIRS = ${SQLITE3LIBDIR} -SHARED_LIBS = ${SQLITE3LIBS} ../../core/libsoci_core.a - -UNAME = $(shell uname) -ifeq ($(UNAME),Darwin) - SHARED_LINK_FLAGS = -dynamiclib -flat_namespace -undefined suppress -else - SHARED_LINK_FLAGS = -shared -endif - - -OBJECTS = blob.o error.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 - -SHARED_OBJECTS = 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_sqlite3.a : ${OBJECTS} - ar rv $@ $? - rm *.o - - -blob.o : blob.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -error.o : error.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 : ${SHARED_OBJECTS} - ${COMPILER} ${SHARED_LINK_FLAGS} -o libsoci_sqlite3.so \ - ${SHARED_OBJECTS} ${SHARED_LIBDIRS} ${SHARED_LIBS} - rm *.o - -blob-s.o : blob.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -error-s.o : error.cpp - ${COMPILER} -c -o $@ $? ${CXXFLAGS} ${INCLUDEDIRS} - -common-s.o : common.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -factory-s.o : factory.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -row-id-s.o : row-id.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -session-s.o : session.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -standard-into-type-s.o : standard-into-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -standard-use-type-s.o : standard-use-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -statement-s.o : statement.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -vector-into-type-s.o : vector-into-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - -vector-use-type-s.o : vector-use-type.cpp - ${COMPILER} -c -o $@ $? ${SHARED_CXXFLAGS} ${INCLUDEDIRS} - - -clean : - rm -f libsoci_sqlite3.a libsoci_sqlite3.so diff --git a/src/core/Makefile.basic b/src/core/Makefile.basic deleted file mode 100644 index bd5f78d86..000000000 --- a/src/core/Makefile.basic +++ /dev/null @@ -1,94 +0,0 @@ -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -INCLUDEDIRS = -I../../include -I../../include/private - -BACKENDLOADERDEFS = -DSOCI_LIB_PREFIX=\"libsoci_\" -DSOCI_LIB_SUFFIX=\".so\" - -OBJS = session.o statement.o row.o values.o \ - into-type.o use-type.o \ - blob.o rowid.o procedure.o ref-counted-prepare-info.o ref-counted-statement.o \ - once-temp-type.o prepare-temp-type.o error.o transaction.o backend-loader.o \ - connection-pool.o connection-parameters.o soci-simple.o - - -libsoci_core.a : generated ${OBJS} - ar rv $@ ${OBJS} - rm *.o - -shared : generated ${OBJS} - ${COMPILER} -fPIC -c ${OBJS} ${CXXFLAGS} ${INCLUDEDIRS} - ${COMPILER} -shared -o libsoci_core.so ${OBJS} - rm *.o - -generated : ../../include/soci/soci-config.h ../../include/private/soci_backends_config.h - -# Note: this file is generated without any configured variables, -# full configuration fill is generated by CMake. -../../include/soci/soci-config.h : ../../include/soci/soci-config.h.in - grep -v CONFIGURED_VARIABLES $? > $@ - -# Note: this file is generated with a basic search path, -# full backends search path is generated by CMake. -../../include/private/soci_backends_config.h : soci_backends_config.h.in - echo '#define DEFAULT_BACKENDS_PATH "."' > $@ - -session.o : session.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -statement.o : statement.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -row.o : row.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -values.o : values.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -into-type.o : into-type.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -use-type.o : use-type.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -blob.o : blob.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -error.o : error.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -rowid.o : rowid.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -procedure.o : procedure.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -ref-counted-prepare-info.o : ref-counted-prepare-info.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -ref-counted-statement.o : ref-counted-statement.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -once-temp-type.o : once-temp-type.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -prepare-temp-type.o : prepare-temp-type.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -transaction.o : transaction.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -backend-loader.o : backend-loader.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${BACKENDLOADERDEFS} ${INCLUDEDIRS} - -connection-pool.o : connection-pool.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -connection-parameters.o : connection-parameters.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - -soci-simple.o : soci-simple.cpp - ${COMPILER} -c $? ${CXXFLAGS} ${INCLUDEDIRS} - - -clean : - rm -f libsoci_core.a libsoci_core.so ../../include/private/soci_backends_config.h diff --git a/tests/oracle/Makefile.basic b/tests/oracle/Makefile.basic deleted file mode 100644 index 11c3e24cd..000000000 --- a/tests/oracle/Makefile.basic +++ /dev/null @@ -1,13 +0,0 @@ -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -INCLUDEDIRS = -I../../include -I../../include/private -I.. \ - -I${ORACLE_HOME}/rdbms/public -LIBDIRS = -L../../src/core -L../../src/backends/oracle -L${ORACLE_HOME}/lib -LIBS = -lsoci_core -lsoci_oracle -ldl -lclntsh -locci -lnnz11 - -test-oracle : test-oracle.cpp - ${COMPILER} $? -o $@ ${INCLUDEDIRS} ${LIBDIRS} ${LIBS} - - -clean : - rm -f test-oracle diff --git a/tests/postgresql/Makefile.basic b/tests/postgresql/Makefile.basic deleted file mode 100644 index 145347c35..000000000 --- a/tests/postgresql/Makefile.basic +++ /dev/null @@ -1,12 +0,0 @@ -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -INCLUDEDIRS = -I../../include -I../../include/private -I.. -I/usr/include/postgresql -LIBDIRS = -L../../src/core -L../../src/backends/postgresql -L/usr/lib/x86_64-linux-gnu -LIBS = -lsoci_postgresql -lsoci_core -ldl -lpq - -test-postgresql : test-postgresql.cpp - ${COMPILER} $? -o $@ ${INCLUDEDIRS} ${LIBDIRS} ${LIBS} - - -clean : - rm -f test-postgresql diff --git a/tests/sqlite3/Makefile.basic b/tests/sqlite3/Makefile.basic deleted file mode 100644 index 697d67404..000000000 --- a/tests/sqlite3/Makefile.basic +++ /dev/null @@ -1,12 +0,0 @@ -COMPILER = g++ -CXXFLAGS = -Wall -pedantic -Wno-long-long -INCLUDEDIRS = -I../../include -I../../include/private -I.. -I/usr/include/sqlite3 -LIBDIRS = -L../../src/core -L../../src/backends/sqlite3 -L/usr/lib/x86_64-linux-gnu -LIBS = -lsoci_sqlite3 -lsoci_core -ldl -lsqlite3 - -test-sqlite3 : test-sqlite3.cpp - ${COMPILER} $? -o $@ ${INCLUDEDIRS} ${LIBDIRS} ${LIBS} - - -clean : - rm -f test-sqlite3 From acd12941080814872087491ea0d30bdebcef3422 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:25:10 +0100 Subject: [PATCH 36/79] Build Boost bindings by default on CI --- scripts/ci/common.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/ci/common.sh b/scripts/ci/common.sh index e2857216a..f605268e7 100644 --- a/scripts/ci/common.sh +++ b/scripts/ci/common.sh @@ -61,6 +61,9 @@ fi if [ -n "${WITH_BOOST}" ]; then SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DWITH_BOOST=${WITH_BOOST}" +else + # Build Boost bindings by default + SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DWITH_BOOST=ON" fi if [ -n "${SOCI_BUILD_STATIC}" ]; then From aea1958205047a84df7131b15c653c6c65f31bd2 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:31:48 +0100 Subject: [PATCH 37/79] Provide compat for old WITH_BOOST var --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b111d62f6..12a106827 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,13 @@ option(SOCI_ASAN "Enable building SOCI with enabled address sanitizers" OFF) cmake_dependent_option(SOCI_LTO "Enable link time optimizations in release builds" ON "LTO_AVAILABLE" OFF) option(SOCI_VISIBILITY "Make all functions hidden by default - this exposes only explicitly exported functions" ON) set(SOCI_LD "" CACHE STRING "Specify a non-default linker") -# Default-enable only if Boost has been found before (in a superproject) -option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${Boost_FOUND}) +if (WITH_BOOST) + message(DEPRECATION "Use of WITH_BOOST option is deprecated - use SOCI_BOOST instead") + option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${WITH_BOOST}) +else() + # Default-enable only if Boost has been found before (in a superproject) + option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${Boost_FOUND}) +endif() # Configure LTO for anything but Debug builds (if enabled in the first place) From 47c05f3082f05293828c6eb1ce9ac74ec3fabfb4 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 19:52:26 +0100 Subject: [PATCH 38/79] Add compat to old target names --- CMakeLists.txt | 4 ++++ cmake/soci_compat.cmake | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 cmake/soci_compat.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 12a106827..4f884c097 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,10 @@ include(soci_compiler_options) add_subdirectory(src) + +include(soci_compat) + + if (SOCI_TESTS) include(CTest) enable_testing() diff --git a/cmake/soci_compat.cmake b/cmake/soci_compat.cmake new file mode 100644 index 000000000..480cb36b4 --- /dev/null +++ b/cmake/soci_compat.cmake @@ -0,0 +1,27 @@ +# Take care of defining ALIAS targets to keep old target names working + +if (SOCI_SHARED) + set(SUFFIX "") +else() + set(SUFFIX "_static") +endif() + +set(OLD_NAMES core db2 empty firebird mysql odbc oracle postgresql sqlite3) +set(ALIAS_NAMES Core DB2 Empty Firebird MySQL ODBC Oracle PostgreSQL SQLite3) + +list(LENGTH ALIAS_NAMES N_ITEMS) +math(EXPR LAST_ITEM "${N_ITEMS} - 1") + +foreach (I RANGE ${LAST_ITEM}) + list(GET OLD_NAMES ${I} CURRENT_NAME) + list(GET ALIAS_NAMES ${I} CURRENT_ALIAS) + + if (NOT TARGET SOCI::${CURRENT_ALIAS}) + continue() + endif() + + get_target_property(UNDERLYING_LIB SOCI::${CURRENT_ALIAS} ALIASED_TARGET) + + add_library(Soci::${CURRENT_NAME}${SUFFIX} ALIAS ${UNDERLYING_LIB}) + add_library(SOCI::${CURRENT_NAME}${SUFFIX} ALIAS ${UNDERLYING_LIB}) +endforeach() From e2c1a5302f5b71ca8a1eed9a89bf1290f4338bd5 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 20:07:39 +0100 Subject: [PATCH 39/79] Disable Boost bindings on macOS CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c756f972..ce4bb68b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,10 +35,13 @@ jobs: include: - backend: empty runner: macos-12 + no_boost: true - backend: postgresql runner: macos-12 + no_boost: true - backend: sqlite3 runner: macos-12 + no_boost: true - backend: oracle no_boost: true runner: ubuntu-22.04 From a8687153e9ed04b4f54ed52661f2f30f90e02d86 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 29 Feb 2024 20:07:56 +0100 Subject: [PATCH 40/79] Properly set ABI version macro --- src/core/CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7f9a8165e..f3c96cc17 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -64,12 +64,20 @@ target_link_libraries(soci_core Threads::Threads ) +if (WIN32) + set(VERSION_SEP "_") +elseif(UNIX) + set(VERSION_SEP ".") +else() + message(WARNING "No known ABI version scheme for current platform") + set(VERSION_SEP "-") +endif() + target_compile_definitions(soci_core PRIVATE DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" - # TODO: Configure prefix and suffix properly SOCI_LIB_PREFIX="${SOCI_LIB_PREFIX}" SOCI_LIB_SUFFIX="${SOCI_LIB_SUFFIX}" SOCI_DEBUG_SUFFIX="${SOCI_DEBUG_SUFFIX}" + SOCI_ABI_VERSION="${PROJECT_VERSION_MAJOR}${VERSION_SEP}${PROJECT_VERSION_MINOR}" ) - From 9defe9b80ed786b86e23cee95ddaf0e1575a2b41 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 1 Mar 2024 20:23:45 +0100 Subject: [PATCH 41/79] Start setting up install support --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 9 +++++ src/core/CMakeLists.txt | 84 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f884c097..a2d4c7c24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.21 FATAL_ERROR) +cmake_minimum_required(VERSION 3.23 FATAL_ERROR) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/find_modules") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0eb2a029..a46cbd835 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,3 +82,12 @@ endif() if (NOT (CONFIG_CONTENT_HASH STREQUAL CONFIG_FILE_HASH)) file(WRITE "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h" "${CONFIG_CONTENT}") endif() + +# Append the generated config header to the public headers of the core target +target_sources(soci_core + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${SOCI_GENERATED_INCLUDES_DIR}" + FILES + "${SOCI_GENERATED_INCLUDES_DIR}/soci/soci-config.h" +) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f3c96cc17..6daea732d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,3 +1,5 @@ +include(GNUInstallDirs) + find_package(Threads REQUIRED) add_library(soci_core @@ -25,12 +27,71 @@ add_library(soci_core "use-type.cpp" "values.cpp" ) + add_library(SOCI::Core ALIAS soci_core) +# We have to explicitly list all public header files in order for them to get +# installed properly. This will automatically set the BASE_DIR up as an include +# directory for the target wrapped in a BUILD_INTERFACE generator expression. +target_sources(soci_core + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include/" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/backend-loader.h" + "${PROJECT_SOURCE_DIR}/include/soci/bind-values.h" + "${PROJECT_SOURCE_DIR}/include/soci/blob-exchange.h" + "${PROJECT_SOURCE_DIR}/include/soci/blob.h" + "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-fusion.h>" + "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-gregorian-date.h>" + "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-optional.h>" + "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-tuple.h>" + "${PROJECT_SOURCE_DIR}/include/soci/callbacks.h" + "${PROJECT_SOURCE_DIR}/include/soci/column-info.h" + "${PROJECT_SOURCE_DIR}/include/soci/connection-parameters.h" + "${PROJECT_SOURCE_DIR}/include/soci/connection-pool.h" + "${PROJECT_SOURCE_DIR}/include/soci/error.h" + "${PROJECT_SOURCE_DIR}/include/soci/exchange-traits.h" + "${PROJECT_SOURCE_DIR}/include/soci/fixed-size-ints.h" + "${PROJECT_SOURCE_DIR}/include/soci/into.h" + "${PROJECT_SOURCE_DIR}/include/soci/into-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/logger.h" + "${PROJECT_SOURCE_DIR}/include/soci/noreturn.h" + "${PROJECT_SOURCE_DIR}/include/soci/once-temp-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/prepare-temp-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/procedure.h" + "${PROJECT_SOURCE_DIR}/include/soci/query_transformation.h" + "${PROJECT_SOURCE_DIR}/include/soci/ref-counted-prepare-info.h" + "${PROJECT_SOURCE_DIR}/include/soci/ref-counted-statement.h" + "${PROJECT_SOURCE_DIR}/include/soci/row-exchange.h" + "${PROJECT_SOURCE_DIR}/include/soci/row.h" + "${PROJECT_SOURCE_DIR}/include/soci/rowid-exchange.h" + "${PROJECT_SOURCE_DIR}/include/soci/rowid.h" + "${PROJECT_SOURCE_DIR}/include/soci/rowset.h" + "${PROJECT_SOURCE_DIR}/include/soci/session.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci-backend.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci-platform.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci-simple.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci-types.h" + "${PROJECT_SOURCE_DIR}/include/soci/statement.h" + "${PROJECT_SOURCE_DIR}/include/soci/std-optional.h" + "${PROJECT_SOURCE_DIR}/include/soci/transaction.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-conversion.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-conversion-traits.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-holder.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-ptr.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-wrappers.h" + "${PROJECT_SOURCE_DIR}/include/soci/use.h" + "${PROJECT_SOURCE_DIR}/include/soci/use-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/values-exchange.h" + "${PROJECT_SOURCE_DIR}/include/soci/values.h" + "${PROJECT_SOURCE_DIR}/include/soci/version.h" +) + target_include_directories(soci_core PUBLIC - "${SOCI_GENERATED_INCLUDES_DIR}" - "${PROJECT_SOURCE_DIR}/include" + "$" PRIVATE "${PROJECT_SOURCE_DIR}/include/soci" "${PROJECT_SOURCE_DIR}/include/private" @@ -59,7 +120,7 @@ endif() target_link_libraries(soci_core PUBLIC - soci_compiler_interface + $ PRIVATE Threads::Threads ) @@ -81,3 +142,20 @@ target_compile_definitions(soci_core SOCI_DEBUG_SUFFIX="${SOCI_DEBUG_SUFFIX}" SOCI_ABI_VERSION="${PROJECT_VERSION_MAJOR}${VERSION_SEP}${PROJECT_VERSION_MINOR}" ) + +set_target_properties(soci_core PROPERTIES EXPORT_NAME Core) + +install( + TARGETS soci_core + EXPORT SociCoreTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" +) +# Generate and install the export file for the target +install( + EXPORT SociCoreTargets + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/soci" + FILE SociCoreTargets.cmake + NAMESPACE SOCI:: +) From 35f0f923c5781425fe7e09befbf54fa1435fd44c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 13 Apr 2024 20:11:17 +0200 Subject: [PATCH 42/79] More work on installation support --- CMakeLists.txt | 33 +++++ SOCIConfig.cmake.in | 125 ++++++++++++++++++ cmake/soci_utils.cmake | 129 +++++++++++++++++++ src/CMakeLists.txt | 4 - src/backends/sqlite3/CMakeLists.txt | 46 ++++++- src/backends/sqlite3/SociSQLite3Config.cmake | 6 + src/core/CMakeLists.txt | 58 +++++---- 7 files changed, 370 insertions(+), 31 deletions(-) create mode 100644 SOCIConfig.cmake.in create mode 100644 src/backends/sqlite3/SociSQLite3Config.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a2d4c7c24..5548b8fd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ project(SOCI LANGUAGES CXX ) +include(soci_utils) + if (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED OR CMAKE_CXX_STANDARD LESS 14) set(CMAKE_CXX_STANDARD 14) endif() @@ -58,6 +60,16 @@ if (SOCI_VISIBILITY) set(CMAKE_CXX_VISIBILITY_PRESET hidden) endif() +if (NOT APPLE) + # This makes runtime loaders look for library dependencies + # in the same directory as the library is located in. + # For details see Craig Scott's CppCon 2019 talk + + # Note: The variable's content is set to $ORIGIN literally, + # this is NOT a butchered cmake variable expansion + set(CMAKE_INSTALL_RPATH "$ORIGIN") +endif() + if (SOCI_LD) # CMake asks the compiler to do the linking so we have to pass the desired linker to the compiler set(USE_LD_FLAG "-fuse-ld=${SOCI_LD}") @@ -87,9 +99,13 @@ set(SOCI_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}" CACHE STRING "Specifies suffix f include(soci_compiler_options) +include(GNUInstallDirs) + +set(SOCI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/soci-${PROJECT_VERSION}") add_subdirectory(src) +message(STATUS "This: ${SOCI_DEPENDENCY_SOCI_COMPONENTS}") include(soci_compat) @@ -99,3 +115,20 @@ if (SOCI_TESTS) enable_testing() add_subdirectory(tests) endif() + + +# Packaging +include(CMakePackageConfigHelpers) +configure_package_config_file("SOCIConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/SOCIConfig.cmake" + INSTALL_DESTINATION "${SOCI_INSTALL_CMAKEDIR}" +) +write_basic_package_version_file(SOCIConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/SOCIConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/SOCIConfigVersion.cmake" + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" +) diff --git a/SOCIConfig.cmake.in b/SOCIConfig.cmake.in new file mode 100644 index 000000000..c7e5560c6 --- /dev/null +++ b/SOCIConfig.cmake.in @@ -0,0 +1,125 @@ +@PACKAGE_INIT@ + + + +# Ensure that the Core target is always included (and as first component) +list(REMOVE_ITEM SOCI_FIND_COMPONENTS Core) +list(INSERT SOCI_FIND_COMPONENTS 0 Core) + +set(__dep_soci_comps "@SOCI_DEPENDENCY_SOCI_COMPONENTS@") +set(__dep_names "@SOCI_DEPENDENCY_NAMES@") +set(__dep_dep_targets "@SOCI_DEPENDENCY_TARGETS@") +set(__dep_required "@SOCI_DEPENDENCY_REQUIRED@") +set(__dep_macro_names "@SOCI_DEPENDENCY_MACRO_NAMES@") +set(__dep_components "@SOCI_DEPENDENCY_COMPONENTS@") + +list(LENGTH __dep_soci_comps __list_size) +foreach (__item IN ITEMS __dep_names __dep_dep_targets __dep_required __dep_macro_names __dep_components) + list(LENGTH ${__item} __current_size) + if (NOT (__list_size EQUAL __current_size)) + message(FATAL_ERROR "SociConfig is invalid -> dependency lists have different sizes") + endif() +endforeach() +unset(__current_size) + +math(EXPR __list_size "${__list_size} - 1") + +foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) + if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/SOCI${__comp}Targets.cmake") + message(FATAL_ERROR "Unknown component '${__comp}' for SOCI") + endif() + + # Handle component-specific dependencies + set(__link_targets) + set(__define_macros) + foreach (__i RANGE ${__list_size}) + list(GET __dep_soci_comps ${__i} __dep_comp) + if (__dep_comp MATCHES "::${__comp}$") + # This entry matches the current component + list(GET __dep_names ${__i} __dep) + list(GET __dep_dep_targets ${__i} __targets) + list(GET __dep_required ${__i} __required) + list(GET __dep_macro_names ${__i} __macros) + list(GET __dep_components ${__i} __components) + + # Split list-valued entries to become actual lists + string(REPLACE "|" ";" __targets "${__targets}") + string(REPLACE "|" ";" __macros "${__macros}") + string(REPLACE "|" ";" __components "${__components}") + + set(__already_found) + foreach (__tgt IN LISTS __targets) + if (TARGET ${__tgt}) + set(__already_found ON) + else() + set(__already_found OFF) + break() + endif() + endforeach() + + if (__already_found) + continue() + endif() + + if (__components) + set(__components COMPONENTS ${__components}) + endif() + + find_package( + ${__dep} + ${__components} + ${${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY} + ) + + if (NOT ${__dep}_FOUND) + if (__required) + message(FATAL_ERROR "Unable to find the dependency ${__dep} for the requested ${__comp} SOCI component") + endif() + continue() + endif() + + list(APPEND __link_targets ${__targets}) + list(APPEND __define_macros ${__macros}) + endif() + endforeach() + unset(__i) + + include("${CMAKE_CURRENT_LIST_DIR}/SOCI${__comp}Targets.cmake") + + set_property( + TARGET SOCI::${__comp} + APPEND + PROPERTY INTERFACE_LINK_LIBRARIES "${__link_targets}" + ) + if (__define_macros) + set_property( + TARGET SOCI::${__comp} + APPEND + PROPERTY INTERFACE_COMPILE_DEFINITIONS "${__define_macros}" + ) + endif() + + set(${CMAKE_FIND_PACKAGE_NAME}_${__comp}_FOUND ON) +endforeach() +unset(__comp) + + +unset(__dep_soci_comps) +unset(__dep_names) +unset(__dep_dep_targets) +unset(__dep_required) +unset(__dep_macro_names) +unset(__dep_components) + + +check_required_components(SOCI) + +if (NOT DEFINED SOCI_FOUND OR SOCI_FOUND) + add_library(soci_interface INTERFACE) + + foreach (__comp IN LISTS SOCI_FIND_COMPONENTS) + target_link_libraries(soci_interface INTERFACE SOCI::${__comp}) + endforeach() + + add_library(SOCI::SOCI ALIAS soci_interface) +endif() diff --git a/cmake/soci_utils.cmake b/cmake/soci_utils.cmake index 0b44f00a9..c234e07a6 100644 --- a/cmake/soci_utils.cmake +++ b/cmake/soci_utils.cmake @@ -49,3 +49,132 @@ function(soci_verify_parsed_arguments) endforeach() endif() endfunction() + + +# Initialize variables populated by soci_public_dependency +set(SOCI_DEPENDENCY_VARIABLES + "SOCI_DEPENDENCY_SOCI_COMPONENTS" + "SOCI_DEPENDENCY_NAMES" + "SOCI_DEPENDENCY_TARGETS" + "SOCI_DEPENDENCY_REQUIRED" + "SOCI_DEPENDENCY_MACRO_NAMES" + "SOCI_DEPENDENCY_COMPONENTS" +) +if (NOT DEFINED SOCI_UTILS_ALREADY_INCLUDED) + foreach(VAR_NAME IN LISTS SOCI_DEPENDENCY_VARIABLES) + set("${VAR_NAME}" "" CACHE INTERNAL "") + endforeach() +endif() + +# This function declares a public dependency of a given target in such a way that +# the dependency is automatically populated to a generated SOCI config file. +# +# Use as +# soci_public_dependency( +# NAME +# DEP_TARGET +# TARGET +# [MACRO_NAMES ...] +# [REQUIRED] +# [COMPONENTS ...] +# ) +# where +# - is the name of the dependency (used for lookup via find_package) +# - is the name of the target that will be imported upon a +# successful find_package call +# - is the name of the ALIAS target to link the found dependency to +# - are the names of specific components of the dependency +macro(soci_public_dependency) + set(FLAGS "REQUIRED") + set(ONE_VAL_OPTIONS "TARGET" "NAME") + set(MULTI_VAL_OPTIONS "COMPONENTS" "MACRO_NAMES" "DEP_TARGETS") + + cmake_parse_arguments(PUBLIC_DEP "${FLAGS}" "${ONE_VAL_OPTIONS}" "${MULTI_VAL_OPTIONS}" ${ARGV}) + soci_verify_parsed_arguments( + PREFIX "PUBLIC_DEP" + FUNCTION_NAME "soci_public_dependency" + REQUIRED "TARGET" "NAME" "DEP_TARGETS" + ) + + # Sanity checking + if (NOT TARGET "${PUBLIC_DEP_TARGET}") + message(FATAL_ERROR "Provided TARGET '${PUBLIC_DEP_TARGET}' does not exist") + endif() + + get_target_property(UNDERLYING_TARGET "${PUBLIC_DEP_TARGET}" ALIASED_TARGET) + if (NOT UNDERLYING_TARGET) + message(FATAL_ERROR "Provided TARGET '${PUBLIC_DEP_TARGET}' is expected to be an ALIAS target") + endif() + + + # Bookkeeping + list(APPEND SOCI_DEPENDENCY_SOCI_COMPONENTS "${PUBLIC_DEP_TARGET}") + list(APPEND SOCI_DEPENDENCY_NAMES "${PUBLIC_DEP_NAME}") + list(JOIN PUBLIC_DEP_DEP_TARGETS "|" STORED_TARGETS) + list(APPEND SOCI_DEPENDENCY_TARGETS "${STORED_TARGETS}") + list(APPEND SOCI_DEPENDENCY_REQUIRED "${PUBLIC_DEP_REQUIRED}") + if (PUBLIC_DEP_MACRO_NAMES) + list(JOIN PUBLIC_DEP_MACRO_NAMES "|" STORED_MACROS) + else() + set(STORED_MACROS "") + endif() + list(APPEND SOCI_DEPENDENCY_MACRO_NAMES "${STORED_MACROS}") + if (PUBLIC_DEP_COMPONENTS) + list(JOIN PUBLIC_DEP_COMPONENTS "|" STORED_COMPONENTS) + else() + set(STORED_COMPONENTS "") + endif() + list(APPEND SOCI_DEPENDENCY_COMPONENTS "${STORED_COMPONENTS}") + + + foreach(VAR_NAME IN LISTS SOCI_DEPENDENCY_VARIABLES) + set("${VAR_NAME}" "${${VAR_NAME}}" CACHE INTERNAL "") + endforeach() + + + # Search for the package now + if (PUBLIC_DEP_REQUIRED) + set(REQUIRED "REQUIRED") + else() + set(REQUIRED "") + endif() + + if (PUBLIC_DEP_COMPONENTS) + set(COMPONENTS COMPONENTS ${PUBLIC_DEP_COMPONENTS}) + else() + set(COMPONENTS "") + endif() + + set(SKIP_SEARCH ON) + foreach (TGT IN LISTS PUBLIC_DEP_DEP_TARGETS) + if (NOT TARGET "${TGT}") + set(SKIP_SEARCH OFF) + endif() + endforeach() + + if (NOT SKIP_SEARCH) + find_package("${PUBLIC_DEP_NAME}" ${COMPONENTS} ${REQUIRED}) + endif() + + set(FOUND_ONE OFF) + foreach (TGT IN LISTS PUBLIC_DEP_DEP_TARGETS) + if (NOT TARGET "${TGT}") + if (FOUND_ONE) + message(FATAL_ERROR "Some but not all targets of a SOCI dependency have been found") + endif() + return() + endif() + + set(FOUND_ONE ON) + + target_link_libraries("${UNDERLYING_TARGET}" PUBLIC "$") + endforeach() + + foreach (MACRO_NAME IN LISTS PUBLIC_DEP_MACRO_NAMES) + # Note: We don't want these compile definitions to be exported to the install tree + # -> We put the logic of when they should be defined into the cmake config files + target_compile_definitions("${UNDERLYING_TARGET}" PUBLIC "$") + endforeach() +endmacro() + +set(SOCI_UTILS_ALREADY_INCLUDED TRUE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a46cbd835..7768c106d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,8 +25,6 @@ check_cxx_source_compiles( set(CONFIG_LINES "#ifndef SOCI_SOCICONFIG_H_INCLUDED" "#define SOCI_SOCICONFIG_H_INCLUDED" "") set(CONFIG_VARS - SOCI_HAVE_BOOST - SOCI_HAVE_BOOST_DATE_TIME SOCI_EMPTY SOCI_DB2 SOCI_FIREBIRD @@ -38,8 +36,6 @@ set(CONFIG_VARS SOCI_VISIBILITY_ATTRIBUTE_SUPPORTED ) set(CONFIG_MACROS - SOCI_HAVE_BOOST - SOCI_HAVE_BOOST_DATE_TIME SOCI_HAVE_EMPTY SOCI_HAVE_DB2 SOCI_HAVE_FIREBIRD diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index e7d6ca1f0..6c2e98813 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -26,9 +26,24 @@ add_library(soci_sqlite3 "vector-use-type.cpp" ) -target_link_libraries(soci_sqlite3 - PUBLIC - SQLite::SQLite3 SOCI::Core +add_library(SOCI::SQLite3 ALIAS soci_sqlite3) + +set_target_properties( + soci_sqlite3 PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME SQLite3 +) + +soci_public_dependency( + NAME SQLite3 + DEP_TARGETS SQLite::SQLite3 + TARGET SOCI::SQLite3 +) +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::SQLite3 ) target_include_directories(soci_sqlite3 @@ -37,6 +52,27 @@ target_include_directories(soci_sqlite3 "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::SQLite3 ALIAS soci_sqlite3) - target_link_libraries(soci_interface INTERFACE SOCI::SQLite3) + + +install( + TARGETS soci_sqlite3 + EXPORT SOCISQLite3Targets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCISQLite3Targets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCISQLite3Targets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/sqlite3/SociSQLite3Config.cmake b/src/backends/sqlite3/SociSQLite3Config.cmake new file mode 100644 index 000000000..d8aa596b9 --- /dev/null +++ b/src/backends/sqlite3/SociSQLite3Config.cmake @@ -0,0 +1,6 @@ +include(CMakeFindDependencyMacro) + +find_dependency(SOCI::Core) +find_dependency(SQLite3) + +include("${CMAKE_CURRENT_LIST_DIR}/SociSQLite3Targets.cmake") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6daea732d..3b8bcfff0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -30,6 +30,13 @@ add_library(soci_core add_library(SOCI::Core ALIAS soci_core) +set_target_properties( + soci_core PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME Core +) + # We have to explicitly list all public header files in order for them to get # installed properly. This will automatically set the BASE_DIR up as an include # directory for the target wrapped in a BUILD_INTERFACE generator expression. @@ -42,10 +49,10 @@ target_sources(soci_core "${PROJECT_SOURCE_DIR}/include/soci/bind-values.h" "${PROJECT_SOURCE_DIR}/include/soci/blob-exchange.h" "${PROJECT_SOURCE_DIR}/include/soci/blob.h" - "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-fusion.h>" - "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-gregorian-date.h>" - "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-optional.h>" - "$<$:${PROJECT_SOURCE_DIR}/include/soci/boost-tuple.h>" + "${PROJECT_SOURCE_DIR}/include/soci/boost-fusion.h" + "${PROJECT_SOURCE_DIR}/include/soci/boost-gregorian-date.h" + "${PROJECT_SOURCE_DIR}/include/soci/boost-optional.h" + "${PROJECT_SOURCE_DIR}/include/soci/boost-tuple.h" "${PROJECT_SOURCE_DIR}/include/soci/callbacks.h" "${PROJECT_SOURCE_DIR}/include/soci/column-info.h" "${PROJECT_SOURCE_DIR}/include/soci/connection-parameters.h" @@ -105,18 +112,19 @@ if (SOCI_SHARED) ) endif() -if (SOCI_BOOST) - find_package(Boost REQUIRED) - target_link_libraries(soci_core PUBLIC Boost::boost) - set(SOCI_HAVE_BOOST TRUE CACHE INTERNAL "" FORCE) - - find_package(Boost COMPONENTS date_time) - - if (TARGET Boost::date_time) - target_link_libraries(soci_core PUBLIC Boost::date_time) - set(SOCI_HAVE_BOOST_DATE_TIME TRUE CACHE INTERNAL "" FORCE) - endif() -endif() +soci_public_dependency( + NAME Boost + DEP_TARGETS Boost::boost Boost::date_time + COMPONENTS date_time + MACRO_NAMES SOCI_HAVE_BOOST SOCI_HAVE_BOOST_DATE_TIME + TARGET SOCI::Core +) +soci_public_dependency( + NAME Boost + DEP_TARGETS Boost::boost + MACRO_NAMES SOCI_HAVE_BOOST + TARGET SOCI::Core +) target_link_libraries(soci_core PUBLIC @@ -143,19 +151,25 @@ target_compile_definitions(soci_core SOCI_ABI_VERSION="${PROJECT_VERSION_MAJOR}${VERSION_SEP}${PROJECT_VERSION_MINOR}" ) -set_target_properties(soci_core PROPERTIES EXPORT_NAME Core) install( TARGETS soci_core - EXPORT SociCoreTargets + EXPORT SOCICoreTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development ) -# Generate and install the export file for the target +# Generate and install a targets file install( - EXPORT SociCoreTargets - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/soci" - FILE SociCoreTargets.cmake + EXPORT SOCICoreTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCICoreTargets.cmake NAMESPACE SOCI:: + COMPONENT soci_development ) From b7d56a03eede7bc83cd82f0cb16d10fb8e514a60 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 23 Apr 2024 18:39:44 +0200 Subject: [PATCH 43/79] Make all components installable --- CMakeLists.txt | 2 - SOCIConfig.cmake.in | 7 ++- src/backends/db2/CMakeLists.txt | 49 ++++++++++++++++--- src/backends/empty/CMakeLists.txt | 40 +++++++++++++-- src/backends/firebird/CMakeLists.txt | 49 ++++++++++++++++--- src/backends/mysql/CMakeLists.txt | 43 ++++++++++++++--- src/backends/odbc/CMakeLists.txt | 50 ++++++++++++++++--- src/backends/oracle/CMakeLists.txt | 51 +++++++++++++++++--- src/backends/postgresql/CMakeLists.txt | 49 ++++++++++++++++--- src/backends/sqlite3/CMakeLists.txt | 2 + src/backends/sqlite3/SociSQLite3Config.cmake | 6 --- 11 files changed, 299 insertions(+), 49 deletions(-) delete mode 100644 src/backends/sqlite3/SociSQLite3Config.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 5548b8fd3..0f19ff592 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,8 +105,6 @@ set(SOCI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/soci-${PROJECT_VERSION} add_subdirectory(src) -message(STATUS "This: ${SOCI_DEPENDENCY_SOCI_COMPONENTS}") - include(soci_compat) diff --git a/SOCIConfig.cmake.in b/SOCIConfig.cmake.in index c7e5560c6..c320f114b 100644 --- a/SOCIConfig.cmake.in +++ b/SOCIConfig.cmake.in @@ -26,7 +26,9 @@ math(EXPR __list_size "${__list_size} - 1") foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) if (NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/SOCI${__comp}Targets.cmake") - message(FATAL_ERROR "Unknown component '${__comp}' for SOCI") + set(SOCI_FOUND FALSE) + set(SOCI_NOT_FOUND_MESSAGE "'${__comp}' is not a known SOCI component") + continue() endif() # Handle component-specific dependencies @@ -73,7 +75,8 @@ foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) if (NOT ${__dep}_FOUND) if (__required) - message(FATAL_ERROR "Unable to find the dependency ${__dep} for the requested ${__comp} SOCI component") + set(SOCI_FOUND FALSE) + set(SOCI_NOT_FOUND_MESSAGE "Unmet dependency '${__dep}' for SOCI component '${__comp}'") endif() continue() endif() diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index 1cc4ad75c..d05db8e13 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -25,10 +25,26 @@ add_library(soci_db2 "vector-use-type.cpp" ) -target_link_libraries(soci_db2 - PUBLIC - DB2::DB2 - SOCI::Core +add_library(SOCI::DB2 ALIAS soci_db2) + +set_target_properties( + soci_db2 PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME DB2 +) + +soci_public_dependency( + NAME DB2 + DEP_TARGETS DB2::DB2 + TARGET SOCI::DB2 + REQUIRED +) +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::DB2 + REQUIRED ) target_include_directories(soci_db2 @@ -37,6 +53,27 @@ target_include_directories(soci_db2 "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::DB2 ALIAS soci_db2) - target_link_libraries(soci_interface INTERFACE SOCI::DB2) + + +install( + TARGETS soci_db2 + EXPORT SOCIDB2Targets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIDB2Targets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIDB2Targets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index b4780ad68..1a11af1d1 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -12,7 +12,20 @@ add_library(soci_empty "vector-use-type.cpp" ) -target_link_libraries(soci_empty PUBLIC SOCI::Core) +add_library(SOCI::Empty ALIAS soci_empty) + +set_target_properties( + soci_empty PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME Empty +) +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::Empty + REQUIRED +) target_include_directories(soci_empty PRIVATE @@ -20,6 +33,27 @@ target_include_directories(soci_empty "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::Empty ALIAS soci_empty) - target_link_libraries(soci_interface INTERFACE SOCI::Empty) + + +install( + TARGETS soci_empty + EXPORT SOCIEmptyTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIEmptyTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIEmptyTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index d70ab73af..e22ea74e4 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -32,10 +32,26 @@ add_library(soci_firebird "vector-use-type.cpp" ) -target_link_libraries(soci_firebird - PUBLIC - Firebird::Firebird - SOCI::Core +add_library(SOCI::Firebird ALIAS soci_firebird) + +set_target_properties( + soci_firebird PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME Firebird +) + +soci_public_dependency( + NAME Firebird + DEP_TARGETS Firebird::Firebird + TARGET SOCI::Firebird + REQUIRED +) +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::Firebird + REQUIRED ) target_include_directories(soci_firebird @@ -44,6 +60,27 @@ target_include_directories(soci_firebird "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::Firebird ALIAS soci_firebird) - target_link_libraries(soci_interface INTERFACE SOCI::Firebird) + + +install( + TARGETS soci_firebird + EXPORT SOCIFirebirdTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIFirebirdTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIFirebirdTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index d1732c81d..49b08bf58 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -26,10 +26,20 @@ add_library(soci_mysql "vector-use-type.cpp" ) -target_link_libraries(soci_mysql - PUBLIC - MySQL::MySQL - SOCI::Core +add_library(SOCI::MySQL ALIAS soci_mysql) + +soci_public_dependency( + NAME MySQL + DEP_TARGETS MySQL::MySQL + TARGET SOCI::MySQL + REQUIRED +) + +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::MySQL + REQUIRED ) target_include_directories(soci_mysql @@ -38,6 +48,27 @@ target_include_directories(soci_mysql "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::MySQL ALIAS soci_mysql) - target_link_libraries(soci_interface INTERFACE SOCI::MySQL) + + +install( + TARGETS soci_mysql + EXPORT SOCIMySQLTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIMySQLTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIMySQLTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index be3d7d88f..be8873518 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -25,10 +25,27 @@ add_library(soci_odbc "vector-use-type.cpp" ) -target_link_libraries(soci_odbc - PUBLIC - ODBC::ODBC - SOCI::Core +add_library(SOCI::ODBC ALIAS soci_odbc) + +set_target_properties( + soci_odbc PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME ODBC +) + +soci_public_dependency( + NAME ODBC + DEP_TARGETS ODBC::ODBC + TARGET SOCI::ODBC + REQUIRED +) + +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::ODBC + REQUIRED ) target_include_directories(soci_odbc @@ -37,6 +54,27 @@ target_include_directories(soci_odbc "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::ODBC ALIAS soci_odbc) - target_link_libraries(soci_interface INTERFACE SOCI::ODBC) + + +install( + TARGETS soci_odbc + EXPORT SOCIODBCTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIODBCTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIODBCTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index f444eab10..4aa48571a 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -25,10 +25,28 @@ add_library(soci_oracle "vector-into-type.cpp" "vector-use-type.cpp" ) -target_link_libraries(soci_oracle - PUBLIC - Oracle::Oracle - SOCI::Core + +add_library(SOCI::Oracle ALIAS soci_oracle) + +set_target_properties( + soci_oracle PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME Oracle +) + +soci_public_dependency( + NAME Oracle + DEP_TARGETS Oracle::Oracle + TARGET SOCI::Oracle + REQUIRED +) + +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::Oracle + REQUIRED ) target_include_directories(soci_oracle @@ -37,6 +55,27 @@ target_include_directories(soci_oracle "${PROJECT_SOURCE_DIR}/include/soci" ) -add_library(SOCI::Oracle ALIAS soci_oracle) - target_link_libraries(soci_interface INTERFACE SOCI::Oracle) + + +install( + TARGETS soci_oracle + EXPORT SOCIOracleTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIOracleTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIOracleTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 9589ff40d..7a2e1cdfd 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -34,10 +34,27 @@ add_library(soci_postgresql "vector-use-type.cpp" ) -target_link_libraries(soci_postgresql - PUBLIC - PostgreSQL::PostgreSQL - SOCI::Core +add_library(SOCI::PostgreSQL ALIAS soci_postgresql) + +set_target_properties( + soci_postgresql PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME PostgreSQL +) + +soci_public_dependency( + NAME PostgreSQL + DEP_TARGETS PostgreSQL::PostgreSQL + TARGET SOCI::PostgreSQL + REQUIRED +) + +soci_public_dependency( + NAME SOCI::Core + DEP_TARGETS SOCI::Core + TARGET SOCI::PostgreSQL + REQUIRED ) target_include_directories(soci_postgresql @@ -48,7 +65,27 @@ target_include_directories(soci_postgresql target_compile_definitions(soci_postgresql INTERFACE SOCI_POSTGRESQL_NO_LO64) -add_library(SOCI::PostgreSQL ALIAS soci_postgresql) - target_link_libraries(soci_interface INTERFACE SOCI::PostgreSQL) + +install( + TARGETS soci_postgresql + EXPORT SOCIPostgreSQLTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + COMPONENT soci_runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_runtime + NAMELINK_COMPONENT soci_development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + COMPONENT soci_development + FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + COMPONENT soci_development +) +# Generate and install a targets file +install( + EXPORT SOCIPostgreSQLTargets + DESTINATION "${SOCI_INSTALL_CMAKEDIR}" + FILE SOCIPostgreSQLTargets.cmake + NAMESPACE SOCI:: + COMPONENT soci_development +) diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 6c2e98813..9ee81a024 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -39,11 +39,13 @@ soci_public_dependency( NAME SQLite3 DEP_TARGETS SQLite::SQLite3 TARGET SOCI::SQLite3 + REQUIRED ) soci_public_dependency( NAME SOCI::Core DEP_TARGETS SOCI::Core TARGET SOCI::SQLite3 + REQUIRED ) target_include_directories(soci_sqlite3 diff --git a/src/backends/sqlite3/SociSQLite3Config.cmake b/src/backends/sqlite3/SociSQLite3Config.cmake deleted file mode 100644 index d8aa596b9..000000000 --- a/src/backends/sqlite3/SociSQLite3Config.cmake +++ /dev/null @@ -1,6 +0,0 @@ -include(CMakeFindDependencyMacro) - -find_dependency(SOCI::Core) -find_dependency(SQLite3) - -include("${CMAKE_CURRENT_LIST_DIR}/SociSQLite3Targets.cmake") From baecbb21611a5d73aa3f62d8dbc0ad0fe813a978 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 23 Apr 2024 19:33:43 +0200 Subject: [PATCH 44/79] Improve config file --- SOCIConfig.cmake.in | 48 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/SOCIConfig.cmake.in b/SOCIConfig.cmake.in index c320f114b..a3e21dc90 100644 --- a/SOCIConfig.cmake.in +++ b/SOCIConfig.cmake.in @@ -1,11 +1,7 @@ @PACKAGE_INIT@ - -# Ensure that the Core target is always included (and as first component) -list(REMOVE_ITEM SOCI_FIND_COMPONENTS Core) -list(INSERT SOCI_FIND_COMPONENTS 0 Core) - +# Auto-generated stub to handle component-wise dependencies set(__dep_soci_comps "@SOCI_DEPENDENCY_SOCI_COMPONENTS@") set(__dep_names "@SOCI_DEPENDENCY_NAMES@") set(__dep_dep_targets "@SOCI_DEPENDENCY_TARGETS@") @@ -13,6 +9,19 @@ set(__dep_required "@SOCI_DEPENDENCY_REQUIRED@") set(__dep_macro_names "@SOCI_DEPENDENCY_MACRO_NAMES@") set(__dep_components "@SOCI_DEPENDENCY_COMPONENTS@") + +if (NOT DEFINED SOCI_FIND_COMPONENTS OR SOCI_FIND_COMPONENTS STREQUAL "") + # Use all available SOCI components + set(SOCI_FIND_COMPONENTS "${__dep_soci_comps}") + list(REMOVE_DUPLICATES SOCI_FIND_COMPONENTS) + list(TRANSFORM SOCI_FIND_COMPONENTS REPLACE "SOCI::" "") +endif() + +# Ensure that the Core target is always included (and as first component) +list(REMOVE_ITEM SOCI_FIND_COMPONENTS Core) +list(INSERT SOCI_FIND_COMPONENTS 0 Core) + + list(LENGTH __dep_soci_comps __list_size) foreach (__item IN ITEMS __dep_names __dep_dep_targets __dep_required __dep_macro_names __dep_components) list(LENGTH ${__item} __current_size) @@ -34,6 +43,7 @@ foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) # Handle component-specific dependencies set(__link_targets) set(__define_macros) + set(__skip_dependency FALSE) foreach (__i RANGE ${__list_size}) list(GET __dep_soci_comps ${__i} __dep_comp) if (__dep_comp MATCHES "::${__comp}$") @@ -67,16 +77,23 @@ foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) set(__components COMPONENTS ${__components}) endif() + if (${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + set(__quiet "QUIET") + else() + set(__quiet "") + endif() + find_package( ${__dep} ${__components} - ${${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY} + ${__quiet} ) if (NOT ${__dep}_FOUND) if (__required) set(SOCI_FOUND FALSE) set(SOCI_NOT_FOUND_MESSAGE "Unmet dependency '${__dep}' for SOCI component '${__comp}'") + set(__skip_dependency TRUE) endif() continue() endif() @@ -87,6 +104,10 @@ foreach(__comp IN LISTS SOCI_FIND_COMPONENTS) endforeach() unset(__i) + if (__skip_dependency) + continue() + endif() + include("${CMAKE_CURRENT_LIST_DIR}/SOCI${__comp}Targets.cmake") set_property( @@ -118,11 +139,16 @@ unset(__dep_components) check_required_components(SOCI) if (NOT DEFINED SOCI_FOUND OR SOCI_FOUND) - add_library(soci_interface INTERFACE) + add_library(soci_interface INTERFACE) - foreach (__comp IN LISTS SOCI_FIND_COMPONENTS) - target_link_libraries(soci_interface INTERFACE SOCI::${__comp}) - endforeach() + foreach (__comp IN LISTS SOCI_FIND_COMPONENTS) + target_link_libraries(soci_interface INTERFACE SOCI::${__comp}) + endforeach() - add_library(SOCI::SOCI ALIAS soci_interface) + add_library(SOCI::SOCI ALIAS soci_interface) + + if (NOT SOCI_FIND_QUIETLY) + list(JOIN SOCI_FIND_COMPONENTS ", " __components) + message(STATUS "Found SOCI: ${CMAKE_CURRENT_LIST_FILE} (found version \"@PROJECT_VERSION@\") found components: ${__components}") + endif() endif() From eba592679c6c7fd51934ab33dc9fcca6f0b168aa Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 23 Apr 2024 19:40:54 +0200 Subject: [PATCH 45/79] Remove WITH_BOOST/SOCI_BOOST option We don't have any compiled code that depends on whether or not Boost is available. Therefore, we can simply check at configure time whether Boost is found or not and then define the indicator macro accordingly. --- CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f19ff592..4ca85dc25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,13 +43,6 @@ option(SOCI_ASAN "Enable building SOCI with enabled address sanitizers" OFF) cmake_dependent_option(SOCI_LTO "Enable link time optimizations in release builds" ON "LTO_AVAILABLE" OFF) option(SOCI_VISIBILITY "Make all functions hidden by default - this exposes only explicitly exported functions" ON) set(SOCI_LD "" CACHE STRING "Specify a non-default linker") -if (WITH_BOOST) - message(DEPRECATION "Use of WITH_BOOST option is deprecated - use SOCI_BOOST instead") - option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${WITH_BOOST}) -else() - # Default-enable only if Boost has been found before (in a superproject) - option(SOCI_BOOST "Whether to enable Boost-specific bindings" ${Boost_FOUND}) -endif() # Configure LTO for anything but Debug builds (if enabled in the first place) From fa97a6980c325d5d1455eeb706a34644d23f1908 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 23 Apr 2024 19:56:03 +0200 Subject: [PATCH 46/79] Properly set ABI version & runtime search paths --- src/core/CMakeLists.txt | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3b8bcfff0..7fb0fc9d4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -134,22 +134,25 @@ target_link_libraries(soci_core ) if (WIN32) - set(VERSION_SEP "_") + set(ABI_VERSION "${PROJECT_VERSION_MAJOR}_${PROJECT_VERSION_MINOR}") elseif(UNIX) - set(VERSION_SEP ".") -else() - message(WARNING "No known ABI version scheme for current platform") - set(VERSION_SEP "-") + # Use SOVERSION, which is only the major version + set(ABI_VERSION "${PROJECT_VERSION_MAJOR}") endif() target_compile_definitions(soci_core PRIVATE DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" - SOCI_LIB_PREFIX="${SOCI_LIB_PREFIX}" - SOCI_LIB_SUFFIX="${SOCI_LIB_SUFFIX}" - SOCI_DEBUG_SUFFIX="${SOCI_DEBUG_SUFFIX}" - SOCI_ABI_VERSION="${PROJECT_VERSION_MAJOR}${VERSION_SEP}${PROJECT_VERSION_MINOR}" + SOCI_LIB_PREFIX="${CMAKE_SHARED_LIBRARY_PREFIX}soci_" + SOCI_LIB_SUFFIX="${CMAKE_SHARED_LIBRARY_SUFFIX}" + SOCI_DEBUG_SUFFIX="${CMAKE_DEBUG_POSTFIX}" ) +if (DEFINED ABI_VERSION) + target_compile_definitions(soci_core + PRIVATE + SOCI_ABI_VERSION="${ABI_VERSION}" + ) +endif() install( From f9b1805fc31bc64e979ab5c67774f2db93ebba7e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 25 Apr 2024 19:02:25 +0200 Subject: [PATCH 47/79] Move template specialization to cpp file --- tests/common-tests.cpp | 10 +++++++++- tests/common-tests.h | 8 +------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/common-tests.cpp b/tests/common-tests.cpp index 23eee449f..15e8a6837 100644 --- a/tests/common-tests.cpp +++ b/tests/common-tests.cpp @@ -6172,6 +6172,15 @@ void checkEqualPadded(const std::string& padded_str, const std::string& expected } } +template<> +void check(soci::Roundtrip const &val) +{ + CHECK(val.inType == val.outType); + CHECK(std::fpclassify(val.inVal) == std::fpclassify(val.outVal)); + if (std::isnormal(val.inVal) && std::isnormal(val.outVal)) + CHECK_THAT(val.inVal, Catch::Matchers::WithinRel(val.outVal)); +} + } // namespace tests } // namespace soci @@ -6208,4 +6217,3 @@ int main(int argc, char** argv) return Catch::Session().run(argc, argv); } - diff --git a/tests/common-tests.h b/tests/common-tests.h index a1c692249..7c711041a 100644 --- a/tests/common-tests.h +++ b/tests/common-tests.h @@ -93,13 +93,7 @@ void check(soci::Roundtrip const &val) } template<> -void check(soci::Roundtrip const &val) -{ - CHECK(val.inType == val.outType); - CHECK(std::fpclassify(val.inVal) == std::fpclassify(val.outVal)); - if (std::isnormal(val.inVal) && std::isnormal(val.outVal)) - CHECK_THAT(val.inVal, Catch::Matchers::WithinRel(val.outVal)); -} +void check(soci::Roundtrip const &val); template void test_roundtrip(soci::session &sql, soci::db_type inputType, T inputVal) From f4af35509d653376966e4f05fff7c3378070d9f0 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 25 Apr 2024 19:20:01 +0200 Subject: [PATCH 48/79] Fix using macro as function --- cmake/soci_utils.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/soci_utils.cmake b/cmake/soci_utils.cmake index c234e07a6..a8f41e094 100644 --- a/cmake/soci_utils.cmake +++ b/cmake/soci_utils.cmake @@ -84,7 +84,7 @@ endif() # successful find_package call # - is the name of the ALIAS target to link the found dependency to # - are the names of specific components of the dependency -macro(soci_public_dependency) +function(soci_public_dependency) set(FLAGS "REQUIRED") set(ONE_VAL_OPTIONS "TARGET" "NAME") set(MULTI_VAL_OPTIONS "COMPONENTS" "MACRO_NAMES" "DEP_TARGETS") @@ -175,6 +175,6 @@ macro(soci_public_dependency) # -> We put the logic of when they should be defined into the cmake config files target_compile_definitions("${UNDERLYING_TARGET}" PUBLIC "$") endforeach() -endmacro() +endfunction() set(SOCI_UTILS_ALREADY_INCLUDED TRUE) From e255daad2cd693795014c45e2cebe460125263c9 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 25 Apr 2024 19:38:17 +0200 Subject: [PATCH 49/79] CI: Don't use no longer existent WITH_BOOST option --- scripts/ci/build_oracle.sh | 1 - scripts/ci/common.sh | 7 ------- 2 files changed, 8 deletions(-) diff --git a/scripts/ci/build_oracle.sh b/scripts/ci/build_oracle.sh index a5e235fcc..95d537a3f 100755 --- a/scripts/ci/build_oracle.sh +++ b/scripts/ci/build_oracle.sh @@ -6,7 +6,6 @@ source ${SOCI_SOURCE_DIR}/scripts/ci/common.sh cmake ${SOCI_DEFAULT_CMAKE_OPTIONS} \ - -DWITH_BOOST=OFF \ -DSOCI_ORACLE=ON \ -DSOCI_ORACLE_TEST_CONNSTR:STRING="service=localhost/XE user=travis password=travis" \ .. diff --git a/scripts/ci/common.sh b/scripts/ci/common.sh index f605268e7..fbe79c8f7 100644 --- a/scripts/ci/common.sh +++ b/scripts/ci/common.sh @@ -59,13 +59,6 @@ if [ -n "${SOCI_CXXSTD}" ]; then SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DCMAKE_CXX_STANDARD=${SOCI_CXXSTD}" fi -if [ -n "${WITH_BOOST}" ]; then - SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DWITH_BOOST=${WITH_BOOST}" -else - # Build Boost bindings by default - SOCI_COMMON_CMAKE_OPTIONS="$SOCI_COMMON_CMAKE_OPTIONS -DWITH_BOOST=ON" -fi - if [ -n "${SOCI_BUILD_STATIC}" ]; then SOCI_COMMON_CMAKE_OPTIONS="${SOCI_COMMON_CMAKE_OPTIONS} -DSOCI_SHARED=OFF" fi From 9e123c6744e35ab4924ef76b355a8281d6974440 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 25 Apr 2024 19:46:12 +0200 Subject: [PATCH 50/79] Fix LTO in debug builds --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ca85dc25..10fd552ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ set(SOCI_LD "" CACHE STRING "Specify a non-default linker") # Configure LTO for anything but Debug builds (if enabled in the first place) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${SOCI_LTO}) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_Debug OFF) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF) if (SOCI_VISIBILITY) set(CMAKE_CXX_VISIBILITY_PRESET hidden) From 8bceabec5ae3c4a04c6e36f7d1dabaf142cddf7e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 1 May 2024 12:48:53 +0200 Subject: [PATCH 51/79] Fix macro name --- src/core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7fb0fc9d4..c2ad6eb55 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -145,7 +145,7 @@ target_compile_definitions(soci_core DEFAULT_BACKENDS_PATH="${CMAKE_INSTALL_FULL_LIBDIR}" SOCI_LIB_PREFIX="${CMAKE_SHARED_LIBRARY_PREFIX}soci_" SOCI_LIB_SUFFIX="${CMAKE_SHARED_LIBRARY_SUFFIX}" - SOCI_DEBUG_SUFFIX="${CMAKE_DEBUG_POSTFIX}" + SOCI_DEBUG_POSTFIX="${CMAKE_DEBUG_POSTFIX}" ) if (DEFINED ABI_VERSION) target_compile_definitions(soci_core From d611b7db62082a7712a790a26de3d150df5bcf2a Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 1 May 2024 14:46:39 +0200 Subject: [PATCH 52/79] AppVeyor: Use actually available Boost versions --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b2b0cf831..883d8db41 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ configuration: Release environment: matrix: - G: "Visual Studio 17 2022" - BOOST_ROOT: C:\Libraries\boost_1_77_0 + BOOST_ROOT: C:\Libraries\boost_1_84_0 MSSQL_VER: 2019 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\13 POSTGRESQL_VER: 13 @@ -17,7 +17,7 @@ environment: MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - G: "Visual Studio 16 2019" - BOOST_ROOT: C:\Libraries\boost_1_73_0 + BOOST_ROOT: C:\Libraries\boost_1_77_0 MSSQL_VER: 2017 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\12 POSTGRESQL_VER: 12 From 1efdcce63b8914e915ea21ce0a169afad0c6e6e7 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 1 May 2024 14:46:51 +0200 Subject: [PATCH 53/79] AppVeyor: Use more up-to-date PostgreSQL --- appveyor.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 883d8db41..30bd192d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,32 +11,32 @@ environment: - G: "Visual Studio 17 2022" BOOST_ROOT: C:\Libraries\boost_1_84_0 MSSQL_VER: 2019 - POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\13 - POSTGRESQL_VER: 13 + POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\15 + POSTGRESQL_VER: 15 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - G: "Visual Studio 16 2019" BOOST_ROOT: C:\Libraries\boost_1_77_0 MSSQL_VER: 2017 - POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\12 - POSTGRESQL_VER: 12 + POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\13 + POSTGRESQL_VER: 13 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - G: "Visual Studio 15 2017 Win64" BOOST_ROOT: C:\Libraries\boost_1_69_0 MSSQL_VER: 2016 - POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\11 - POSTGRESQL_VER: 11 + POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\12 + POSTGRESQL_VER: 12 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - G: "Visual Studio 14 2015 Win64" BOOST_ROOT: C:\Libraries\boost_1_60_0 MSSQL_VER: 2014 - POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\10 - POSTGRESQL_VER: 10 + POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\11 + POSTGRESQL_VER: 11 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 From b111ec105c0d5c94d580f7eddfe6128fd5f1a2ac Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Wed, 1 May 2024 15:00:57 +0200 Subject: [PATCH 54/79] AppVeyor: Properly locate SQLite --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 30bd192d1..a799340d0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -114,7 +114,7 @@ build_script: - cd C:\projects\soci - mkdir build - cd build - - cmake .. -G"%G%" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_VERBOSE_MAKEFILE=ON -DSOCI_ENABLE_WERROR=ON -DSQLITE3_INCLUDE_DIR=%SQLITE_ROOT%/include -DSQLITE3_LIBRARY=%SQLITE3_LIBRARY% + - cmake .. -G"%G%" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_VERBOSE_MAKEFILE=ON -DSOCI_ENABLE_WERROR=ON -DCMAKE_PREFIX_PATH=%SQLITE_ROOT% -DSQLite3_LIBRARY=%SQLITE3_LIBRARY% - cmake --build . --config %CONFIGURATION% -- %BUILD_TOOL_OPTIONS% test_script: From 7bcdd9a63fd1a4190ec4d812e67dc8ba0e68a597 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 19 May 2024 11:42:45 +0200 Subject: [PATCH 55/79] Ensure MySQL is found in vcpkg context --- cmake/find_modules/FindMySQL.cmake | 14 ++++++++------ include/soci/mysql/soci-mysql.h | 4 ++-- src/backends/mysql/CMakeLists.txt | 28 +++++++++++++++++++--------- tests/mysql/mysql_tests.cpp | 5 +++-- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/cmake/find_modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake index a3beb3ecc..e6588600e 100644 --- a/cmake/find_modules/FindMySQL.cmake +++ b/cmake/find_modules/FindMySQL.cmake @@ -15,7 +15,7 @@ include(CheckCXXSourceCompiles) if(WIN32) - find_path(MySQL_INCLUDE_DIRS mysql.h + find_path(MySQL_INCLUDE_DIRS mysql/mysql.h PATHS $ENV{MYSQL_INCLUDE_DIR} $ENV{MYSQL_INCLUDE_DIRS} @@ -26,7 +26,7 @@ if(WIN32) $ENV{ProgramW6432}/MySQL/*/include ) else() - find_path(MySQL_INCLUDE_DIRS mysql.h + find_path(MySQL_INCLUDE_DIRS mysql/mysql.h PATHS $ENV{MYSQL_INCLUDE_DIR} $ENV{MYSQL_INCLUDE_DIRS} @@ -93,7 +93,9 @@ find_package_handle_standard_args(MySQL REQUIRED_VARS MySQL_LIBRARIES MySQL_INCLUDE_DIRS ) -add_library(MySQL INTERFACE) -target_link_libraries(MySQL INTERFACE ${MySQL_LIBRARIES}) -target_include_directories(MySQL INTERFACE ${MySQL_INCLUDE_DIRS}) -add_library(MySQL::MySQL ALIAS MySQL) +if (MySQL_FOUND) + add_library(MySQL INTERFACE) + target_link_libraries(MySQL INTERFACE ${MySQL_LIBRARIES}) + target_include_directories(MySQL INTERFACE ${MySQL_INCLUDE_DIRS}) + add_library(MySQL::MySQL ALIAS MySQL) +endif() diff --git a/include/soci/mysql/soci-mysql.h b/include/soci/mysql/soci-mysql.h index 08736e38c..af314049d 100644 --- a/include/soci/mysql/soci-mysql.h +++ b/include/soci/mysql/soci-mysql.h @@ -35,8 +35,8 @@ SOCI_GCC_WARNING_SUPPRESS(pedantic) #endif -#include // MySQL Client -#include // MySQL Error codes +#include // MySQL Client +#include // MySQL Error codes #ifdef SOCI_GCC_WARNING_RESTORE SOCI_GCC_WARNING_RESTORE(pedantic) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 49b08bf58..2e3cb951c 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -1,15 +1,25 @@ -if (SOCI_MYSQL_AS_AVAILABLE) - set(REQUIRED_FLAG "") -else() - set(REQUIRED_FLAG "REQUIRED") -endif() +option(SOCI_MYSQL_VCPKG_ONLY "Whether to look for the MySQL lib only as a vcpkg package" OFF) -find_package(MySQL ${REQUIRED_FLAG}) +if (NOT SOCI_MYSQL_VCPKG_ONLY) + find_package(MySQL) +endif() if (NOT MySQL_FOUND) - message(STATUS "Disabling MySQL backend as the required dependencies were not found") - set(SOCI_MYSQL OFF CACHE BOOL "" FORCE) - return() + # The MySQL package has a different name when using vcpkg + if (DEFINED VCPKG_TARGET_TRIPLET) + find_package(unofficial-libmysql) + endif() + if (unofficial-libmysql_FOUND) + add_library(MySQL::MySQL ALIAS unofficial::libmysql::libmysql) + message(STATUS "Found MySQL as a vcpkg package") + else() + if (NOT SOCI_MYSQL_AS_AVAILABLE) + message(FATAL_ERROR "Unable to find MySQL") + endif() + message(STATUS "Disabling MySQL backend as the required dependencies were not found") + set(SOCI_MYSQL OFF CACHE BOOL "" FORCE) + return() + endif() endif() add_library(soci_mysql diff --git a/tests/mysql/mysql_tests.cpp b/tests/mysql/mysql_tests.cpp index af9e8ebd8..f075b9e95 100644 --- a/tests/mysql/mysql_tests.cpp +++ b/tests/mysql/mysql_tests.cpp @@ -23,10 +23,11 @@ #include #include #include -#include -#include #include +#include +#include + std::string connectString; backend_factory const &backEnd = *soci::factory_mysql(); From 24feb969e8b9d33e80eb6b6588249723a778627e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 09:56:46 +0200 Subject: [PATCH 56/79] Disable Boost autolinking on Windows Using the autolink feature seems to lead to linker errors for the Boost date_time library with older versions of Boost and/or older versions of Visual Studio. --- src/core/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c2ad6eb55..3f4544df6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -114,14 +114,14 @@ endif() soci_public_dependency( NAME Boost - DEP_TARGETS Boost::boost Boost::date_time + DEP_TARGETS Boost::boost Boost::date_time Boost::disable_autolinking COMPONENTS date_time MACRO_NAMES SOCI_HAVE_BOOST SOCI_HAVE_BOOST_DATE_TIME TARGET SOCI::Core ) soci_public_dependency( NAME Boost - DEP_TARGETS Boost::boost + DEP_TARGETS Boost::boost Boost::disable_autolinking MACRO_NAMES SOCI_HAVE_BOOST TARGET SOCI::Core ) From 10982080d51e34d6cc5ae6cb96414945bb182928 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 09:33:59 +0200 Subject: [PATCH 57/79] AppVeyor: Use Ninja for faster builds --- appveyor.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a799340d0..bc6478415 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,39 +8,40 @@ configuration: Release environment: matrix: - - G: "Visual Studio 17 2022" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_84_0 MSSQL_VER: 2019 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\15 POSTGRESQL_VER: 15 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - - G: "Visual Studio 16 2019" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_77_0 MSSQL_VER: 2017 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\13 POSTGRESQL_VER: 13 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - - G: "Visual Studio 15 2017 Win64" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_69_0 MSSQL_VER: 2016 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\12 POSTGRESQL_VER: 12 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - G: "Visual Studio 14 2015 Win64" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_60_0 MSSQL_VER: 2014 POSTGRESQL_ROOT: C:\Program Files\PostgreSQL\11 POSTGRESQL_VER: 11 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - G: "MinGW Makefiles" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + G: "MinGW Makefiles" MINGW_BIN: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin MSSQL_VER: 2014 BOOST_ROOT: C:\Libraries\boost_1_60_0 @@ -48,7 +49,6 @@ environment: POSTGRESQL_VER: 9.4 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 install: # Start these ones here as we can't specify the service name dynamically above. @@ -92,7 +92,7 @@ before_build: else { $env:SQLITE3_LIBRARY = $env:SQLITE_ROOT + '/lib/sqlite3-static.lib' - $env:BUILD_TOOL_OPTIONS = '/m' + $env:BUILD_TOOL_OPTIONS = '' } - cd C:\projects\sqlite\src - mkdir build From 2de4a697bc3e21914ae67e74b80e5b00680f7c2f Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 12:35:26 +0200 Subject: [PATCH 58/79] Mark dependency's includes as SYSTEM That way no compiler should ever emit a warning about something in the associated header files and thus we don't have to jump through hoops in order to avoid warnings that we can't do anything about. --- cmake/find_modules/FindDB2.cmake | 2 +- cmake/find_modules/FindFirebird.cmake | 2 +- cmake/find_modules/FindMySQL.cmake | 2 +- cmake/find_modules/FindOracle.cmake | 2 +- include/soci/mysql/soci-mysql.h | 14 -------------- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/cmake/find_modules/FindDB2.cmake b/cmake/find_modules/FindDB2.cmake index 70222565a..b786d4374 100644 --- a/cmake/find_modules/FindDB2.cmake +++ b/cmake/find_modules/FindDB2.cmake @@ -96,5 +96,5 @@ find_package_handle_standard_args(DB2 add_library(DB2 INTERFACE) target_link_libraries(DB2 INTERFACE ${DB2_LIBRARIES}) -target_include_directories(DB2 INTERFACE ${DB2_INCLUDE_DIRS}) +target_include_directories(DB2 SYSTEM INTERFACE ${DB2_INCLUDE_DIRS}) add_library(DB2::DB2 ALIAS DB2) diff --git a/cmake/find_modules/FindFirebird.cmake b/cmake/find_modules/FindFirebird.cmake index 594b69908..9a55a5742 100644 --- a/cmake/find_modules/FindFirebird.cmake +++ b/cmake/find_modules/FindFirebird.cmake @@ -35,5 +35,5 @@ find_package_handle_standard_args(Firebird add_library(Firebird INTERFACE) target_link_libraries(Firebird INTERFACE ${Firebird_LIBRARIES}) -target_include_directories(Firebird INTERFACE ${Firebird_INCLUDE_DIRS}) +target_include_directories(Firebird SYSTEM INTERFACE ${Firebird_INCLUDE_DIRS}) add_library(Firebird::Firebird ALIAS Firebird) diff --git a/cmake/find_modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake index e6588600e..bf70f56ac 100644 --- a/cmake/find_modules/FindMySQL.cmake +++ b/cmake/find_modules/FindMySQL.cmake @@ -96,6 +96,6 @@ find_package_handle_standard_args(MySQL if (MySQL_FOUND) add_library(MySQL INTERFACE) target_link_libraries(MySQL INTERFACE ${MySQL_LIBRARIES}) - target_include_directories(MySQL INTERFACE ${MySQL_INCLUDE_DIRS}) + target_include_directories(MySQL SYSTEM INTERFACE ${MySQL_INCLUDE_DIRS}) add_library(MySQL::MySQL ALIAS MySQL) endif() diff --git a/cmake/find_modules/FindOracle.cmake b/cmake/find_modules/FindOracle.cmake index 1319eed88..64ced2106 100644 --- a/cmake/find_modules/FindOracle.cmake +++ b/cmake/find_modules/FindOracle.cmake @@ -92,5 +92,5 @@ endif() add_library(Oracle INTERFACE) target_link_libraries(Oracle INTERFACE ${Oracle_LIBRARIES}) -target_include_directories(Oracle INTERFACE ${Oracle_INCLUDE_DIRS}) +target_include_directories(Oracle SYSTEM INTERFACE ${Oracle_INCLUDE_DIRS}) add_library(Oracle::Oracle ALIAS Oracle) diff --git a/include/soci/mysql/soci-mysql.h b/include/soci/mysql/soci-mysql.h index af314049d..a40c3800f 100644 --- a/include/soci/mysql/soci-mysql.h +++ b/include/soci/mysql/soci-mysql.h @@ -23,24 +23,10 @@ #include // SOCKET #endif // _WIN32 -// Some version of mysql.h contain trailing comma in an enum declaration that -// trigger -Wpedantic, so suppress it as there is nothing to be done about it -// using the macros defined in our private soci-compiler.h header, that we can -// only include when building SOCI itself. -#ifdef SOCI_MYSQL_SOURCE - #include "soci-compiler.h" -#endif - -#ifdef SOCI_GCC_WARNING_SUPPRESS - SOCI_GCC_WARNING_SUPPRESS(pedantic) -#endif #include // MySQL Client #include // MySQL Error codes -#ifdef SOCI_GCC_WARNING_RESTORE - SOCI_GCC_WARNING_RESTORE(pedantic) -#endif #include From f38e2222648ad3079b8840dcaf22d4c4ca4c77ac Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 12:51:19 +0200 Subject: [PATCH 59/79] Restrict to only using target-specific compiler flags --- cmake/soci_compiler_options.cmake | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmake/soci_compiler_options.cmake b/cmake/soci_compiler_options.cmake index 77f7f5af2..973406e5a 100644 --- a/cmake/soci_compiler_options.cmake +++ b/cmake/soci_compiler_options.cmake @@ -14,11 +14,8 @@ if (MSVC) target_compile_definitions(soci_compiler_interface INTERFACE _CRT_NONSTDC_NO_WARNING) target_compile_definitions(soci_compiler_interface INTERFACE _SCL_SECURE_NO_WARNINGS) - if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") - string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /we4266") - endif() + # Configure warnings + target_compile_options(soci_compiler_interface INTERFACE "/W4" "/we4266") if (SOCI_ENABLE_WERROR) target_compile_options(soci_compiler_interface INTERFACE "/WX") @@ -47,7 +44,6 @@ else() target_compile_options(soci_compiler_interface INTERFACE "-fsanitize=address") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SOCI_GCC_CLANG_COMMON_FLAGS} ") if (CMAKE_COMPILER_IS_GNUCXX) if (NOT (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")) target_compile_options(soci_compiler_interface INTERFACE "-Wno-variadic-macros") From 81cd483b1b3abfcdebbe21eb77bd1f4ac1a79f9d Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 13:32:50 +0200 Subject: [PATCH 60/79] Base macro defs on platform not compiler --- cmake/soci_compiler_options.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/soci_compiler_options.cmake b/cmake/soci_compiler_options.cmake index 973406e5a..ebfc59b2c 100644 --- a/cmake/soci_compiler_options.cmake +++ b/cmake/soci_compiler_options.cmake @@ -8,12 +8,14 @@ add_library(soci_compiler_interface INTERFACE) option(SOCI_ENABLE_WERROR "Enables turning compiler warnings into errors" OFF) -if (MSVC) +if (WIN32) target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_DEPRECATE) target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_WARNINGS) target_compile_definitions(soci_compiler_interface INTERFACE _CRT_NONSTDC_NO_WARNING) target_compile_definitions(soci_compiler_interface INTERFACE _SCL_SECURE_NO_WARNINGS) +endif() +if (MSVC) # Configure warnings target_compile_options(soci_compiler_interface INTERFACE "/W4" "/we4266") From d5f70a117566cc8c5111530a8c7e8ae9f13faaaf Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 13:56:39 +0200 Subject: [PATCH 61/79] Tame the Windows API headers --- cmake/soci_compiler_options.cmake | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmake/soci_compiler_options.cmake b/cmake/soci_compiler_options.cmake index ebfc59b2c..d14278c95 100644 --- a/cmake/soci_compiler_options.cmake +++ b/cmake/soci_compiler_options.cmake @@ -9,10 +9,21 @@ add_library(soci_compiler_interface INTERFACE) option(SOCI_ENABLE_WERROR "Enables turning compiler warnings into errors" OFF) if (WIN32) - target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_DEPRECATE) - target_compile_definitions(soci_compiler_interface INTERFACE _CRT_SECURE_NO_WARNINGS) - target_compile_definitions(soci_compiler_interface INTERFACE _CRT_NONSTDC_NO_WARNING) - target_compile_definitions(soci_compiler_interface INTERFACE _SCL_SECURE_NO_WARNINGS) + target_compile_definitions(soci_compiler_interface + INTERFACE + _CRT_SECURE_NO_DEPRECATE + _CRT_SECURE_NO_WARNINGS + _CRT_NONSTDC_NO_WARNING + _SCL_SECURE_NO_WARNINGS + ) + + # Prevent the Windows header files from defining annoying macros + # and also cut down on the definitions in general + target_compile_definitions(soci_compiler_interface + INTERFACE + NOMINMAX + WIN32_LEAN_AND_MEAN + ) endif() if (MSVC) From 707126437f75feb3e608347c621b8065a65de980 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 13:43:42 +0200 Subject: [PATCH 62/79] AppVeyor: Use MSVC instead of Clang --- appveyor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index bc6478415..adf63206e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ environment: POSTGRESQL_VER: 15 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 + VCVARS_SCRIPT: "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_77_0 @@ -24,6 +25,7 @@ environment: POSTGRESQL_VER: 13 MYSQL_VER: 80 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 8.0 + VCVARS_SCRIPT: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_69_0 @@ -32,6 +34,7 @@ environment: POSTGRESQL_VER: 12 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 + VCVARS_SCRIPT: "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Auxiliary/Build" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 G: "Ninja" BOOST_ROOT: C:\Libraries\boost_1_60_0 @@ -40,6 +43,7 @@ environment: POSTGRESQL_VER: 11 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 + VCVARS_SCRIPT: "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 G: "MinGW Makefiles" MINGW_BIN: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin @@ -49,6 +53,7 @@ environment: POSTGRESQL_VER: 9.4 MYSQL_VER: 57 MYSQL_DIR: C:\Program Files\MySql\MySQL Server 5.7 + VCVARS_SCRIPT: "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC" install: # Start these ones here as we can't specify the service name dynamically above. @@ -93,6 +98,15 @@ before_build: { $env:SQLITE3_LIBRARY = $env:SQLITE_ROOT + '/lib/sqlite3-static.lib' $env:BUILD_TOOL_OPTIONS = '' + # Set up Visual Studio environment (MSVC compiler etc.) + # Works by calling the respective bat script and then copying over the env vars + cd $env:VCVARS_SCRIPT + cmd /c "vcvarsall.bat x64&set" | + foreach { + if ($_ -match "=") { + $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" + } + } } - cd C:\projects\sqlite\src - mkdir build From c4895b34cbc7d3e28cdcbb352f8dee819f8f47e9 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 24 May 2024 19:22:29 +0200 Subject: [PATCH 63/79] remove unnecessary winsock include --- include/soci/mysql/soci-mysql.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/soci/mysql/soci-mysql.h b/include/soci/mysql/soci-mysql.h index a40c3800f..360cc3b40 100644 --- a/include/soci/mysql/soci-mysql.h +++ b/include/soci/mysql/soci-mysql.h @@ -19,10 +19,6 @@ #include #include -#ifdef _WIN32 -#include // SOCKET -#endif // _WIN32 - #include // MySQL Client #include // MySQL Error codes From 9231cd02f9e8e0585d49a6ef5c1c84fb1ec1246e Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Fri, 24 May 2024 19:23:25 +0200 Subject: [PATCH 64/79] Fix Windows build with UNICODE macro --- src/core/backend-loader.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/backend-loader.cpp b/src/core/backend-loader.cpp index 9bc308ea4..42631e109 100644 --- a/src/core/backend-loader.cpp +++ b/src/core/backend-loader.cpp @@ -31,11 +31,7 @@ typedef HMODULE soci_handler_t; #define UNLOCK(x) LeaveCriticalSection(x) #define MUTEX_INIT(x) InitializeCriticalSection(x) #define MUTEX_DEST(x) DeleteCriticalSection(x) -#ifdef _UNICODE #define DLOPEN(x) LoadLibraryA(x) -#else -#define DLOPEN(x) LoadLibrary(x) -#endif #define DLCLOSE(x) FreeLibrary(x) #define DLSYM(x, y) GetProcAddress(x, y) From 1252be7017b05b142ae6265cb975fd8d3269dc59 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 20 May 2024 12:14:37 +0200 Subject: [PATCH 65/79] Get rid of SOCI_DUMMY_RETURN --- include/soci/soci-platform.h | 10 ---------- src/core/logger.cpp | 6 +----- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/include/soci/soci-platform.h b/include/soci/soci-platform.h index e662565b8..7079b54b6 100644 --- a/include/soci/soci-platform.h +++ b/include/soci/soci-platform.h @@ -165,16 +165,6 @@ private: \ // about initializing variables unnecessarily. #define SOCI_DUMMY_INIT(x) (x) -// And this one can be used to return after calling a "[[noreturn]]" function. -// Here the problem is that MSVC complains about unreachable code in this case -// (but only in release builds, where optimizations are enabled), while other -// compilers complain about missing return statement without it. -#if defined(_MSC_VER) && defined(NDEBUG) - #define SOCI_DUMMY_RETURN(x) -#else - #define SOCI_DUMMY_RETURN(x) return x -#endif - #define SOCI_OS_LINUX 0x0001 #define SOCI_OS_FREE_BSD 0x0002 #define SOCI_OS_APPLE 0x0003 diff --git a/src/core/logger.cpp b/src/core/logger.cpp index 6e9e1e08c..e8524d107 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -15,7 +15,7 @@ namespace // anonymous { // Helper to throw from not implemented logger_impl methods. -void throw_not_supported() +[[noreturn]] void throw_not_supported() { throw soci_error("Legacy method not supported by this logger."); } @@ -46,15 +46,11 @@ void logger_impl::set_stream(std::ostream *) std::ostream * logger_impl::get_stream() const { throw_not_supported(); - - SOCI_DUMMY_RETURN(NULL); } std::string logger_impl::get_last_query() const { throw_not_supported(); - - SOCI_DUMMY_RETURN(std::string()); } logger::logger(logger_impl * impl) From 1e0e1ddf26f13b90a789df822da514e11b52f08f Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Mon, 27 May 2024 20:20:27 +0200 Subject: [PATCH 66/79] Add override specs --- src/core/session.cpp | 10 +++++----- tests/common-tests.cpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/session.cpp b/src/core/session.cpp index fab538f8e..f2e1bf34d 100644 --- a/src/core/session.cpp +++ b/src/core/session.cpp @@ -35,7 +35,7 @@ class standard_logger_impl : public logger_impl logStream_ = NULL; } - virtual void start_query(std::string const & query) + virtual void start_query(std::string const & query) override { if (logStream_ != NULL) { @@ -45,23 +45,23 @@ class standard_logger_impl : public logger_impl lastQuery_ = query; } - virtual void set_stream(std::ostream * s) + virtual void set_stream(std::ostream * s) override { logStream_ = s; } - virtual std::ostream * get_stream() const + virtual std::ostream * get_stream() const override { return logStream_; } - virtual std::string get_last_query() const + virtual std::string get_last_query() const override { return lastQuery_; } private: - virtual logger_impl* do_clone() const + virtual logger_impl* do_clone() const override { return new standard_logger_impl; } diff --git a/tests/common-tests.cpp b/tests/common-tests.cpp index 15e8a6837..dce0b8f83 100644 --- a/tests/common-tests.cpp +++ b/tests/common-tests.cpp @@ -5934,13 +5934,13 @@ TEST_CASE_METHOD(common_tests, "Logger", "[core][log]") { } - virtual void start_query(std::string const & query) + virtual void start_query(std::string const & query) override { m_logbuf.push_back(query); } private: - virtual logger_impl* do_clone() const + virtual logger_impl* do_clone() const override { return new test_log_impl(m_logbuf); } From c616644ca349013ac4c3bf16d7a272f20c72651d Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Thu, 30 May 2024 17:23:34 +0200 Subject: [PATCH 67/79] Make include less confusing --- tests/mysql/mysql_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mysql/mysql_tests.cpp b/tests/mysql/mysql_tests.cpp index f075b9e95..d23e7acba 100644 --- a/tests/mysql/mysql_tests.cpp +++ b/tests/mysql/mysql_tests.cpp @@ -8,7 +8,7 @@ #include -#include "mysql/mysql_tests.h" +#include "mysql_tests.h" #include "soci/soci.h" #include "soci-compiler.h" From e8cc7011fa1aa612b2c5e7ecface47250c8638f8 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Tue, 28 May 2024 19:43:59 +0200 Subject: [PATCH 68/79] Improve FindMySQL --- cmake/find_modules/FindMySQL.cmake | 243 +++++++++++++++++++---------- include/soci/mysql/soci-mysql.h | 5 + src/backends/mysql/CMakeLists.txt | 28 ++-- tests/mysql/mysql_tests.cpp | 5 + 4 files changed, 181 insertions(+), 100 deletions(-) diff --git a/cmake/find_modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake index bf70f56ac..af445e18a 100644 --- a/cmake/find_modules/FindMySQL.cmake +++ b/cmake/find_modules/FindMySQL.cmake @@ -1,101 +1,182 @@ # - Try to find MariaDB / MySQL library # Find the MySQL includes and client library # This module defines -# MySQL_INCLUDE_DIRS -# MySQL_LIBRARIES, the libraries needed to use MySQL. -# MySQL_LIB_DIR, path to the MySQL_LIBRARIES -# MySQL_FOUND, If false, do not try to use MySQL. - -# Copyright (c) 2006-2008, JarosÅ‚aw Staniek -# Copyright (c) 2023 Vadim Zeitline (MariaDB support) -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -include(CheckCXXSourceCompiles) - -if(WIN32) - find_path(MySQL_INCLUDE_DIRS mysql/mysql.h - PATHS - $ENV{MYSQL_INCLUDE_DIR} - $ENV{MYSQL_INCLUDE_DIRS} - $ENV{MYSQL_DIR}/include - $ENV{MYSQL_DIRS}/include - $ENV{ProgramFiles}/MySQL/*/include - $ENV{SystemDrive}/MySQL/*/include - $ENV{ProgramW6432}/MySQL/*/include - ) -else() - find_path(MySQL_INCLUDE_DIRS mysql/mysql.h - PATHS - $ENV{MYSQL_INCLUDE_DIR} - $ENV{MYSQL_INCLUDE_DIRS} - $ENV{MYSQL_DIR}/include - $ENV{MYSQL_DIRS}/include - PATH_SUFFIXES - mariadb - mysql - ) +# MySQL_FOUND +# An interface target MySQL::MySQL to be used in a target_link_libraries call + + +if (DEFINED VCPKG_TARGET_TRIPLET) + # In vcpg the MySQL packages are called unofficial-libmysql + find_package(unofficial-libmysql) + + set(FOUND_VAR "unofficial-libmysql_FOUND") + set(LIBRARY_TARGET "unofficial::libmysql::libmysql") + + if (NOT ${FOUND_VAR}) + find_package(unofficial-libmariadb QUIET) + set(FOUND_VAR "unofficial-libmariadb_FOUND") + set(LIBRARY_TARGET "unofficial::libmariadb::libmariadb") + endif() + + if (${FOUND_VAR}) + set(MySQL_FOUND TRUE) + message(STATUS "Found MySQL via vcpkg installation") + add_library(MySQL::MySQL ALIAS ${LIBRARY_TARGET}) + return() + endif() +endif() + +find_package(PkgConfig QUIET) + +if (PKG_CONFIG_FOUND) + # Try via PkgConfig + pkg_check_modules(MYSQLCLIENT QUIET mysqlclient) + + if (MYSQLCLIENT_FOUND) + if (NOT BUILD_SHARED_LIBS AND MYSQLCLIENT_STATIC_FOUND) + set(PREFIX MYSQLCLIENT_STATIC) + else() + set(PREFIX MYSQLCLIENT) + endif() + + set(MySQL_LIBRARIES ${${PREFIX}_LINK_LIBRARIES}) + set(MySQL_LDFLAGS ${${PREFIX}_LDFLAGS} ${${PREFIX}_LDFLAGS_OTHER}) + set(MySQL_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${PREFIX}_INCLUDE_DIRS_OTHER}) + set(MySQL_CFLAGS ${${PREFIX}_CFLAGS} ${${PREFIX}_CFLAGS_OTHER}) + set(MySQL_VERSION ${MYSQLCLIENT_VERSION}) + endif() endif() -if(WIN32) - if (${CMAKE_BUILD_TYPE}) - string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) - endif() - - # path suffix for debug/release mode - # binary_dist: mysql binary distribution - # build_dist: custom build - if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") - set(binary_dist debug) - set(build_dist Debug) - else() - ADD_DEFINITIONS(-DDBUG_OFF) - set(binary_dist opt) - set(build_dist Release) - endif() - - set(MySQL_LIB_PATHS - $ENV{MYSQL_DIR}/lib/${binary_dist} - $ENV{MYSQL_DIR}/libmysql/${build_dist} - $ENV{MYSQL_DIR}/client/${build_dist} - $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist} - $ENV{SystemDrive}/MySQL/*/lib/${binary_dist} - $ENV{MYSQL_DIR}/lib/opt - $ENV{MYSQL_DIR}/client/release - $ENV{ProgramFiles}/MySQL/*/lib/opt - $ENV{SystemDrive}/MySQL/*/lib/opt - $ENV{ProgramW6432}/MySQL/*/lib - ) - find_library(MySQL_LIBRARIES NAMES libmysql - PATHS - ${MySQL_LIB_PATHS} - ) -else() - set(MySQL_LIB_PATHS - $ENV{MySQL_DIR}/lib - PATH_SUFFIXES - mariadb - mysql - ) - find_library(MySQL_LIBRARIES NAMES mariadbclient mysqlclient - PATHS - ${MySQL_LIB_PATHS} - ) +if (NOT MySQL_LIBRARIES) + # Try using config exe + find_program(CONFIG_EXE + NAMES + mysql_config mariadb_config + PATHS + $ENV{MYSQL_DIR} + $ENV{MYSQL_DIRS} + $ENV{ProgramFiles}/MySQL/ + $ENV{ProgramFiles}/MariaDB/ + ) + + if (CONFIG_EXE) + execute_process(COMMAND ${CONFIG_EXE} --include OUTPUT_VARIABLE MySQL_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${CONFIG_EXE} --libs OUTPUT_VARIABLE MySQL_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${CONFIG_EXE} --version OUTPUT_VARIABLE MySQL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "'${MySQL_LIBRARIES}'") + endif() endif() -if(MySQL_LIBRARIES) - get_filename_component(MySQL_LIB_DIR ${MySQL_LIBRARIES} PATH) +if (NOT MySQL_LIBRARIES) + message(WARNING "Falling back to manual MySQL search -> this might miss dependencies") + set(MySQL_COMPILE_DEFINITIONS "") + + include(CheckCXXSourceCompiles) + + foreach(TOP_LEVEL_DIR IN ITEMS "mysql/" "") + if(WIN32) + find_path(MySQL_INCLUDE_DIRS ${TOP_LEVEL_DIR}mysql.h + PATHS + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_INCLUDE_DIRS} + $ENV{MYSQL_DIR}/include + $ENV{MYSQL_DIRS}/include + $ENV{ProgramFiles}/MySQL/*/include + $ENV{SystemDrive}/MySQL/*/include + $ENV{ProgramW6432}/MySQL/*/include + ) + else() + find_path(MySQL_INCLUDE_DIRS ${TOP_LEVEL_DIR}mysql.h + PATHS + $ENV{MYSQL_INCLUDE_DIR} + $ENV{MYSQL_INCLUDE_DIRS} + $ENV{MYSQL_DIR}/include + $ENV{MYSQL_DIRS}/include + PATH_SUFFIXES + mariadb + mysql + ) + endif() + + if (MySQL_INCLUDE_DIRS) + if (TOP_LEVEL_DIR STREQUAL "") + list(APPEND MySQL_COMPILE_DEFINITIONS SOCI_MYSQL_DIRECT_INCLUDE) + set(VERSION_FILE "${MySQL_INCLUDE_DIRS}/mysql_version.h") + else() + set(VERSION_FILE "${MySQL_INCLUDE_DIRS}/mysql/mysql_version.h") + endif() + + # Parse out MySQL version + file(READ "${VERSION_FILE}" VERSION_CONTENT) + string(REGEX MATCH "#define[ \t]+LIBMYSQL_VERSION[ \t]+\"([^\"]+)\"" VERSION_CONTENT "${VERSION_CONTENT}") + set(MySQL_VERSION "${CMAKE_MATCH_1}") + + break() + endif() + endforeach() + + if (MySQL_INCLUDE_DIRS) + endif() + + if(WIN32) + if (${CMAKE_BUILD_TYPE}) + string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) + endif() + + # path suffix for debug/release mode + # binary_dist: mysql binary distribution + # build_dist: custom build + if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") + set(binary_dist debug) + set(build_dist Debug) + else() + list(APPEND MySQL_COMPILE_DEFINITIONS DBUG_OFF) + set(binary_dist opt) + set(build_dist Release) + endif() + + set(MySQL_LIB_PATHS + $ENV{MYSQL_DIR}/lib/${binary_dist} + $ENV{MYSQL_DIR}/libmysql/${build_dist} + $ENV{MYSQL_DIR}/client/${build_dist} + $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist} + $ENV{SystemDrive}/MySQL/*/lib/${binary_dist} + $ENV{MYSQL_DIR}/lib/opt + $ENV{MYSQL_DIR}/client/release + $ENV{ProgramFiles}/MySQL/*/lib/opt + $ENV{SystemDrive}/MySQL/*/lib/opt + $ENV{ProgramW6432}/MySQL/*/lib + ) + find_library(MySQL_LIBRARIES NAMES libmysql + PATHS + ${MySQL_LIB_PATHS} + ) + else() + set(MySQL_LIB_PATHS + $ENV{MySQL_DIR}/lib + PATH_SUFFIXES + mariadb + mysql + ) + find_library(MySQL_LIBRARIES NAMES mariadbclient mysqlclient + PATHS + ${MySQL_LIB_PATHS} + ) + endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MySQL REQUIRED_VARS MySQL_LIBRARIES MySQL_INCLUDE_DIRS + VERSION_VAR MySQL_VERSION ) if (MySQL_FOUND) add_library(MySQL INTERFACE) target_link_libraries(MySQL INTERFACE ${MySQL_LIBRARIES}) target_include_directories(MySQL SYSTEM INTERFACE ${MySQL_INCLUDE_DIRS}) + target_compile_options(MySQL INTERFACE ${MySQL_CFLAGS}) + target_link_options(MySQL INTERFACE ${MySQL_LDFLAGS}) + target_compile_definitions(MySQL INTERFACE ${MySQL_COMPILE_DEFINITIONS}) add_library(MySQL::MySQL ALIAS MySQL) endif() diff --git a/include/soci/mysql/soci-mysql.h b/include/soci/mysql/soci-mysql.h index 360cc3b40..b573fc019 100644 --- a/include/soci/mysql/soci-mysql.h +++ b/include/soci/mysql/soci-mysql.h @@ -20,8 +20,13 @@ #include #include +#ifdef SOCI_MYSQL_DIRECT_INCLUDE +#include // MySQL Client +#include // MySQL Error codes +#else #include // MySQL Client #include // MySQL Error codes +#endif #include diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 2e3cb951c..49b08bf58 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -1,25 +1,15 @@ -option(SOCI_MYSQL_VCPKG_ONLY "Whether to look for the MySQL lib only as a vcpkg package" OFF) - -if (NOT SOCI_MYSQL_VCPKG_ONLY) - find_package(MySQL) +if (SOCI_MYSQL_AS_AVAILABLE) + set(REQUIRED_FLAG "") +else() + set(REQUIRED_FLAG "REQUIRED") endif() +find_package(MySQL ${REQUIRED_FLAG}) + if (NOT MySQL_FOUND) - # The MySQL package has a different name when using vcpkg - if (DEFINED VCPKG_TARGET_TRIPLET) - find_package(unofficial-libmysql) - endif() - if (unofficial-libmysql_FOUND) - add_library(MySQL::MySQL ALIAS unofficial::libmysql::libmysql) - message(STATUS "Found MySQL as a vcpkg package") - else() - if (NOT SOCI_MYSQL_AS_AVAILABLE) - message(FATAL_ERROR "Unable to find MySQL") - endif() - message(STATUS "Disabling MySQL backend as the required dependencies were not found") - set(SOCI_MYSQL OFF CACHE BOOL "" FORCE) - return() - endif() + message(STATUS "Disabling MySQL backend as the required dependencies were not found") + set(SOCI_MYSQL OFF CACHE BOOL "" FORCE) + return() endif() add_library(soci_mysql diff --git a/tests/mysql/mysql_tests.cpp b/tests/mysql/mysql_tests.cpp index d23e7acba..a25a32fe2 100644 --- a/tests/mysql/mysql_tests.cpp +++ b/tests/mysql/mysql_tests.cpp @@ -25,8 +25,13 @@ #include #include +#ifdef SOCI_MYSQL_DIRECT_INCLUDE +#include +#include +#else #include #include +#endif std::string connectString; backend_factory const &backEnd = *soci::factory_mysql(); From 8283a389238052c5b3623da6ae8b5481af43aa32 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 2 Jun 2024 16:44:44 +0200 Subject: [PATCH 69/79] Change fatal error to debug message --- cmake/soci_utils.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/soci_utils.cmake b/cmake/soci_utils.cmake index a8f41e094..94b5abe44 100644 --- a/cmake/soci_utils.cmake +++ b/cmake/soci_utils.cmake @@ -160,7 +160,7 @@ function(soci_public_dependency) foreach (TGT IN LISTS PUBLIC_DEP_DEP_TARGETS) if (NOT TARGET "${TGT}") if (FOUND_ONE) - message(FATAL_ERROR "Some but not all targets of a SOCI dependency have been found") + message(DEBUG "The following SOCI dependencies have been found only partially: ${PUBLIC_DEP_DEP_TARGETS}") endif() return() endif() From b6b31bea4e370944c84397ff6b808d0da3bf2dda Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 2 Jun 2024 19:27:07 +0200 Subject: [PATCH 70/79] Prevent compiler flags from propagating to superprojects --- cmake/soci_compiler_options.cmake | 38 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/cmake/soci_compiler_options.cmake b/cmake/soci_compiler_options.cmake index d14278c95..b08cb5153 100644 --- a/cmake/soci_compiler_options.cmake +++ b/cmake/soci_compiler_options.cmake @@ -28,7 +28,11 @@ endif() if (MSVC) # Configure warnings - target_compile_options(soci_compiler_interface INTERFACE "/W4" "/we4266") + target_compile_options(soci_compiler_interface + INTERFACE + "$<$:/W4>" + "$<$:/we4266>" + ) if (SOCI_ENABLE_WERROR) target_compile_options(soci_compiler_interface INTERFACE "/WX") @@ -45,21 +49,21 @@ else() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER}" MATCHES "clang") if(SOCI_ASAN AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) # This can also be used to set a linker flag - target_link_libraries(soci_compiler_interface INTERFACE "-fsanitize=address") - target_compile_options(soci_compiler_interface INTERFACE "-fsanitize=address") + target_link_libraries(soci_compiler_interface INTERFACE "$<$:-fsanitize=address>") + target_compile_options(soci_compiler_interface INTERFACE "$<$:-fsanitize=address>") endif() set(SOCI_USE_STD_FLAGS ON) elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) if (SOCI_ASAN AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) # This can also be used to set a linker flag - target_link_libraries(soci_compiler_interface INTERFACE "-fsanitize=address") - target_compile_options(soci_compiler_interface INTERFACE "-fsanitize=address") + target_link_libraries(soci_compiler_interface INTERFACE "$<$:-fsanitize=address>") + target_compile_options(soci_compiler_interface INTERFACE "$<$:-fsanitize=address>") endif() if (CMAKE_COMPILER_IS_GNUCXX) if (NOT (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")) - target_compile_options(soci_compiler_interface INTERFACE "-Wno-variadic-macros") + target_compile_options(soci_compiler_interface INTERFACE "$<$:-Wno-variadic-macros>") endif() endif() @@ -71,17 +75,17 @@ else() if (SOCI_USE_STD_FLAGS) target_compile_options(soci_compiler_interface INTERFACE - "-pedantic" - "-Wno-error=parentheses" - "-Wall" - "-Wextra" - "-Wpointer-arith" - "-Wcast-align" - "-Wcast-qual" - "-Wfloat-equal" - "-Woverloaded-virtual" - "-Wredundant-decls" - "-Wno-long-long" + "$<$:-pedantic>" + "$<$:-Wno-error=parentheses>" + "$<$:-Wall>" + "$<$:-Wextra>" + "$<$:-Wpointer-arith>" + "$<$:-Wcast-align>" + "$<$:-Wcast-qual>" + "$<$:-Wfloat-equal>" + "$<$:-Woverloaded-virtual>" + "$<$:-Wredundant-decls>" + "$<$:-Wno-long-long>" ) endif() endif() From 08eb273e63d72f478b31bd3eab9230103b6c6c55 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 2 Jun 2024 19:42:21 +0200 Subject: [PATCH 71/79] Link core to DL libraries --- src/core/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3f4544df6..bb1a21015 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -131,6 +131,7 @@ target_link_libraries(soci_core $ PRIVATE Threads::Threads + ${CMAKE_DL_LIBS} ) if (WIN32) From 21515f2c63eaddc6e0b72006fb69a8bb9f4e6e93 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 25 Aug 2024 16:26:42 +0200 Subject: [PATCH 72/79] Remove debug message --- cmake/find_modules/FindMySQL.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/find_modules/FindMySQL.cmake b/cmake/find_modules/FindMySQL.cmake index af445e18a..bb0c53dbc 100644 --- a/cmake/find_modules/FindMySQL.cmake +++ b/cmake/find_modules/FindMySQL.cmake @@ -63,7 +63,6 @@ if (NOT MySQL_LIBRARIES) execute_process(COMMAND ${CONFIG_EXE} --include OUTPUT_VARIABLE MySQL_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND ${CONFIG_EXE} --libs OUTPUT_VARIABLE MySQL_LIBRARIES OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND ${CONFIG_EXE} --version OUTPUT_VARIABLE MySQL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "'${MySQL_LIBRARIES}'") endif() endif() From a2faea6e93756d6611679ee0f3f8fc8b09217f6c Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sat, 31 Aug 2024 16:48:21 +0200 Subject: [PATCH 73/79] Don't set output directories This can lead to inconsistencies when another project embeds SOCI which especially on Windows are likely to lead to issues when SOCI is built as a DLL. Fixes #1159 --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10fd552ee..d523891c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,11 +73,6 @@ if (SOCI_LD) add_link_options("${USE_LD_FLAG}") endif() -# Ensure that libraries and executables end up at the top of the build tree -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) - if (SOCI_SHARED) set(SOCI_LIB_TYPE "SHARED") else() From 6a106e7f0f3cc189589ddce0800f1c1f840cba79 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 09:31:28 +0200 Subject: [PATCH 74/79] Also use versioned .so install for MySQL --- src/backends/mysql/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 49b08bf58..865757ea8 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -28,6 +28,13 @@ add_library(soci_mysql add_library(SOCI::MySQL ALIAS soci_mysql) +set_target_properties( + soci_mysql PROPERTIES + SOVERSION ${PROJECT_VERSION_MAJOR} + VERSION ${PROJECT_VERSION} + EXPORT_NAME MySQL +) + soci_public_dependency( NAME MySQL DEP_TARGETS MySQL::MySQL From f4e01a4bf359badba6f01101322bc885b249a89f Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 12:42:26 +0200 Subject: [PATCH 75/79] Re-introduce explicit build artefact directories It turned out that on Windows this is actually necessary in order for DLLs to be usable. In contrast to what has been removed in a2faea6e93756d6611679ee0f3f8fc8b09217f6c, this commit adds a guard that will ensure that when CMAKE_RUNTIME_DIRECTORY (which is the crucial one in order for DLLs to work) is already set, we don't overwrite it. This means that we should stick to whatever has already been configured, avoiding any inconsistencies. Additionally, we now emit a warning if SOCI is embedded by a project that doesn't set these variables as that can also lead to inconsistencies as then the order of when exactly SOCI is embedded into the top-level project matters. Relates to #1159 --- CMakeLists.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d523891c0..b5b585e0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,23 @@ if (SOCI_LD) add_link_options("${USE_LD_FLAG}") endif() +if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + # Ensure that build artifacts are easy to find. And on Windows this + # guarantees that the built DLLs end up next to applications + # linking to them as otherwise they won't be found. + 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) + + if (NOT PROJECT_IS_TOP_LEVEL) + # If the embedding project does not define these variables, this can lead to + # inconsistencies which can cause issues on Windows when e.g. the embedding + # project has an executable that links to a SOCI DLL which will be put into + # a different directory which will lead to the exe not finding the DLL. + message(WARNING "Setting CMAKE_{LIBRARY, ARCHIVE, RUNTIME}_DIRECTORY variables which should have done by the embedding cmake project") + endif() +endif() + if (SOCI_SHARED) set(SOCI_LIB_TYPE "SHARED") else() From 8d14ee456f4da428ba64d1b09d67887a35acd2cd Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 13:18:35 +0200 Subject: [PATCH 76/79] Rework install paths The install paths can now be configured by setting the corresponding SOCI_INSTALL_* variables. Additionally, this commit fixes the header files being installed under .../soci/soci/bla.h (two "soci" subdirs) and now install properly under .../soci/bla.h --- CMakeLists.txt | 6 ++---- cmake/soci_install_dirs.cmake | 15 +++++++++++++++ src/backends/db2/CMakeLists.txt | 8 ++++---- src/backends/empty/CMakeLists.txt | 8 ++++---- src/backends/firebird/CMakeLists.txt | 8 ++++---- src/backends/mysql/CMakeLists.txt | 8 ++++---- src/backends/odbc/CMakeLists.txt | 8 ++++---- src/backends/oracle/CMakeLists.txt | 8 ++++---- src/backends/postgresql/CMakeLists.txt | 8 ++++---- src/backends/sqlite3/CMakeLists.txt | 8 ++++---- src/core/CMakeLists.txt | 8 ++++---- 11 files changed, 53 insertions(+), 40 deletions(-) create mode 100644 cmake/soci_install_dirs.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index b5b585e0d..d61964990 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,4 @@ +# Note: we need >= v3.23 in order to make use of file sets for header installation cmake_minimum_required(VERSION 3.23 FATAL_ERROR) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -103,10 +104,7 @@ set(SOCI_DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}" CACHE STRING "Specifies suffix f include(soci_compiler_options) - -include(GNUInstallDirs) - -set(SOCI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/soci-${PROJECT_VERSION}") +include(soci_install_dirs) add_subdirectory(src) diff --git a/cmake/soci_install_dirs.cmake b/cmake/soci_install_dirs.cmake new file mode 100644 index 000000000..95ba35d8b --- /dev/null +++ b/cmake/soci_install_dirs.cmake @@ -0,0 +1,15 @@ +include(GNUInstallDirs) + +set(SOCI_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/soci-${PROJECT_VERSION}" + CACHE FILEPATH "Directory into which cmake related files (e.g. SOCIConfig.cmake) are installed") + +set(SOCI_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}" + CACHE FILEPATH "Directory into which to install any SOCI executables") + +set(SOCI_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/soci" + CACHE FILEPATH "Directory into which any SOCI libraries (except DLLs on Windows) are installed") + +# Note that our headers are installed via a file set that already has the headers +# in a dedicated "soci" subdirectory (which will be part of the installation) +set(SOCI_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}" + CACHE FILEPATH "Directory into which the 'soci' directory with all SOCI header files is installed") diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index d05db8e13..08c857834 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -59,14 +59,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::DB2) install( TARGETS soci_db2 EXPORT SOCIDB2Targets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index 1a11af1d1..473b35402 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -39,14 +39,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::Empty) install( TARGETS soci_empty EXPORT SOCIEmptyTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index e22ea74e4..a069e9b2f 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -66,14 +66,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::Firebird) install( TARGETS soci_firebird EXPORT SOCIFirebirdTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index 865757ea8..b06c4b88a 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -61,14 +61,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::MySQL) install( TARGETS soci_mysql EXPORT SOCIMySQLTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index be8873518..a02c08274 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -60,14 +60,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::ODBC) install( TARGETS soci_odbc EXPORT SOCIODBCTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 4aa48571a..84b62e8f9 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -61,14 +61,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::Oracle) install( TARGETS soci_oracle EXPORT SOCIOracleTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 7a2e1cdfd..40f3e84bd 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -71,14 +71,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::PostgreSQL) install( TARGETS soci_postgresql EXPORT SOCIPostgreSQLTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 9ee81a024..520e17815 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -60,14 +60,14 @@ target_link_libraries(soci_interface INTERFACE SOCI::SQLite3) install( TARGETS soci_sqlite3 EXPORT SOCISQLite3Targets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bb1a21015..46c389c31 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -159,14 +159,14 @@ endif() install( TARGETS soci_core EXPORT SOCICoreTargets - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/soci" + RUNTIME DESTINATION "${SOCI_INSTALL_BINDIR}" COMPONENT soci_runtime - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + LIBRARY DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_runtime NAMELINK_COMPONENT soci_development - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/soci" + ARCHIVE DESTINATION "${SOCI_INSTALL_LIBDIR}" COMPONENT soci_development - FILE_SET headers DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/soci" + FILE_SET headers DESTINATION "${SOCI_INSTALL_INCLUDEDIR}" COMPONENT soci_development ) # Generate and install a targets file From a74341ccacb653de3f32a4e1a4636882c42c0e63 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 18:42:16 +0200 Subject: [PATCH 77/79] Update example --- examples/subdir-include/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/subdir-include/CMakeLists.txt b/examples/subdir-include/CMakeLists.txt index e14b62365..ebe1f903a 100644 --- a/examples/subdir-include/CMakeLists.txt +++ b/examples/subdir-include/CMakeLists.txt @@ -2,7 +2,7 @@ # when placing SOCI in a subdirectory lib/soci/ of your project. # For this example, the SOCI backend called Empty is used. -cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +cmake_minimum_required(VERSION 3.23 FATAL_ERROR) project(SOCIExampleSubdirectoryInclude) @@ -12,6 +12,15 @@ set(SOCI_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/soci) # The backend you want to use needs to be enabled here. set(SOCI_EMPTY ON CACHE INTERNAL "" FORCE) +# In order to not run into cross-platform issues, the **top-level** project +# should define these variables to ensure all build artefacts (of a given kind) +# end up in the same place. +if (PROJECT_IS_TOP_LEVEL) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") +endif() + # This line needs to be changed to include the directory you placed SOCI in, e.g. # add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/soci ${SOCI_BINARY_DIR}) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. ${SOCI_BINARY_DIR}) From 6b6db13fc99ce838fe8929708d786a2333e0533f Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 18:42:37 +0200 Subject: [PATCH 78/79] Fix not all headers getting installed --- src/backends/db2/CMakeLists.txt | 8 ++++++++ src/backends/empty/CMakeLists.txt | 9 +++++++++ src/backends/firebird/CMakeLists.txt | 8 ++++++++ src/backends/mysql/CMakeLists.txt | 8 ++++++++ src/backends/odbc/CMakeLists.txt | 8 ++++++++ src/backends/oracle/CMakeLists.txt | 8 ++++++++ src/backends/postgresql/CMakeLists.txt | 8 ++++++++ src/backends/sqlite3/CMakeLists.txt | 8 ++++++++ src/core/CMakeLists.txt | 9 +++++---- 9 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/backends/db2/CMakeLists.txt b/src/backends/db2/CMakeLists.txt index 08c857834..6b8c54b35 100644 --- a/src/backends/db2/CMakeLists.txt +++ b/src/backends/db2/CMakeLists.txt @@ -34,6 +34,14 @@ set_target_properties( EXPORT_NAME DB2 ) +target_sources(soci_db2 + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/db2/soci-db2.h" +) + soci_public_dependency( NAME DB2 DEP_TARGETS DB2::DB2 diff --git a/src/backends/empty/CMakeLists.txt b/src/backends/empty/CMakeLists.txt index 473b35402..51467cbfa 100644 --- a/src/backends/empty/CMakeLists.txt +++ b/src/backends/empty/CMakeLists.txt @@ -20,6 +20,15 @@ set_target_properties( VERSION ${PROJECT_VERSION} EXPORT_NAME Empty ) + +target_sources(soci_empty + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/empty/soci-empty.h" +) + soci_public_dependency( NAME SOCI::Core DEP_TARGETS SOCI::Core diff --git a/src/backends/firebird/CMakeLists.txt b/src/backends/firebird/CMakeLists.txt index a069e9b2f..cb62f17c5 100644 --- a/src/backends/firebird/CMakeLists.txt +++ b/src/backends/firebird/CMakeLists.txt @@ -41,6 +41,14 @@ set_target_properties( EXPORT_NAME Firebird ) +target_sources(soci_firebird + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/firebird/soci-firebird.h" +) + soci_public_dependency( NAME Firebird DEP_TARGETS Firebird::Firebird diff --git a/src/backends/mysql/CMakeLists.txt b/src/backends/mysql/CMakeLists.txt index b06c4b88a..1a4ccbd31 100644 --- a/src/backends/mysql/CMakeLists.txt +++ b/src/backends/mysql/CMakeLists.txt @@ -35,6 +35,14 @@ set_target_properties( EXPORT_NAME MySQL ) +target_sources(soci_mysql + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/mysql/soci-mysql.h" +) + soci_public_dependency( NAME MySQL DEP_TARGETS MySQL::MySQL diff --git a/src/backends/odbc/CMakeLists.txt b/src/backends/odbc/CMakeLists.txt index a02c08274..3f0b29634 100644 --- a/src/backends/odbc/CMakeLists.txt +++ b/src/backends/odbc/CMakeLists.txt @@ -34,6 +34,14 @@ set_target_properties( EXPORT_NAME ODBC ) +target_sources(soci_odbc + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/odbc/soci-odbc.h" +) + soci_public_dependency( NAME ODBC DEP_TARGETS ODBC::ODBC diff --git a/src/backends/oracle/CMakeLists.txt b/src/backends/oracle/CMakeLists.txt index 84b62e8f9..c629f2bfb 100644 --- a/src/backends/oracle/CMakeLists.txt +++ b/src/backends/oracle/CMakeLists.txt @@ -35,6 +35,14 @@ set_target_properties( EXPORT_NAME Oracle ) +target_sources(soci_oracle + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/oracle/soci-oracle.h" +) + soci_public_dependency( NAME Oracle DEP_TARGETS Oracle::Oracle diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 40f3e84bd..782f71b7e 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -43,6 +43,14 @@ set_target_properties( EXPORT_NAME PostgreSQL ) +target_sources(soci_postgresql + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/postgresql/soci-postgresql.h" +) + soci_public_dependency( NAME PostgreSQL DEP_TARGETS PostgreSQL::PostgreSQL diff --git a/src/backends/sqlite3/CMakeLists.txt b/src/backends/sqlite3/CMakeLists.txt index 520e17815..8bd2efe3f 100644 --- a/src/backends/sqlite3/CMakeLists.txt +++ b/src/backends/sqlite3/CMakeLists.txt @@ -35,6 +35,14 @@ set_target_properties( EXPORT_NAME SQLite3 ) +target_sources(soci_sqlite3 + PUBLIC + FILE_SET headers TYPE HEADERS + BASE_DIRS "${PROJECT_SOURCE_DIR}/include" + FILES + "${PROJECT_SOURCE_DIR}/include/soci/sqlite3/soci-sqlite3.h" +) + soci_public_dependency( NAME SQLite3 DEP_TARGETS SQLite::SQLite3 diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 46c389c31..63796c574 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -60,8 +60,9 @@ target_sources(soci_core "${PROJECT_SOURCE_DIR}/include/soci/error.h" "${PROJECT_SOURCE_DIR}/include/soci/exchange-traits.h" "${PROJECT_SOURCE_DIR}/include/soci/fixed-size-ints.h" - "${PROJECT_SOURCE_DIR}/include/soci/into.h" "${PROJECT_SOURCE_DIR}/include/soci/into-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/into.h" + "${PROJECT_SOURCE_DIR}/include/soci/is-detected.h" "${PROJECT_SOURCE_DIR}/include/soci/logger.h" "${PROJECT_SOURCE_DIR}/include/soci/noreturn.h" "${PROJECT_SOURCE_DIR}/include/soci/once-temp-type.h" @@ -77,20 +78,20 @@ target_sources(soci_core "${PROJECT_SOURCE_DIR}/include/soci/rowset.h" "${PROJECT_SOURCE_DIR}/include/soci/session.h" "${PROJECT_SOURCE_DIR}/include/soci/soci-backend.h" - "${PROJECT_SOURCE_DIR}/include/soci/soci.h" "${PROJECT_SOURCE_DIR}/include/soci/soci-platform.h" "${PROJECT_SOURCE_DIR}/include/soci/soci-simple.h" "${PROJECT_SOURCE_DIR}/include/soci/soci-types.h" + "${PROJECT_SOURCE_DIR}/include/soci/soci.h" "${PROJECT_SOURCE_DIR}/include/soci/statement.h" "${PROJECT_SOURCE_DIR}/include/soci/std-optional.h" "${PROJECT_SOURCE_DIR}/include/soci/transaction.h" - "${PROJECT_SOURCE_DIR}/include/soci/type-conversion.h" "${PROJECT_SOURCE_DIR}/include/soci/type-conversion-traits.h" + "${PROJECT_SOURCE_DIR}/include/soci/type-conversion.h" "${PROJECT_SOURCE_DIR}/include/soci/type-holder.h" "${PROJECT_SOURCE_DIR}/include/soci/type-ptr.h" "${PROJECT_SOURCE_DIR}/include/soci/type-wrappers.h" - "${PROJECT_SOURCE_DIR}/include/soci/use.h" "${PROJECT_SOURCE_DIR}/include/soci/use-type.h" + "${PROJECT_SOURCE_DIR}/include/soci/use.h" "${PROJECT_SOURCE_DIR}/include/soci/values-exchange.h" "${PROJECT_SOURCE_DIR}/include/soci/values.h" "${PROJECT_SOURCE_DIR}/include/soci/version.h" From efe3a055238418ec61c5d61a74bd7f10dc5931c5 Mon Sep 17 00:00:00 2001 From: Robert Adam Date: Sun, 1 Sep 2024 19:43:27 +0200 Subject: [PATCH 79/79] Work around bug in VS2015 There seems to be a bug in Visual Studio 2015 that leads to a call tp logger_impl::set_stream instead of standard_logger_impl::set_stream in the "basic logging support" test case, which throws a "not supported" exception instead of actually setting the log stream, leading to a test case failure. This hasn't been observed for any other compiler, including more recent versions of Visual Studio. Thus, it was concluded that this was indeed a compiler bug in VS2015. Moving the standard_logger_impl implementation out of the anonymous namespace works around this bug without affecting the behavior of the library in any way. --- src/core/session.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/core/session.cpp b/src/core/session.cpp index f2e1bf34d..493bd2747 100644 --- a/src/core/session.cpp +++ b/src/core/session.cpp @@ -15,9 +15,6 @@ using namespace soci; using namespace soci::details; -namespace // anonymous -{ - void ensureConnected(session_backend * backEnd) { if (backEnd == NULL) @@ -70,8 +67,6 @@ class standard_logger_impl : public logger_impl std::string lastQuery_; }; -} // namespace anonymous - session::session() : once(this), prepare(this), logger_(new standard_logger_impl),