pax_global_header00006660000000000000000000000064141443172400014512gustar00rootroot0000000000000052 comment=38ddc46bc315ed822b77378c4acb6bfe23dc6cda mariadb-connector-odbc-3.1.15/000077500000000000000000000000001414431724000160755ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/BUILD.md000066400000000000000000000037401414431724000172620ustar00rootroot00000000000000# Building the Connector/ODBC Following are build instructions for various operating systems. Paths and filenames are given for example, and may be different on your system of for your purposes. ## Windows Prior to start building on Windows you need to have following tools installed: - Microsoft Visual Studio https://visualstudio.microsoft.com/downloads/ - Git https://git-scm.com/download/win - cmake https://cmake.org/download/ If you need msi package to be generated by the build process, you also need: - WiX Toolset https://wixtoolset.org/releases/ and provide WIX_DIR parameter for cmake with path to WiX binaries, or set WIX env variable with path to WiX installation directory. If you need 32bit(or 64bit) build, you may need to give cmake -A Win32 parameter(or -A Win64), or specify correct cmake generator name using option -G ``` git clone https://github.com/MariaDB/mariadb-connector-odbc.git cd mariadb-connector-odbc cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCONC_WITH_MSI=OFF -DWITH_SSL=SCHANNEL -DWIX_DIR="C:\Program Files (x86)\WiX Toolset v3.11\bin\" . cmake --build . --config RelWithDebInfo msiexec.exe /i wininstall\mariadb-connector-odbc-3.0.6-win32.msi ``` ## CentOS ``` sudo yum -y install git cmake make gcc openssl-devel unixODBC unixODBC-devel git clone https://github.com/MariaDB/mariadb-connector-odbc.git mkdir build && cd build cmake ../mariadb-connector-odbc/ -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCMAKE_INSTALL_PREFIX=/usr/local -DWITH_SSL=OPENSSL cmake --build . --config RelWithDebInfo sudo make install ``` ## Debian & Ubuntu ``` sudo apt-get update sudo sh apt-get install -y git cmake make gcc libssl-dev unixodbc-dev git clone https://github.com/MariaDB/mariadb-connector-odbc.git mkdir build && cd build cmake ../mariadb-connector-odbc/ -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCMAKE_INSTALL_PREFIX=/usr/local -DWITH_SSL=OPENSSL cmake --build . --config RelWithDebInfo sudo make install ``` mariadb-connector-odbc-3.1.15/CMakeLists.txt000066400000000000000000000513151414431724000206420ustar00rootroot00000000000000# ************************************************************************************ # Copyright (C) 2013,2021 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # ************************************************************************************ CMAKE_POLICY(SET CMP0048 NEW) PROJECT(mariadb_connector_odbc C) cmake_minimum_required(VERSION 2.8) SET(MARIADB_ODBC_VERSION_MAJOR 3) SET(MARIADB_ODBC_VERSION_MINOR 1) SET(MARIADB_ODBC_VERSION_PATCH 15) SET(MARIADB_ODBC_VERSION_QUALITY "ga") SET(MARIADB_ODBC_VERSION "03.01.0015") SET(MARIADB_DEFAULT_PLUGINS_SUBDIR "plugin") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/ma_odbc_version.h.in ${CMAKE_SOURCE_DIR}/ma_odbc_version.h) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/maodbcu.rc.in ${CMAKE_SOURCE_DIR}/maodbcu.rc) SET (MARIADB_ODBC_SOURCES odbc_3_api.c ma_api_internal.c ma_error.c ma_connection.c ma_helper.c ma_debug.c ma_dsn.c ma_driver.c ma_info.c ma_environment.c ma_parse.c ma_catalog.c ma_statement.c ma_desc.c ma_string.c ma_result.c ma_common.c ma_server.c ma_legacy_helpers.c ma_typeconv.c ma_bulk.c) SET(DSN_DIALOG_FILES ${CMAKE_SOURCE_DIR}/dsn/odbc_dsn.c ${CMAKE_SOURCE_DIR}/dsn/odbc_dsn.rc ${CMAKE_SOURCE_DIR}/dsn/resource.h ma_dsn.c ma_common.c) MACRO(ADD_OPTION _name _text _default) IF(NOT DEFINED ${_name}) OPTION(${OPT}${_name} "${_text}" "${_default}") ELSE() OPTION(${OPT}${_name} "${_text}" "${${_name}}") ENDIF() ENDMACRO() # This has to be before C/C's cmake run, or it will build with /MD IF(WIN32) IF (MSVC) SET(CONFIG_TYPES "DEBUG" "RELEASE" "RELWITHDEBINFO" "MINSIZEREL") FOREACH(BUILD_TYPE ${CONFIG_TYPES}) FOREACH(COMPILER CXX C) SET(COMPILER_FLAGS "${CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}}") IF (NOT COMPILER_FLAGS STREQUAL "") STRING(REPLACE "/MD" "/MT" COMPILER_FLAGS ${COMPILER_FLAGS}) IF (BUILD_TYPE STREQUAL "Debug") SET(COMPILER_FLAGS "${COMPILER_FLAGS} /RTC1 /RTCc") STRING(REPLACE "/Zi" "/ZI" COMPILER_FLAGS ${COMPILER_FLAGS}) ENDIF() MESSAGE (STATUS "CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE}= ${COMPILER_FLAGS}") SET(CMAKE_${COMPILER}_FLAGS_${BUILD_TYPE} ${COMPILER_FLAGS} CACHE STRING "overwritten by mariadb-odbc" FORCE) ENDIF() ENDFOREACH() ENDFOREACH() ENDIF() ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)# -DWIN32_LEAN_AND_MEAN) SET(INSTALL_PLUGINDIR "${MARIADB_DEFAULT_PLUGINS_SUBDIR}") ENDIF() INCLUDE(${CMAKE_SOURCE_DIR}/cmake/SearchLibrary.cmake) INCLUDE(${CMAKE_SOURCE_DIR}/cmake/SetValueMacro.cmake) ### Build options, initial settings and platform defaults INCLUDE("${CMAKE_SOURCE_DIR}/cmake/options_defaults.cmake") ### Setting installation paths - should go before C/C subproject sets its own. We need to have control over those INCLUDE("${CMAKE_SOURCE_DIR}/cmake/install.cmake") IF(WIN32 OR WITH_OPENSSL OR "${WITH_SSL}" STREQUAL "OPENSSL") # If C/C is linked dynamically, we don't need link C/ODBC against encryption library IF (NOT MARIADB_LINK_DYNAMIC) IF(WITH_OPENSSL OR "${WITH_SSL}" STREQUAL "OPENSSL") FIND_PACKAGE(OpenSSL) IF(OPENSSL_FOUND) MESSAGE(STATUS "Configuring to build with OpenSSL ${OPENSSL_LIBRARIES}") ADD_DEFINITIONS(-DHAVE_OPENSSL) INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES}) SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${SSL_LIBRARIES}) ELSE() MESSAGE(FATAL_ERROR "OpenSSL not found. Please install OpenSSL or disable SSL support via option -DWITH_OPENSSL=Off") ENDIF() ELSE() MESSAGE(STATUS "Configuring SSL support using SChannel") SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} version.lib) ENDIF() ENDIF() ELSE() MESSAGE(STATUS "Configuring to build without SSL support") ENDIF() ### Including C/C subproject IF(NOT USE_SYSTEM_INSTALLED_LIB) IF(GIT_BUILD_SRCPKG) # We don't want with conn/c (wrong) src pkg to be built. SET(GIT_BUILD_SRCPKG FALSE) SET(ODBC_GIT_BUILD_SRCPKG TRUE) ENDIF() MESSAGE(STATUS "Running C/C cmake scripts") INCLUDE(${CMAKE_SOURCE_DIR}/cmake/connector_c.cmake) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/libmariadb/include) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}/libmariadb/include) SET(PLUGINS_LIST dialog caching_sha2_password auth_gssapi_client sha256_password mysql_clear_password client_ed25519) IF(APPLE) SET_TARGET_PROPERTIES(${PLUGINS_LIST} PROPERTIES XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp -f" ) IF(WITH_SIGNCODE) SET_TARGET_PROPERTIES(${PLUGINS_LIST} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application: ${DEVELOPER_ID}") ELSE() SET_TARGET_PROPERTIES(${PLUGINS_LIST} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") ENDIF() ENDIF() ELSE() # Adding mariadb subdirs of standard include locations INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} "/usr/local/include/mariadb") INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} "/usr/include/mariadb") MESSAGE(STATUS "Linking against libmariadb installed on the system") ENDIF() IF(WITH_SIGNCODE) IF(WIN32 AND NOT SIGN_OPTIONS) SET(SIGN_OPTIONS /a /t http://timestamp.verisign.com/scripts/timstamp.dll) ELSE() SEPARATE_ARGUMENTS(SIGN_OPTIONS) ENDIF() MARK_AS_ADVANCED(SIGN_OPTIONS) ENDIF() #Debug log is controlled by conenction option solely ADD_DEFINITIONS(-DMAODBC_DEBUG) IF(WIN32) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/dsn) SET(ODBC_LIBS odbc32) SET(ODBC_INSTLIBS odbccp32) SET(MARIADB_ODBC_SOURCES ${MARIADB_ODBC_SOURCES} ma_dll.c ma_platform_win32.c ma_error.h ma_connection.h ma_helper.h ma_debug.h ma_dsn.h ma_driver.h ma_info.h ma_environment.h ma_parse.h ma_catalog.h ma_statement.h ma_desc.h ma_string.h ma_odbc.h ma_api_internal.h ma_odbc_version.h ma_result.h ma_server.h ma_legacy_helpers.h ma_typeconv.h ma_bulk.h) SET(PLATFORM_DEPENDENCIES ws2_32 Shlwapi Pathcch) IF (MSVC) MESSAGE(STATUS "MSVC_VERSION= ${MSVC_VERSION}") IF (NOT(MSVC_VERSION LESS 1900)) MESSAGE(STATUS "Configuring to link connector against legacy_stdio_definitions") SET(LEGACY_STDIO legacy_stdio_definitions) SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${LEGACY_STDIO}) ENDIF() ENDIF() ELSE() SEARCH_LIBRARY(LIB_MATH floor m) SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${LIB_MATH}) SET (MARIADB_ODBC_SOURCES ${MARIADB_ODBC_SOURCES} ma_platform_posix.c ma_conv_charset.c) ENDIF() INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "RelWithDebInfo") ENDIF() IF(NOT WIN32) # Looking for DM(UnixODBC) files INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindDM.cmake) IF(DM_FOUND) INCLUDE_DIRECTORIES(${ODBC_INCLUDE_DIR}) LINK_DIRECTORIES(${ODBC_LIB_DIR}) ELSE() MESSAGE(FATAL_ERROR "Driver Manager was not found") ENDIF() ENDIF() IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES AIX) IF(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 20) SET(ICONV_LIBRARIES "-liconv") SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${ICONV_LIBRARIES}) ELSE() # Looking for iconv files INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindIconv.cmake) IF(ICONV_FOUND) INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR}) SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${ICONV_LIBRARIES}) ELSE() MESSAGE(FATAL_ERROR "iconv was not found") ENDIF() ENDIF() ENDIF() # By now we have everything needed by tests. If we need to build them only - firing config now and exit # There is "normal" tests config below IF(BUILD_TESTS_ONLY) ADD_SUBDIRECTORY(test) IF(NOT WIN32) # Configuring ini files for testing with UnixODBC MESSAGE(STATUS "Configurig Test Driver: ${TEST_DRIVER}, Test DSN: ${TEST_DSN}, tcp://${TEST_UID}@${TEST_SERVER}:${TEST_PORT}/${TEST_SCHEMA} socket: ${TEST_SOCKET}") SET(DRIVER_LIB_LOCATION "${libmaodbc_prefix}/${INSTALL_LIBDIR}") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/test/odbcinst.ini.in ${CMAKE_BINARY_DIR}/test/odbcinst.ini) SET(TEST_DRIVER "${DRIVER_LIB_LOCATION}") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/test/odbc.ini.in ${CMAKE_BINARY_DIR}/test/odbc.ini) ENDIF() RETURN() ENDIF() SET(CPACK_PACKAGE_VERSION ${MARIADB_ODBC_VERSION_MAJOR}.${MARIADB_ODBC_VERSION_MINOR}.${MARIADB_ODBC_VERSION_PATCH}) SET(CPACK_COMPONENTS_ALL ClientPlugins ODBCLibs Documentation) SET(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) # We need to determine the last parameter for SQLColAttribute: # Older UnixODBC version expect SQLPOINTER while Windows expects SQLLEN * TRY_COMPILE(COMPILE_OK ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/sqlcolattribute.c) MESSAGE(STATUS "Checking if SQLColAttribute expects SQLPOINTER ${COMPILE_OK}") IF(COMPILE_OK) ADD_DEFINITIONS(-DSQLCOLATTRIB_SQLPOINTER) ELSE() ADD_DEFINITIONS(-DSQLCOLATTRIB_SQLLEN_PTR) ENDIF() IF(WIN32) SET(UNICODE "W") ELSE() IF (DIRECT_LINK_TESTS) ADD_DEFINITIONS(-DHAVE_UNICODE) ENDIF() ENDIF() SET(LIBRARY_NAME "maodbc") CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/mariadb-odbc-driver.def.in ${CMAKE_SOURCE_DIR}/mariadb-odbc-driver-uni.def) IF(MARIADB_LINK_DYNAMIC)# OR USE_SYSTEM_INSTALLED_LIB) IF(USE_SYSTEM_INSTALLED_LIB) SET(MARIADB_CLIENT_TARGET_NAME mariadb) ELSE() SET(MARIADB_CLIENT_TARGET_NAME libmariadb) ENDIF() MESSAGE(STATUS "Linking Connector/C library dynamically(${MARIADB_CLIENT_TARGET_NAME})") ELSE() SET(MARIADB_CLIENT_TARGET_NAME mariadbclient) MESSAGE(STATUS "Linking Connector/C library statically(${MARIADB_CLIENT_TARGET_NAME})") ENDIF() IF(WIN32) ADD_LIBRARY(${LIBRARY_NAME} SHARED ${MARIADB_ODBC_SOURCES} ${CMAKE_SOURCE_DIR}/mariadb-odbc-driver-uni.def maodbcu.rc) ELSE() MESSAGE(STATUS "Version script: ${CMAKE_SOURCE_DIR}/maodbc.def") ADD_LIBRARY(${LIBRARY_NAME} SHARED ${MARIADB_ODBC_SOURCES} maodbcu.rc) IF(APPLE) SET(MAODBC_INSTALL_RPATH "${ODBC_LIB_DIR}" "@loader_path" "/usr/local/opt/libiodbc" "/usr/local/iODBC/lib" "/usr/local/opt/openssl@1.1/lib" "/usr/local/opt/libressl/lib") SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-Wl" INSTALL_RPATH_USE_LINK_PATH 0 BUILD_WITH_INSTALL_RPATH 1 XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp -f" INSTALL_RPATH "${MAODBC_INSTALL_RPATH}") IF(WITH_SIGNCODE) SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application: ${DEVELOPER_ID}") ELSE() SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") ENDIF() ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES AIX) SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/maodbc.def") ENDIF() ENDIF() SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES LANGUAGE C) MESSAGE(STATUS "All linked targets/external dependencies: ${MARIADB_CLIENT_TARGET_NAME} ${ODBC_INSTLIBS} ${PLATFORM_DEPENDENCIES}") TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${MARIADB_CLIENT_TARGET_NAME} ${ODBC_INSTLIBS} ${PLATFORM_DEPENDENCIES}) # Currently on Windows only IF(WIN32) ####### MAODBCS ####### ADD_LIBRARY(maodbcs SHARED ${DSN_DIALOG_FILES} ${CMAKE_SOURCE_DIR}/dsn/mariadb_odbc_setup.def) SET_TARGET_PROPERTIES(maodbcs PROPERTIES LANGUAGE C) TARGET_LINK_LIBRARIES(maodbcs comctl32 ${LEGACY_STDIO} Shlwapi ${ODBC_LIBS} ${ODBC_INSTLIBS}) ADD_EXECUTABLE(dsn_test dsn_test.c) TARGET_LINK_LIBRARIES(dsn_test ${MARIADB_LIB}) ELSEIF(APPLE) ADD_CUSTOM_COMMAND(TARGET maodbc POST_BUILD COMMAND ${CMAKE_SOURCE_DIR}/osxpostbuild.sh ARGS $ ) ENDIF() SET(CPACK_PACKAGE_NAME "mariadb-connector-odbc") IF(WIN32) IF(WITH_MSI) IF(ALL_PLUGINS_STATIC) ADD_CUSTOM_COMMAND(TARGET maodbc POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -DDRIVER_LIB_DIR=$ -DPLUGINS_LIB_DIR="" -DINSTALLER_TOOLS_DIR=$ -DPLUGINS_SUBDIR_NAME=${MARIADB_DEFAULT_PLUGINS_SUBDIR} -DFILE_IN=${CMAKE_SOURCE_DIR}/wininstall/binaries_dir.xml.in -DFILE_OUT=${CMAKE_SOURCE_DIR}/wininstall/binaries_dir.xml -P ${CMAKE_SOURCE_DIR}/cmake/ConfigureFile.cmake) ELSE() ADD_CUSTOM_COMMAND(TARGET maodbc POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -DDRIVER_LIB_DIR=$ -DPLUGINS_LIB_DIR=$ -DINSTALLER_TOOLS_DIR=$ -DPLUGINS_SUBDIR_NAME=${MARIADB_DEFAULT_PLUGINS_SUBDIR} -DFILE_IN=${CMAKE_SOURCE_DIR}/wininstall/binaries_dir.xml.in -DFILE_OUT=${CMAKE_SOURCE_DIR}/wininstall/binaries_dir.xml -P ${CMAKE_SOURCE_DIR}/cmake/ConfigureFile.cmake) ENDIF() ADD_SUBDIRECTORY(wininstall) ELSE() MESSAGE(STATUS "MSI package won't be generated - WITH_MSI=${WITH_MSI}") ENDIF() ADD_EXECUTABLE(install_driver osxinstall/install_driver.c) TARGET_LINK_LIBRARIES(install_driver ${PLATFORM_DEPENDENCIES} ${ODBC_INSTLIBS}) ELSE() IF(APPLE) MESSAGE(STATUS "Configuring to generate PKG package") ADD_SUBDIRECTORY(osxinstall) ELSE() CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libmaodbc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libmaodbc.pc @ONLY) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libmaodbc.pc DESTINATION "${INSTALL_PCDIR}" COMPONENT Development) ENDIF() INSTALL(TARGETS ${LIBRARY_NAME} LIBRARY DESTINATION ${INSTALL_LIBDIR} COMPONENT ODBCLibs) INSTALL(FILES ${CMAKE_SOURCE_DIR}/README DESTINATION "${INSTALL_DOCDIR}" COMPONENT Documentation) INSTALL(FILES ${CMAKE_SOURCE_DIR}/COPYING DESTINATION "${INSTALL_LICENSEDIR}" COMPONENT Documentation) ENDIF() # Tests. Should be verified by now, if we have them. IF(WITH_UNIT_TESTS) ADD_SUBDIRECTORY(test) IF(NOT WIN32) # Configuring ini files for testing with UnixODBC MESSAGE(STATUS "Configurig Test Driver: ${TEST_DRIVER}, Test DSN: ${TEST_DSN}, tcp://${TEST_UID}@${TEST_SERVER}:${TEST_PORT}/${TEST_SCHEMA} socket: ${TEST_SOCKET}") ADD_CUSTOM_COMMAND(TARGET maodbc POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -DDRIVER_LIB_LOCATION=$ -DTEST_DRIVER=${TEST_DRIVER} -DFILE_IN=${CMAKE_SOURCE_DIR}/test/odbcinst.ini.in -DFILE_OUT=${CMAKE_BINARY_DIR}/test/odbcinst.ini -P ${CMAKE_SOURCE_DIR}/cmake/ConfigureFile.cmake ) ADD_CUSTOM_COMMAND(TARGET maodbc POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -DTEST_DRIVER=$ -DTEST_DSN=${TEST_DSN} -DTEST_PORT=${TEST_PORT} -DTEST_SERVER=${TEST_SERVER} -DTEST_SOCKET=${TEST_SOCKET} -DTEST_SCHEMA=${TEST_SCHEMA} -DTEST_UID=${TEST_UID} -DTEST_PASSWORD="${TEST_PASSWORD}" -DTEST_USETLS=${TEST_USETLS} -DFILE_IN=${CMAKE_SOURCE_DIR}/test/odbc.ini.in -DFILE_OUT=${CMAKE_BINARY_DIR}/test/odbc.ini -P ${CMAKE_SOURCE_DIR}/cmake/ConfigureFile.cmake ) ENDIF() ENDIF() # Packaging SET(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab") SET(CPACK_PACKAGE_DESCRIPTION "MariaDB Connector/ODBC. ODBC driver library for connecting to MariaDB and MySQL servers") IF(NOT SYSTEM_NAME) STRING(TOLOWER ${CMAKE_SYSTEM_NAME} system_name) ENDIF() SET(QUALITY_SUFFIX "") IF (MARIADB_ODBC_VERSION_QUALITY AND NOT "${MARIADB_ODBC_VERSION_QUALITY}" STREQUAL "ga" AND NOT "${MARIADB_ODBC_VERSION_QUALITY}" STREQUAL "GA") SET(QUALITY_SUFFIX "-${MARIADB_ODBC_VERSION_QUALITY}") ENDIF() IF(PACKAGE_PLATFORM_SUFFIX) SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-odbc-${CPACK_PACKAGE_VERSION}-${PACKAGE_PLATFORM_SUFFIX}") ELSE() SET(CPACK_PACKAGE_FILE_NAME "mariadb-connector-odbc-${CPACK_PACKAGE_VERSION}${QUALITY_SUFFIX}-${system_name}-${CMAKE_SYSTEM_PROCESSOR}") ENDIF() MESSAGE(STATUS "Package Name: ${CPACK_PACKAGE_FILE_NAME}") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") SET(CPACK_SOURCE_PACKAGE_FILE_NAME "mariadb-connector-odbc-${CPACK_PACKAGE_VERSION}${QUALITY_SUFFIX}-src") SET(CPACK_PACKAGE_CONTACT "info@mariadb.com") SET(CPACK_PACKAGE_VENDOR "MariaDB Corporation AB") SET(CPACK_SOURCE_IGNORE_FILES /test/ /.git/ .gitignore .gitmodules .gitattributes CMakeCache.txt cmake_dist.cmake CPackSourceConfig.cmake CPackConfig.cmake /.build/ cmake_install.cmake CTestTestfile.cmake /CMakeFiles/ /version_resources/ .*vcxproj .*gz$ .*zip$ .*so$ .*so.2 .*so.3 .*dll$ .*a$ .*pdb$ .*sln$ .*sdf$ Makefile$ /autom4te.cache/ /.travis/ .travis.yml /libmariadb/ /_CPack_Packages/ ) # Build source packages IF(GIT_BUILD_SRCPKG OR ODBC_GIT_BUILD_SRCPKG) IF(WIN32) EXECUTE_PROCESS(COMMAND git archive --format=zip --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip --worktree-attributes -v HEAD) ELSE() EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=zip --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.zip -v HEAD) EXECUTE_PROCESS(COMMAND git archive ${GIT_BRANCH} --format=tar --prefix=${CPACK_SOURCE_PACKAGE_FILE_NAME}/ --output=${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar -v HEAD) EXECUTE_PROCESS(COMMAND gzip -9 -f ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar) ENDIF() ENDIF() IF(WIN32) SET(DEFAULT_GENERATOR "ZIP") ELSE() SET(DEFAULT_GENERATOR "TGZ") ENDIF() IF(NOT CPACK_GENERATOR) SET(CPACK_GENERATOR "${DEFAULT_GENERATOR}") ENDIF() IF(NOT CPACK_SOURCE_GENERATOR) SET(CPACK_SOURCE_GENERATOR "${DEFAULT_GENERATOR}") ENDIF() MESSAGE(STATUS "License File: ${CPACK_RESOURCE_FILE_LICENSE}") MESSAGE(STATUS "ReadMe File: ${CPACK_PACKAGE_DESCRIPTION_FILE}") MESSAGE(STATUS "Source Package Filename: ${CPACK_SOURCE_PACKAGE_FILE_NAME}.${CPACK_SOURCE_GENERATOR}") INCLUDE(CPack) mariadb-connector-odbc-3.1.15/COPYING000066400000000000000000000636421414431724000171430ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mariadb-connector-odbc-3.1.15/README000066400000000000000000000004431414431724000167560ustar00rootroot00000000000000MariaDB Connector/ODBC 3.1 GA This is a GA release of the MariaDB Connector/ODBC. MariaDB Connector/ODBC is released under version 2.1 of the GNU Lesser Public License. License information can be found in the COPYING file. To report issues: https://jira.mariadb.org/projects/ODBC/issues/ mariadb-connector-odbc-3.1.15/README.md000066400000000000000000000022451414431724000173570ustar00rootroot00000000000000# MariaDB Connector/ODBC 3.1

## Status [![License (LGPL version 2.1)](https://img.shields.io/badge/license-GNU%20LGPL%20version%202.1-green.svg?style=flat-square)](http://opensource.org/licenses/LGPL-2.1) [![Linux Build](https://secure.travis-ci.org/MariaDB-Corporation/mariadb-connector-odbc.png?branch=master)](https://travis-ci.com/MariaDB-Corporation/mariadb-connector-odbc) [![Windows status](https://ci.appveyor.com/api/projects/status/1fv21j33a6mpkxq5/branch/master?svg=true)](https://ci.appveyor.com/project/LawrinNovitsky/mariadb-connector-odbc) This is a GA release of the MariaDB Connector/ODBC. MariaDB Connector/ODBC is released under version 2.1 of the GNU Lesser Public License. License information can be found in the COPYING file. Tracker link https://jira.mariadb.org/projects/ODBC/issues/ ## Documentation For a Getting started guide, API docs, recipes, etc. see the [About MariaDB connector/ODBC](https://mariadb.com/kb/en/mariadb/about-mariadb-connector-odbc/) mariadb-connector-odbc-3.1.15/appveyor-download.bat000066400000000000000000000006751414431724000222470ustar00rootroot00000000000000@echo off set archive=http://ftp.hosteurope.de/mirror/archive.mariadb.org//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi set last=http://mirror.i3d.net/pub/mariadb//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi curl -fLsS -o server.msi %archive% if %ERRORLEVEL% == 0 goto end curl -fLsS -o server.msi %last% if %ERRORLEVEL% == 0 goto end echo Failure Reason Given is %errorlevel% exit /b %errorlevel% :end echo "File found". mariadb-connector-odbc-3.1.15/appveyor.yml000066400000000000000000000075441414431724000204770ustar00rootroot00000000000000environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 DB: 10.4.14 CMAKE_PARAM_G: 'Visual Studio 14 2015 Win64' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 DB: 10.4.14 CMAKE_PARAM_G: 'Visual Studio 15 2017 Win64' - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 DB: 10.5.5 CMAKE_PARAM_G: 'Visual Studio 15 2017 Win64' # scripts that are called at very beginning, before repo cloning init: - git config --global core.autocrlf input - wmic cpu get NumberOfCores - wmic ComputerSystem get TotalPhysicalMemory clone_folder: c:\maodbc platform: x64 configuration: Release build_script: # build libmariadb separately first because otherwise the Wix installer build might look for files that aren't available yet - cd libmariadb - cmake --build . --config RelWithDebInfo --parallel 2 # build odbc - cd .. - cmake --build . --config RelWithDebInfo --parallel 2 # scripts to run before build before_build: - cd c:\maodbc - git submodule init - git submodule update - rm -rf win64 - mkdir win64 - cd win64 - cmake .. -G "%CMAKE_PARAM_G%" -DCONC_WITH_MSI=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SIGNCODE=0 -DWITH_SSL=SCHANNEL after_build: # download and install MariaDB Server - cmd: appveyor-download.bat - msiexec /i server.msi INSTALLDIR=c:\mariadb-server SERVICENAME=mariadb /qn # create test database - c:\mariadb-server\bin\mysql.exe -e "CREATE DATABASE odbc_test" --user=root # install built odbc driver - ps: $msifile = Get-ChildItem $env:APPVEYOR_BUILD_FOLDER\win64\wininstall\mariadb-connector-odbc*.msi | Select-Object -First 1 - ps: Push-AppveyorArtifact $msifile.FullName -FileName $msifile.Name - ps: Write $msifile - ps: msiexec /i $msifile INSTALLDIR=c:\mariadb-odbc /qn # add ODBC DSN with the built driver # notice that it isn't possible to currently use Add-OdbcDsn in PowerShell. It will give an error because of missing properties. Therefore the # DSN is added to registry below. #- ps: Add-OdbcDsn -Name "test" -DriverName "MariaDB ODBC 3.1 Driver" -DsnType "System" -SetPropertyValue @("Server=localhost", "PORT=3306", "Database=odbc_test") - ps: New-Item -Path "HKCU:\Software\ODBC" - ps: New-Item -Path "HKCU:\Software\ODBC\ODBC.INI" - ps: $regPath = "HKCU:\Software\ODBC\ODBC.INI\test" - ps: New-Item -Path $regPath - ps: New-ItemProperty -Path $regPath -Name "CONN_TIMEOUT" -Value "0" - ps: New-ItemProperty -Path $regPath -Name "DATABASE" -Value "odbc_test" - ps: New-ItemProperty -Path $regPath -Name "DESCRIPTION" -Value "MariaDB ODBC test" - ps: New-ItemProperty -Path $regPath -Name "Driver" -Value "MariaDB ODBC 3.1 Driver" - ps: New-ItemProperty -Path $regPath -Name "OPTIONS" -Value "0" - ps: New-ItemProperty -Path $regPath -Name "PORT" -Value "0" - ps: New-ItemProperty -Path $regPath -Name "PWD" -Value "" - ps: New-ItemProperty -Path $regPath -Name "SERVER" -Value "localhost" - ps: New-ItemProperty -Path $regPath -Name "SSLVERIFY" -Value "0" - ps: New-ItemProperty -Path $regPath -Name "TCPIP" -Value "1" - ps: New-ItemProperty -Path $regPath -Name "UID" -Value "root" - ps: New-Item -Path "HKCU:\Software\ODBC\ODBC.INI\ODBC Data Sources" - ps: New-ItemProperty -Path "HKCU:\Software\ODBC\ODBC.INI\ODBC Data Sources" -Name "test" -Value "MariaDB ODBC 3.1 Driver" - timeout /T 1 # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) - cd test - ctest -V on_finish: # - dir # - dir RelWithDebInfo # - dir wininstall # - dir libmariadb\RelWithDebInfo # - dir release # - dir libmariadb # - dir libmariadb\release # - dir wininstall # - type wininstall\mariadb_odbc.xml mariadb-connector-odbc-3.1.15/azure-pipelines.yml000066400000000000000000000261111414431724000217350ustar00rootroot00000000000000resources: containers: - container: ubuntu-1804 image: ubuntu:18.04 options: "--name ubuntu-1804 --add-host=mariadb.example.com:127.0.0.1 -v /usr/bin/docker:/tmp/docker:ro" jobs: - job: SSLFiles displayName: 'Creating SSL Files' pool: vmImage: 'ubuntu-16.04' container: $[ variables['containerImage'] ] steps: - script: | java --version mkdir tmp chmod 777 .travis/gen-ssl.sh .travis/gen-ssl.sh mariadb.example.com tmp cp -R tmp $BUILD_ARTIFACTSTAGINGDIRECTORY displayName: 'create SSL certificates' - task: PublishPipelineArtifact@0 inputs: targetPath: '$(Build.ArtifactStagingDirectory)' artifactName: ssl_certs - job: windowsTest displayName: 'test windows' pool: vmImage: 'windows-2019' dependsOn: - SSLFiles steps: - task: DownloadPipelineArtifact@2 displayName: 'Download SSL files' inputs: artifactName: ssl_certs targetPath: $(System.DefaultWorkingDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download 10.4 server' inputs: buildType: 'specific' project: '550599d3-6165-4abd-8c86-e3f7e53a1847' definition: '3' buildVersionToDownload: 'latestFromBranch' branchName: 'refs/heads/10.4-enterprise' artifactName: 'Windows-Release' targetPath: '$(System.DefaultWorkingDirectory)' - script: | for /f %%a in ('dir /B $(System.DefaultWorkingDirectory)\win_build\mariadb-enterprise-10.*-winx64.msi') do set servername=$(System.DefaultWorkingDirectory)\win_build\%%a echo %servername% msiexec /i %servername% INSTALLDIR=c:\projects\server SERVICENAME=mariadb ALLOWREMOTEROOTACCESS=true /qn c:\projects\server\bin\mysql.exe -e "create database testo" --user=root c:\projects\server\bin\mysql.exe -e "GRANT ALL on *.* to 'someUser'@'%' identified by 'Passw@rd2' with grant option;" --user=root displayName: 'install server' - script: | echo 127.0.0.1 mariadb.example.com >> %WINDIR%\System32\Drivers\Etc\Hosts displayName: 'set hostname' - script: | git submodule init git submodule update displayName: 'update submodule' - script: | cd libmariadb cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=RelWithDebInfo cmake --build . --config RelWithDebInfo cd .. cmake -G "Visual Studio 16 2019" -DCONC_WITH_MSI=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SIGNCODE=0 -DWITH_SSL=SCHANNEL -DWITH_OPENSSL=OFF cmake --build . --config RelWithDebInfo displayName: 'build connector' - task: CopyFiles@2 inputs: contents: 'wininstall/**.msi' targetFolder: $(Build.ArtifactStagingDirectory) - task: PublishPipelineArtifact@1 inputs: targetPath: '$(Build.ArtifactStagingDirectory)' artifactName: msi_package - task: PowerShell@2 inputs: targetType: inline script: | New-Item -Path "HKCU:\Software\ODBC" New-Item -Path "HKCU:\Software\ODBC\ODBC.INI" $regPath = "HKCU:\Software\ODBC\ODBC.INI\test" New-Item -Path $regPath New-ItemProperty -Path $regPath -Name "CONN_TIMEOUT" -Value "0" New-ItemProperty -Path $regPath -Name "DATABASE" -Value "test" New-ItemProperty -Path $regPath -Name "DESCRIPTION" -Value "MariaDB ODBC test" New-ItemProperty -Path $regPath -Name "Driver" -Value "MariaDB ODBC 3.1 Driver" New-ItemProperty -Path $regPath -Name "OPTIONS" -Value "0" New-ItemProperty -Path $regPath -Name "PORT" -Value "3306" New-ItemProperty -Path $regPath -Name "PWD" -Value "Passw@rd2" New-ItemProperty -Path $regPath -Name "SERVER" -Value "mariadb.example.com" New-ItemProperty -Path $regPath -Name "SSLVERIFY" -Value "0" New-ItemProperty -Path $regPath -Name "TCPIP" -Value "1" New-ItemProperty -Path $regPath -Name "UID" -Value "someUser" New-Item -Path "HKCU:\Software\ODBC\ODBC.INI\ODBC Data Sources" New-ItemProperty -Path "HKCU:\Software\ODBC\ODBC.INI\ODBC Data Sources" -Name "test" -Value "MariaDB ODBC 3.1 Driver" displayName: 'set registry' - task: PowerShell@2 inputs: targetType: inline script: | $msifile = Get-ChildItem $env:$(System.DefaultWorkingDirectory)\wininstall\mariadb-connector-odbc*.msi | Select-Object -First 1 Write $msifile msiexec /i $msifile.fullname INSTALLDIR=c:\mariadb-odbc /qn displayName: 'install odbc' - script: | set MARIADB_PLUGIN_DIR=$(System.DefaultWorkingDirectory)\libmariadb\plugins\lib\RelWithDebInfo SET TEST_SCHEMA=test timeout /T 1 cd test ctest -V if %ERRORLEVEL% EQU 0 ( echo Success ) else ( echo exit code is %errorlevel% exit /b %errorlevel% ) displayName: 'run tests' - job: RunInContainer pool: vmImage: 'ubuntu-16.04' displayName: 'test ubuntu bionic' dependsOn: - SSLFiles strategy: matrix: ubuntu-1804: containerImage: ubuntu-1804 containerName: bionic container: $[variables['containerImage']] steps: - task: DownloadPipelineArtifact@2 inputs: artifactName: ssl_certs targetPath: $(System.DefaultWorkingDirectory) - script: /tmp/docker exec -t -u 0 $(containerImage) sh -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" -y install sudo" displayName: Set up sudo - task: DownloadPipelineArtifact@2 displayName: 'Download 10.4 enterprise server artifact files' inputs: source: 'specific' project: '550599d3-6165-4abd-8c86-e3f7e53a1847' artifact: '$(containerImage)' runVersion: 'latestFromBranch' runBranch: 'refs/heads/10.4-enterprise' downloadPath: $(System.DefaultWorkingDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download galera server artifact files' inputs: source: 'specific' project: '550599d3-6165-4abd-8c86-e3f7e53a1847' artifact: $(containerImage) runVersion: 'latestFromBranch' runBranch: 'refs/heads/es-mariadb-4.x' downloadPath: $(System.DefaultWorkingDirectory) - script: | tar xf mariadb-enterprise* sudo ln -fs /usr/share/zoneinfo/UTC /etc/localtime sudo apt-get update && sudo apt-get install -y --no-install-recommends apt-transport-https ca-certificates tzdata pwgen export DEBIAN_FRONTEND="noninteractive" sudo debconf-set-selections <<< "mariadb-server-10.4 mysql-server/root_password password P4ssw@rd" sudo debconf-set-selections <<< "mariadb-server-10.4 mysql-server/root_password_again password P4ssw@rd" sudo apt-get update -y sudo apt-get install --allow-unauthenticated -f -y git libssl-dev libaio1 libaio-dev libxml2 libcurl4 curl libc-dev linux-libc-dev libc-dev-bin libdbi-perl rsync socat libnuma1 zlib1g-dev libreadline5 libjemalloc1 libsnappy1v5 libcrack2 gawk lsof psmisc perl libreadline5 sudo apt-get install --allow-unauthenticated -y --force-yes -m unixodbc-dev cd mariadb-enterprise*/ sudo groupadd mysql sudo useradd -g mysql mysql export PROJ_PATH=`pwd` echo $PROJ_PATH cat <> my.cnf [mysqld] port=3306 max_allowed_packet=16M datadir=$PROJ_PATH/data socket=/tmp/mysql.sock user=mysql ssl-ca=$(System.DefaultWorkingDirectory)/tmp/ca.crt ssl-cert=$(System.DefaultWorkingDirectory)/tmp/server.crt ssl-key=$(System.DefaultWorkingDirectory)/tmp/server.key EOT sudo chown mysql $PROJ_PATH/my.cnf sudo tail -n 5000 $PROJ_PATH/my.cnf sudo chmod 777 $PROJ_PATH sudo ln -s $PROJ_PATH /usr/local/mysql sudo ./scripts/mysql_install_db --defaults-file=$PROJ_PATH/my.cnf --user=mysql sudo chown -R root . sudo chown -R mysql data export PATH=$PATH:$PROJ_PATH/bin/ env: WORKING_DIR: $(System.DefaultWorkingDirectory) displayName: 'install server' - script: | git submodule init git submodule update displayName: 'update submodule' - script: | sudo apt-get install -f -y make cmake #cd libmariadb #cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_SSL=OPENSSL -DCERT_PATH=$(System.DefaultWorkingDirectory)/tmp #make #cd .. export TEST_SCHEMA=testo export TEST_DRIVER=maodbc_test export TEST_DSN=maodbc_test export TEST_SERVER=mariadb.example.com export TEST_UID=someUser export TEST_PASSWORD=Passw@rd2 cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWITH_OPENSSL=ON -DWITH_SSL=OPENSSL . cmake --build . --config RelWithDebInfo displayName: 'Build' - script: | cd mariadb-enterprise*/ sudo ./bin/mysqld --defaults-file=./my.cnf & for i in {30..0}; do if sudo ./bin/mysql -e "SELECT 1" &> /dev/null; then echo 'MySQL connected...' break fi echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then echo >&2 'MySQL init process failed.' sudo ./bin/mysql -e "SELECT 1" exit 1 fi sudo ./bin/mysql -e "CREATE USER 'someUser'@'%' identified by 'Passw@rd2';" sudo ./bin/mysql -e "GRANT ALL on *.* to 'someUser'@'%' identified by 'Passw@rd2' with grant option;" sudo ./bin/mysql -e "CREATE DATABASE testo;" echo "Running tests" cd ../test export ODBCINI="$(System.DefaultWorkingDirectory)/test/odbc.ini" export ODBCSYSINI=$(System.DefaultWorkingDirectory)/test cat $ODBCINI cat $ODBCSYSINI/odbcinst.ini ctest -V if [ $? -ne 0 ]; then exit 1 fi cd $(System.DefaultWorkingDirectory)/mariadb-enterprise*/ sudo ./bin/mysqladmin shutdown env: TEST_DRIVER: maodbc_test TEST_DSN: maodbc_test TEST_SERVER: mariadb.example.com TEST_SOCKET: TEST_SCHEMA: testo TEST_UID: someUser TEST_PASSWORD: Passw@rd2 TEST_SSL_CA_FILE: "$(System.DefaultWorkingDirectory)/tmp/server.crt" TEST_SSL_CLIENT_KEY_FILE: "$(System.DefaultWorkingDirectory)/tmp/client.key" TEST_SSL_CLIENT_CERT_FILE: "$(System.DefaultWorkingDirectory)/tmp/client.crt" TEST_SSL_CLIENT_KEYSTORE_FILE: "$(System.DefaultWorkingDirectory)/tmp/client-keystore.p12" displayName: 'run tests' mariadb-connector-odbc-3.1.15/cmake/000077500000000000000000000000001414431724000171555ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/cmake/COPYING-CMAKE-SCRIPTS000066400000000000000000000024571414431724000221630ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mariadb-connector-odbc-3.1.15/cmake/ConfigureFile.cmake000066400000000000000000000000551414431724000227000ustar00rootroot00000000000000CONFIGURE_FILE(${FILE_IN} ${FILE_OUT} @ONLY) mariadb-connector-odbc-3.1.15/cmake/FindDM.cmake000077500000000000000000000111011414431724000212550ustar00rootroot00000000000000# Copyright (C) 2015,2020 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # FindDM.cmake # # Cmake script to look for driver manager includes and libraries on platforms others than Windows # We expect that the driver manager is UnixODBC IF(WITH_IODBC) SET(ODBC_CONFIG_EXEC iodbc-config) SET(ODBC_CONFIG_INCLUDES --cflags) SET(ODBC_CONFIG_LIBS --libs) SET(ODBC_LIBS iodbc) SET(ODBC_INSTLIBS iodbcinst) ELSE() #UnixODBC SET(ODBC_CONFIG_EXEC odbc_config) SET(ODBC_CONFIG_INCLUDES --include-prefix) SET(ODBC_CONFIG_LIBS --lib-prefix) SET(ODBC_LIBS odbc) SET(ODBC_INSTLIBS odbcinst) ENDIF() IF(ODBC_LIB_DIR AND ODBC_INCLUDE_DIR) MESSAGE(STATUS "Using preset values for DM dirs") ELSE() FIND_PROGRAM(ODBC_CONFIG ${ODBC_CONFIG_EXEC} PATH /usr/bin ${DM_DIR} "${DM_DIR}/bin" ) IF(ODBC_CONFIG) MESSAGE(STATUS "Found ${ODBC_CONFIG_EXEC}: ${ODBC_CONFIG}") EXECUTE_PROCESS(COMMAND ${ODBC_CONFIG} ${ODBC_CONFIG_INCLUDES} OUTPUT_VARIABLE result) STRING(REPLACE "\n" "" ODBC_INCLUDE_DIR ${result}) EXECUTE_PROCESS(COMMAND ${ODBC_CONFIG} ${ODBC_CONFIG_LIBS} OUTPUT_VARIABLE result) STRING(REPLACE "\n" "" ODBC_LIB_DIR ${result}) IF(WITH_IODBC) STRING(REPLACE "-I" "" ODBC_INCLUDE_DIR ${ODBC_INCLUDE_DIR}) STRING(REPLACE "-L" "" ODBC_LIB_DIR ${ODBC_LIB_DIR}) STRING(REGEX REPLACE " +-liodbc -liodbcinst" "" ODBC_LIB_DIR ${ODBC_LIB_DIR}) ENDIF() ELSE() MESSAGE(STATUS "${ODBC_CONFIG_EXEC} is not found ") # Try to find the include directory, giving precedence to special variables SET(LIB_PATHS /usr/local /usr /usr/local/Cellar/libiodbc/3.52.12) IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") SET(LIB_PATHS "${LIB_PATHS}" "/usr/lib/x86_64-linux-gnu") IF(EXISTS "/usr/lib64/") SET(LIB_SUFFIX "lib64" "x86_64-linux-gnu") ELSE() SET(LIB_SUFFIX "lib" "x86_64-linux-gnu") ENDIF() ELSE() SET(LIB_PATHS "${LIB_PATHS}" "/usr/local/lib/i386-linux-gnu" "/usr/lib/i386-linux-gnu" "/usr/local/lib/i686-linux-gnu" "/usr/lib/i686-linux-gnu" "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") SET(LIB_SUFFIX "lib" "i386-linux-gnu" "i686-linux-gnu" "${CMAKE_LIBRARY_ARCHITECTURE}") ENDIF() FIND_PATH(ODBC_INCLUDE_DIR sql.h HINTS ${DM_INCLUDE_DIR} ${DM_DIR} ENV DM_INCLUDE_DIR ENV DM_DIR PATHS /usr/local /usr /usr/local/Cellar/libiodbc/3.52.12 PATH_SUFFIXES include include/iodbc NO_DEFAULT_PATH DOC "Driver Manager Includes") # Giving chance to cmake_(environment)path FIND_PATH(ODBC_INCLUDE_DIR sql.h DOC "Driver Manager Includes") IF(ODBC_INCLUDE_DIR) MESSAGE(STATUS "Found ODBC Driver Manager includes: ${ODBC_INCLUDE_DIR}") ENDIF() # Try to find DM libraries, giving precedence to special variables FIND_PATH(ODBC_LIB_DIR "lib${ODBC_LIBS}.so" HINTS ${DM_LIB_DIR} ${DM_DIR} ENV DM_LIB_DIR ENV DM_DIR PATHS ${LIB_PATHS} PATH_SUFFIXES ${LIB_SUFFIX} NO_DEFAULT_PATH DOC "Driver Manager Libraries") FIND_PATH(ODBC_LIB_DIR "lib${ODBC_LIBS}.so" DOC "Driver Manager Libraries") FIND_PATH(ODBCINST_LIB_DIR "lib${ODBC_INSTLIBS}.so" HINTS ${DM_LIB_DIR} ${DM_DIR} ENV DM_LIB_DIR ENV DM_DIR PATHS ${LIB_PATHS} PATH_SUFFIXES ${LIB_SUFFIX} NO_DEFAULT_PATH DOC "Driver Manager Libraries") FIND_PATH(ODBCINST_LIB_DIR "lib${ODBC_INSTLIBS}.so" DOC "Driver Manager Libraries") ENDIF() ENDIF() IF(ODBC_LIB_DIR AND ODBC_INCLUDE_DIR) MESSAGE(STATUS "Found ODBC Driver Manager libraries: ${ODBC_LIB_DIR} ${ODBCINST_LIB_DIR}") SET(DM_FOUND TRUE) ENDIF() mariadb-connector-odbc-3.1.15/cmake/FindIconv.cmake000066400000000000000000000036331414431724000220430ustar00rootroot00000000000000# Copyright (c) 2010 Michael Bell if (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) # Already in cache, be silent set(ICONV_FIND_QUIETLY TRUE) endif (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) IF(APPLE) find_path(ICONV_INCLUDE_DIR iconv.h PATHS /opt/local/include/ /usr/include/ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ NO_CMAKE_SYSTEM_PATH) ELSE() find_path(ICONV_INCLUDE_DIR iconv.h) ENDIF() IF(APPLE) find_library(ICONV_LIBRARIES NAMES iconv libiconv c PATHS /opt/local/lib/ /usr/lib/ NO_CMAKE_SYSTEM_PATH) SET(ICONV_EXTERNAL TRUE) ELSE() find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2) IF(ICONV_LIBRARIES) SET(ICONV_EXTERNAL TRUE) ELSE() find_library(ICONV_LIBRARIES NAMES c) ENDIF() ENDIF() if (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) set (ICONV_FOUND TRUE) endif (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) IF(ICONV_EXTERNAL) set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) ENDIF() if (ICONV_FOUND) include(CheckCSourceCompiles) CHECK_C_SOURCE_COMPILES(" #include int main(){ iconv_t conv = 0; const char* in = 0; size_t ilen = 0; char* out = 0; size_t olen = 0; iconv(conv, &in, &ilen, &out, &olen); return 0; } " ICONV_SECOND_ARGUMENT_IS_CONST ) endif (ICONV_FOUND) set (CMAKE_REQUIRED_INCLUDES) set (CMAKE_REQUIRED_LIBRARIES) if (ICONV_FOUND) if (NOT ICONV_FIND_QUIETLY) message (STATUS "Found Iconv: ${ICONV_LIBRARIES}") endif (NOT ICONV_FIND_QUIETLY) else (ICONV_FOUND) if (Iconv_FIND_REQUIRED) message (FATAL_ERROR "Could not find Iconv") endif (Iconv_FIND_REQUIRED) endif (ICONV_FOUND) MARK_AS_ADVANCED( ICONV_INCLUDE_DIR ICONV_LIBRARIES ICONV_EXTERNAL ICONV_SECOND_ARGUMENT_IS_CONST ) mariadb-connector-odbc-3.1.15/cmake/SearchLibrary.cmake000066400000000000000000000012071414431724000227110ustar00rootroot00000000000000INCLUDE(CheckFunctionExists) INCLUDE(CheckLibraryExists) FUNCTION(SEARCH_LIBRARY library_name function liblist) IF(${${library_name}}) RETURN() ENDIF() CHECK_FUNCTION_EXISTS(${function} ${function}_IS_SYS_FUNC) # check if function is part of libc IF(HAVE_${function}_IS_SYS_FUNC) SET(${library_name} "" PARENT_SCOPE) RETURN() ENDIF() FOREACH(lib ${liblist}) CHECK_LIBRARY_EXISTS(${lib} ${function} "" HAVE_${function}_IN_${lib}) IF(HAVE_${function}_IN_${lib}) SET(${library_name} ${lib} PARENT_SCOPE) SET(HAVE_${library_name} 1 PARENT_SCOPE) RETURN() ENDIF() ENDFOREACH() ENDFUNCTION() mariadb-connector-odbc-3.1.15/cmake/SetValueMacro.cmake000066400000000000000000000005341414431724000226730ustar00rootroot00000000000000# Macro that checks if variable is defined, otherwise checks env for same name, otherwise uses default value MACRO(SET_VALUE _variable _default_value) IF (NOT ${_variable}) IF(DEFINED ENV{${_variable}}) SET(${_variable} $ENV{${_variable}}) ELSE() SET(${_variable} ${_default_value}) ENDIF() ENDIF() ENDMACRO(SET_VALUE) mariadb-connector-odbc-3.1.15/cmake/connector_c.cmake000066400000000000000000000023211414431724000224510ustar00rootroot00000000000000INCLUDE(FindGit) IF(NOT EXISTS ${CMAKE_SOURCE_DIR}/libmariadb/CMakeLists.txt AND GIT_EXECUTABLE) EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule init WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") EXECUTE_PROCESS(COMMAND "${GIT_EXECUTABLE}" submodule update WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") ENDIF() IF(NOT EXISTS ${CMAKE_SOURCE_DIR}/libmariadb/CMakeLists.txt) MESSAGE(FATAL_ERROR "No MariaDB Connector/C! Run git submodule init git submodule update Then restart the build. ") ENDIF() SET(OPT CONC_) IF (CMAKE_BUILD_TYPE STREQUAL "Debug") SET(CONC_WITH_RTC ON) ENDIF() SET(CONC_WITH_SIGNCODE ${SIGNCODE}) SET(SIGN_OPTIONS ${SIGNTOOL_PARAMETERS}) IF(TARGET zlib) GET_PROPERTY(ZLIB_LIBRARY_LOCATION TARGET zlib PROPERTY LOCATION) ELSE() SET(ZLIB_LIBRARY_LOCATION ${ZLIB_LIBRARY}) ENDIF() SET(CONC_WITH_CURL OFF) SET(CONC_WITH_MYSQLCOMPAT ON) IF (INSTALL_LAYOUT STREQUAL "RPM") SET(CONC_INSTALL_LAYOUT "RPM") ELSE() SET(CONC_INSTALL_LAYOUT "DEFAULT") ENDIF() SET(PLUGIN_INSTALL_DIR ${INSTALL_PLUGINDIR}) SET(MARIADB_UNIX_ADDR ${MYSQL_UNIX_ADDR}) MESSAGE("== Configuring MariaDB Connector/C") ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN) ADD_SUBDIRECTORY(libmariadb) mariadb-connector-odbc-3.1.15/cmake/install.cmake000066400000000000000000000133511414431724000216300ustar00rootroot00000000000000# # Copyright (C) 2013-2016 MariaDB Corporation AB # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the COPYING-CMAKE-SCRIPTS file. # # # This file contains settings for the following layouts: # # - RPM # Built with default prefix=/usr # # # The following va+riables are used and can be overwritten # # INSTALL_LAYOUT installation layout (DEFAULT = standard for tar.gz and zip packages # RPM packages # # INSTALL_BINDIR location of binaries (mariadb_config) # INSTALL_LIBDIR location of libraries # INSTALL_PLUGINDIR location of plugins # INSTALL_DOCDIR location of docs # INSTALL_LICENSEDIR location of license IF(DEB) SET(INSTALL_LAYOUT "DEB") ENDIF() IF(RPM) SET(INSTALL_LAYOUT "RPM") ENDIF() IF(NOT INSTALL_LAYOUT) SET(INSTALL_LAYOUT "DEFAULT") ENDIF() SET(INSTALL_LAYOUT ${INSTALL_LAYOUT} CACHE STRING "Installation layout. Currently supported options are DEFAULT (tar.gz and zip), RPM and DEB") # On Windows we only provide zip and .msi. Latter one uses a different packager. IF(UNIX) IF(INSTALL_LAYOUT MATCHES "RPM|DEB") SET(libmaodbc_prefix "/usr") ELSEIF(INSTALL_LAYOUT STREQUAL "DEFAULT") SET(libmaodbc_prefix ${CMAKE_INSTALL_PREFIX}) ENDIF() ENDIF() IF(CMAKE_DEFAULT_PREFIX_INITIALIZED_BY_DEFAULT) SET(CMAKE_DEFAULT_PREFIX ${libmariadb_prefix} CACHE PATH "Installation prefix" FORCE) ENDIF() # check if the specified installation layout is valid SET(VALID_INSTALL_LAYOUTS "DEFAULT" "RPM" "DEB") LIST(FIND VALID_INSTALL_LAYOUTS "${INSTALL_LAYOUT}" layout_no) IF(layout_no EQUAL -1) MESSAGE(FATAL_ERROR "Invalid installation layout ${INSTALL_LAYOUT}. Please specify one of the following layouts: ${VALID_INSTALL_LAYOUTS}") ENDIF() # This has been done before C/C cmake scripts are included IF(NOT DEFINED INSTALL_LIB_SUFFIX) SET(INSTALL_LIB_SUFFIX "lib" CACHE STRING "Directory, under which to install libraries, e.g. lib or lib64") IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8" AND EXISTS "/usr/lib64/") SET(INSTALL_LIB_SUFFIX "lib64") ENDIF() ENDIF() # # Todo: We don't generate man pages yet, will fix it # later (webhelp to man transformation) # # # DEFAULT layout # SET(INSTALL_BINDIR_DEFAULT "bin") SET(INSTALL_LIBDIR_DEFAULT "${INSTALL_LIB_SUFFIX}/mariadb") SET(INSTALL_PCDIR_DEFAULT "${INSTALL_LIB_SUFFIX}/pkgconfig") SET(INSTALL_INCLUDEDIR_DEFAULT "include/mariadb") SET(INSTALL_DOCDIR_DEFAULT "docs") SET(INSTALL_LICENSEDIR_DEFAULT ${INSTALL_DOCDIR_DEFAULT}) IF(NOT IS_SUBPROJECT) SET(INSTALL_PLUGINDIR_DEFAULT "lib/mariadb/plugin") ENDIF() SET(LIBMARIADB_STATIC_DEFAULT "mariadbclient") # # RPM layout # SET(INSTALL_BINDIR_RPM "bin") IF((CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "s390x") AND CMAKE_SIZEOF_VOID_P EQUAL 8) SET(INSTALL_LIBDIR_RPM "lib64/mariadb") SET(INSTALL_PCDIR_RPM "lib64/pkgconfig") SET(INSTALL_PLUGINDIR_RPM "lib64/mariadb/plugin") ELSE() SET(INSTALL_LIBDIR_RPM "lib/mariadb") SET(INSTALL_PCDIR_RPM "lib/pkgconfig") SET(INSTALL_PLUGINDIR_RPM "lib/mariadb/plugin") ENDIF() SET(INSTALL_INCLUDEDIR_RPM "include/mariadb") SET(INSTALL_DOCDIR_RPM "share/doc/mariadb-connector-odbc") SET(INSTALL_LICENSEDIR_RPM ${INSTALL_DOCDIR_RPM}) SET(LIBMARIADB_STATIC_RPM "mariadbclient") # # DEB layout # Only ia-32 and amd64 here. the list is too long to hardcode it IF(NOT CMAKE_LIBRARY_ARCHITECTURE) IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(CMAKE_LIBRARY_ARCHITECTURE "x86_64-linux-gnu") ELSE() SET(CMAKE_LIBRARY_ARCHITECTURE "i386-linux-gnu") ENDIF() ENDIF() SET(INSTALL_BINDIR_DEB "bin") SET(INSTALL_LIBDIR_DEB "lib/${CMAKE_LIBRARY_ARCHITECTURE}") SET(INSTALL_PCDIR_DEB "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig") SET(INSTALL_PLUGINDIR_DEB "${INSTALL_LIBDIR_DEB}/libmariadb${CPACK_PACKAGE_VERSION_MAJOR}/plugin") SET(INSTALL_INCLUDEDIR_DEB "include/mariadb") SET(INSTALL_DOCDIR_DEB "share/doc/mariadb-connector-odbc") SET(INSTALL_LICENSEDIR_DEB "${INSTALL_DOCDIR_DEB}") SET(LIBMARIADB_STATIC_DEB "mariadb") # # Overwrite defaults # IF(INSTALL_LIBDIR) SET(INSTALL_LIBDIR_${INSTALL_LAYOUT} ${INSTALL_LIBDIR}) ENDIF() IF(INSTALL_PCDIR) SET(INSTALL_PCDIR_${INSTALL_LAYOUT} ${INSTALL_PCDIR}) ENDIF() IF(INSTALL_PLUGINDIR) SET(INSTALL_PLUGINDIR_${INSTALL_LAYOUT} ${INSTALL_PLUGINDIR}) ENDIF() IF(INSTALL_DOCDIR) SET(INSTALL_DOCDIR_${INSTALL_LAYOUT} ${INSTALL_DOCDIR}) ENDIF() IF(INSTALL_LICENSEDIR) SET(INSTALL_LICENSEDIR_${INSTALL_LAYOUT} ${INSTALL_LICENSEDIR}) ENDIF() # Extra INSTALL_PLUGINDIR_CLIENT that overrides any INSTALL_PLUGINDIR override IF(INSTALL_PLUGINDIR_CLIENT) SET(INSTALL_PLUGINDIR_${INSTALL_LAYOUT} ${INSTALL_PLUGINDIR_CLIENT}) ENDIF() IF(INSTALL_INCLUDEDIR) SET(INSTALL_INCLUDEDIR_${INSTALL_LAYOUT} ${INSTALL_INCLUDEDIR}) ENDIF() IF(INSTALL_BINDIR) SET(INSTALL_BINDIR_${INSTALL_LAYOUT} ${INSTALL_BINDIR}) ENDIF() IF(NOT INSTALL_PREFIXDIR) SET(INSTALL_PREFIXDIR_${INSTALL_LAYOUT} ${libmariadb_prefix}) ELSE() SET(INSTALL_PREFIXDIR_${INSTALL_LAYOUT} ${INSTALL_PREFIXDIR}) ENDIF() IF(DEFINED INSTALL_SUFFIXDIR) SET(INSTALL_SUFFIXDIR_${INSTALL_LAYOUT} ${INSTALL_SUFFIXDIR}) ENDIF() FOREACH(dir "BIN" "LIB" "PC" "INCLUDE" "DOC" "LICENSE" "PLUGIN") SET(INSTALL_${dir}DIR ${INSTALL_${dir}DIR_${INSTALL_LAYOUT}}) MARK_AS_ADVANCED(INSTALL_${dir}DIR) MESSAGE(STATUS "MariaDB Connector ODBC: INSTALL_${dir}DIR=${INSTALL_${dir}DIR}") ENDFOREACH() SET(INSTALL_PLUGINDIR_CLIENT ${INSTALL_PLUGINDIR}) MESSAGE(STATUS "MariaDB Connector ODBC: INSTALL_PLUGINDIR_CLIENT=${INSTALL_PLUGINDIR_CLIENT}") MESSAGE(STATUS "Libraries installation dir: ${INSTALL_LIBDIR}") MESSAGE(STATUS "Authentication Plugins installation dir: ${INSTALL_PLUGINDIR}") mariadb-connector-odbc-3.1.15/cmake/linux_x86_toolchain.cmake000066400000000000000000000005531414431724000240660ustar00rootroot00000000000000# toolchain file for building a 32bit version on a 64bit host # Usage: # cmake -DCMAKE_TOOLCHAIN_FILE=linux_86.toolchain.cmake set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR "i686") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") mariadb-connector-odbc-3.1.15/cmake/options_defaults.cmake000066400000000000000000000057721414431724000235540ustar00rootroot00000000000000# # Copyright (C) 2021 MariaDB Corporation AB # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the COPYING-CMAKE-SCRIPTS file. # OPTION(BUILD_INTERACTIVE_TESTS "Build test(s) requiring user interaction" OFF) OPTION(USE_INTERACTIVE_TESTS "Run interactive test(s) with ctest" OFF) IF(WIN32) OPTION(WITH_MSI "Build MSI installation package" ON) OPTION(WITH_SIGNCODE "Digitally sign files" OFF) OPTION(MARIADB_LINK_DYNAMIC "Link Connector/C library dynamically" OFF) OPTION(ALL_PLUGINS_STATIC "Compile all plugins in, i.e. make them static" ON) ELSE() OPTION(WITH_MSI "Build MSI installation package" OFF) OPTION(MARIADB_LINK_DYNAMIC "Link Connector/C library dynamically" ON) SET(BUILD_INTERACTIVE_TESTS OFF) SET(USE_INTERACTIVE_TESTS OFF) OPTION(ALL_PLUGINS_STATIC "Compile all plugins in, i.e. make them static" OFF) ENDIF() IF(USE_INTERACTIVE_TESTS) SET(BUILD_INTERACTIVE_TESTS ON) ENDIF() OPTION(WITH_SSL "Enables use of TLS/SSL library" ON) OPTION(WITH_UNIT_TESTS "Build test suite" ON) OPTION(USE_SYSTEM_INSTALLED_LIB "Use installed in the syctem C/C library and do not build one" OFF) OPTION(DIRECT_LINK_TESTS "Link tests directly against driver library(bypass DM)" OFF) # This is to be used for some testing scenarious, obviously. e.g. testing of the connector installation. OPTION(BUILD_TESTS_ONLY "Build only tests and nothing else" OFF) OPTION(WITH_UNIT_TESTS "Build unit tests" ON) IF(BUILD_TESTS_ONLY) SET(WITH_UNIT_TESTS ON) ENDIF() IF(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") SET(WITH_UNIT_TESTS OFF) ENDIF() IF(NOT EXISTS ${CMAKE_SOURCE_DIR}/libmariadb) SET(USE_SYSTEM_INSTALLED_LIB ON) ENDIF() IF(APPLE) SET(CMAKE_SKIP_BUILD_RPATH FALSE) SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) OPTION(WITH_IODBC "Build with iOdbc" ON) CMAKE_POLICY(SET CMP0042 NEW) CMAKE_POLICY(SET CMP0068 NEW) SET(CMAKE_INSTALL_RPATH "") SET(CMAKE_INSTALL_NAME_DIR "") SET(CMAKE_MACOSX_RPATH ON) ELSE() OPTION(WITH_IODBC "Build with iOdbc" OFF) ENDIF() IF(WIN32) # Currently limiting this feature to Windows only, where it's most probably going to be only used IF(ALL_PLUGINS_STATIC) SET(CLIENT_PLUGIN_AUTH_GSSAPI_CLIENT "STATIC") SET(CLIENT_PLUGIN_DIALOG "STATIC") SET(CLIENT_PLUGIN_CLIENT_ED25519 "STATIC") SET(CLIENT_PLUGIN_CACHING_SHA2_PASSWORD "STATIC") SET(CLIENT_PLUGIN_SHA256_PASSWORD "STATIC") SET(CLIENT_PLUGIN_MYSQL_CLEAR_PASSWORD "STATIC") SET(CLIENT_PLUGIN_MYSQL_OLD_PASSWORD "STATIC") SET(MARIADB_LINK_DYNAMIC OFF) ENDIF() ELSE() # Defaults for creating odbc(inst).ini for tests with Unix/iOdbc IF(WITH_UNIT_TESTS) SET_VALUE(TEST_DRIVER "maodbc_test") SET_VALUE(TEST_DSN "maodbc_test") SET_VALUE(TEST_PORT "3306") SET_VALUE(TEST_SERVER "localhost") SET_VALUE(TEST_SOCKET "") SET_VALUE(TEST_SCHEMA "test") SET_VALUE(TEST_UID "root") SET_VALUE(TEST_PASSWORD "") SET_VALUE(TEST_USETLS "0") ENDIF() ENDIF() mariadb-connector-odbc-3.1.15/cmake/sqlcolattribute.c000066400000000000000000000010331414431724000225370ustar00rootroot00000000000000#include #include SQLRETURN SQL_API SQLColAttribute(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLPOINTER NumericAttributePtr ) { return SQL_SUCCESS; } int main() { return 0; } mariadb-connector-odbc-3.1.15/dsn/000077500000000000000000000000001414431724000166615ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/dsn/ma_odbc_setup.h000066400000000000000000000026371414431724000216460ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2014 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_odbc_setup_h_ #define _ma_odbc_setup_h_ #define MA_WIN_SET_VALUE(Page, Field, Value) \ Edit_SetText(GetDlgItem(hwndTab[(Page)],(Field)), (Value)); #define MA_WIN_SET_MAXLEN(Page, Field, Value) \ Edit_LimitText(GetDlgItem(hwndTab[(Page)],(Field)), (Value)); typedef struct { MADB_DsnKey *Key; int Page; int Item; int MaxLength; /* For DSN_TYPE_CBOXGROUP that is the bit it represents */ my_bool Mandatory; } MADB_DsnMap; #endif /* _ma_odbc_setup_h_ */ mariadb-connector-odbc-3.1.15/dsn/mariadb_odbc_setup.def000066400000000000000000000001011414431724000231370ustar00rootroot00000000000000LIBRARY maodbcs.dll EXPORTS ConfigDSN DSNDialog DSNPrompt mariadb-connector-odbc-3.1.15/dsn/odbc_dsn.c000066400000000000000000000745411414431724000206130ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #include #include #include #include #include #include #include "resource.h" #include #include #pragma comment(lib, "ComCtl32.lib") #define CONNSTR_BUFFER_SIZE 1024 #define LASTPAGE 5 HINSTANCE hInstance; unsigned int CurrentPage= 0; SQLHANDLE Environment= NULL; my_bool DBFilled= FALSE; my_bool CSFilled= FALSE; my_bool ConnectionOK= FALSE; my_bool notCanceled= TRUE; char DSNStr[2048]; HWND hwndTab[7], hwndMain; const int *EffectiveDisabledPages= NULL, *EffectiveDisabledControls= NULL; BOOL OpenCurSelection= TRUE; /* On Windows we are supposed to have schannel, or we can have openssl */ #if defined(_WIN32) || defined(HAVE_OPENSSL) # define SSL_DISABLED 0 #else # define SSL_DISABLED 1 #endif const int DisabledPages[MAODBC_PROMPT_REQUIRED + 1][LASTPAGE + 1]= { { 0, 0, 0, 0, SSL_DISABLED, 0}, { 0, 0, 0, 0, SSL_DISABLED, 0}, { 1, 0, 1, 1, 1, 1} }; const int PromptDisabledControls[]= { txtDsnName, 0 }, EmptyDisabledControls[]= { 0 }; const int* DisabledControls[]= { EmptyDisabledControls, PromptDisabledControls, EmptyDisabledControls }; MADB_DsnMap DsnMap[] = { {&DsnKeys[0], 0, txtDsnName, 64, 1}, {&DsnKeys[1], 0, txtDSNDescription, 64, 0}, {&DsnKeys[5], 1, rbPipe, 0, 0}, {&DsnKeys[6], 1, rbTCP, 0, 0}, {&DsnKeys[7], 1, txtServerName, 128, 0}, {&DsnKeys[8], 1, txtUserName, 64, 0}, {&DsnKeys[9], 1, txtPassword, 64, 0}, {&DsnKeys[10], 1, cbDatabase, 0, 0}, {&DsnKeys[11], 1, txtPort, 5, 0}, {&DsnKeys[12], 2, txtInitCmd, 2048, 0}, {&DsnKeys[13], 2, txtConnectionTimeOut, 5, 0}, {&DsnKeys[39], 2, txtReadTimeOut, 5, 0}, {&DsnKeys[40], 2, txtWriteTimeOut, 5, 0}, {&DsnKeys[36], 2, cbInteractive, 0, 0}, {&DsnKeys[14], 2, ckReconnect, 0, 0}, {&DsnKeys[15], 2, ckConnectPrompt, 0, 0}, {&DsnKeys[16], 2, cbCharset, 0, 0}, {&DsnKeys[34], 2, txtServerKey, 260, 0}, {&DsnKeys[18], 3, txtPluginDir, 260, 0}, {&DsnKeys[38], 3, ckSchParamNoError, 0, 0}, {&DsnKeys[19], 4, txtSslKey, 260, 0}, {&DsnKeys[20], 4, txtSslCert, 260, 0}, {&DsnKeys[21], 4, txtSslCertAuth, 260, 0}, {&DsnKeys[22], 4, txtSslCaPath, 260, 0}, {&DsnKeys[23], 4, txtSslCipher, 32, 0}, {&DsnKeys[24], 4, cbSslVerify, 0, 0}, {&DsnKeys[32], 4, cbTls11, 1, 0}, {&DsnKeys[32], 4, cbTls12, 2, 0}, {&DsnKeys[32], 4, cbTls13, 4, 0}, {&DsnKeys[33], 4, cbForceTls, 0, 0}, {&DsnKeys[27], 4, txtCrl, 0, 0}, {&DsnKeys[25], 4, txtTlsPeerFp, 41, 0}, {&DsnKeys[26], 4, txtTlsPeerFpList, 260, 0}, {NULL, 0, 0, 0, 0} }; #define CBGROUP_BIT(MapIdx) (char)(DsnMap[MapIdx].MaxLength) #define IS_CB_CHECKED(MapIdx) GetButtonState(DsnMap[MapIdx].Page, DsnMap[MapIdx].Item) #define CBGROUP_SETBIT(_Dsn, MapIdx) *GET_FIELD_PTR(_Dsn, DsnMap[MapIdx].Key, char)|= CBGROUP_BIT(MapIdx) #define CBGROUP_RESETBIT(_Dsn, MapIdx) *GET_FIELD_PTR(_Dsn, DsnMap[MapIdx].Key, char)&= ~CBGROUP_BIT(MapIdx) MADB_OptionsMap OptionsMap[]= { {1, rbPipe, MADB_OPT_FLAG_NAMED_PIPE}, {2, ckReconnect, MADB_OPT_FLAG_AUTO_RECONNECT}, {2, ckConnectPrompt, MADB_OPT_FLAG_NO_PROMPT}, {2, ckCompressed, MADB_OPT_FLAG_COMPRESSED_PROTO}, {2, ckUseMycnf, MADB_OPT_FLAG_USE_CNF}, {3, ckIgnoreSchema, MADB_OPT_FLAG_NO_SCHEMA}, {3, ckIgnoreSpace, MADB_OPT_FLAG_IGNORE_SPACE}, {3, ckMultiStmt, MADB_OPT_FLAG_MULTI_STATEMENTS}, {LASTPAGE, ckIgnoreSchema, MADB_OPT_FLAG_NO_SCHEMA}, {LASTPAGE, ckEnableDynamicCursor, MADB_OPT_FLAG_DYNAMIC_CURSOR}, {LASTPAGE, ckDisableDriverCursor, MADB_OPT_FLAG_NO_DEFAULT_CURSOR}, {LASTPAGE, ckDontCacheForwardCursor, MADB_OPT_FLAG_NO_CACHE}, {LASTPAGE, ckForwardCursorOnly, MADB_OPT_FLAG_FORWARD_CURSOR}, {LASTPAGE, ckReturnMatchedRows, MADB_OPT_FLAG_FOUND_ROWS}, {LASTPAGE, ckEnableSQLAutoIsNull, MADB_OPT_FLAG_AUTO_IS_NULL}, {LASTPAGE, ckPadCharFullLength, MADB_OPT_FLAG_PAD_SPACE}, {LASTPAGE, ckNullDate, MADB_OPT_FLAG_ZERO_DATE_TO_MIN}, {LASTPAGE, ckDebug, MADB_OPT_FLAG_DEBUG}, {LASTPAGE, ckReturnMatchedRows, MADB_OPT_FLAG_FOUND_ROWS}, {LASTPAGE, ckIgnoreSpace, MADB_OPT_FLAG_IGNORE_SPACE}, /* last element */ {0, 0, 0} }; void GetDialogFields(void); HBRUSH hbrBg= NULL; void DsnApplyDefaults(MADB_Dsn* Dsn) { /* Setting default port number if Tcp/Ip selected, and the port is 0 */ if (Dsn->IsTcpIp && Dsn->Port == 0) { Dsn->Port = 3306; } } my_bool SetDialogFields() { int i= 0; MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); /* Basically - if dialog does not exist yet */ if (Dsn == NULL) { return TRUE; } while (DsnMap[i].Key) { switch (DsnMap[i].Key->Type) { case DSN_TYPE_STRING: case DSN_TYPE_COMBO: { char *Val= *(char **)((char *)Dsn + DsnMap[i].Key->DsnOffset); if (Val && Val[0]) SetDlgItemText(hwndTab[DsnMap[i].Page], DsnMap[i].Item, Val); break; } case DSN_TYPE_INT: { int Val= *(int *)((char *)Dsn + DsnMap[i].Key->DsnOffset); SetDlgItemInt(hwndTab[DsnMap[i].Page], DsnMap[i].Item, Val, 0); } break; case DSN_TYPE_BOOL: { my_bool Val= *(my_bool *)((char *)Dsn + DsnMap[i].Key->DsnOffset); SendDlgItemMessage(hwndTab[DsnMap[i].Page], DsnMap[i].Item, BM_SETCHECK, Val ? BST_CHECKED : BST_UNCHECKED, 0); } break; case DSN_TYPE_CBOXGROUP: SendDlgItemMessage(hwndTab[DsnMap[i].Page], DsnMap[i].Item, BM_SETCHECK, (*GET_FIELD_PTR(Dsn, DsnMap[i].Key, char) & CBGROUP_BIT(i)) != '\0' ? BST_CHECKED : BST_UNCHECKED, 0); } i++; } i= 0; while (OptionsMap[i].Item != 0) { my_bool Val= (Dsn->Options & OptionsMap[i].value) ? 1 : 0; SendDlgItemMessage(hwndTab[OptionsMap[i].Page], OptionsMap[i].Item, BM_SETCHECK, Val ? BST_CHECKED : BST_UNCHECKED, 0); if (Val && OptionsMap[i].Item == rbPipe) { SendMessage(GetDlgItem(hwndTab[OptionsMap[i].Page], lblServerName), WM_SETTEXT, 0, (LPARAM)"Named pipe:"); ShowWindow(GetDlgItem(hwndTab[OptionsMap[i].Page], lblPort), SW_HIDE); ShowWindow(GetDlgItem(hwndTab[OptionsMap[i].Page], txtPort), SW_HIDE); } i++; } return TRUE; } char *GetFieldStrVal(int Dialog, int Field, char* (*allocator)(size_t)) { int rc; int len= Edit_GetTextLength(GetDlgItem(hwndTab[Dialog], Field)); char *p; if (allocator) { p= allocator(len * sizeof(char) + 2); } else { p= (char *)MADB_CALLOC(len * sizeof(char) + 2); } if (p) rc= Edit_GetText(GetDlgItem(hwndTab[Dialog], Field), p, len + 1); return p; } int GetFieldIntVal(int Dialog, int Field) { int rc= 0; char *p= GetFieldStrVal(Dialog, Field, NULL); if (p) { rc= atoi(p); free(p); } return rc; } my_bool GetButtonState(int Dialog, int Button) { return (my_bool)IsDlgButtonChecked(hwndTab[Dialog], Button); } my_bool SaveDSN(HWND hDlg, MADB_Dsn *Dsn) { DsnApplyDefaults(Dsn); if (Dsn->SaveFile != NULL || MADB_SaveDSN(Dsn)) return TRUE; MessageBox(hDlg, Dsn->ErrorMsg, "Error", MB_OK); return FALSE; } unsigned int GetNextActiveTab(int current_page, int offset) { unsigned int result= current_page + offset; for (; result >= 0 && result <= LASTPAGE && EffectiveDisabledPages[result] != 0; result+= offset) { if (offset > 1) { offset= 1; } else if (offset < -1) { offset= -1; } } if (result < 0 || result > LASTPAGE) { result= current_page; } return result; } #define DISABLE_CONTROLS(page_map) i= 0;\ /* Assuming that controls in the #page_map are sorted by tab page */\ while (page_map[i].Item && page_map[i].Page != CurrentPage && ++i);\ \ while (page_map[i].Item && page_map[i].Page == CurrentPage)\ {\ int j= 0;\ while (EffectiveDisabledControls[j])\ {\ if (EffectiveDisabledControls[j] == page_map[i].Item)\ {\ EnableWindow(GetDlgItem(hwndTab[CurrentPage], EffectiveDisabledControls[j]), FALSE);\ }\ ++j;\ }\ ++i;\ } 1 void DisableControls(MADB_Dsn *Dsn) { int i; DISABLE_CONTROLS(DsnMap); DISABLE_CONTROLS(OptionsMap); } void SetPage(HWND hDlg, int value) { MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); unsigned int new_page= GetNextActiveTab(CurrentPage, value); /* Save if last page or all following pages are disabled */ if (value > 0 && (CurrentPage == LASTPAGE || new_page== CurrentPage)) { GetDialogFields(); if (Dsn->isPrompt || SaveDSN(hDlg, Dsn)) { SendMessage(hwndMain, WM_DESTROY, 0, 0); } return; } ShowWindow(hwndTab[CurrentPage != (unsigned int)(-1) ? CurrentPage : 0], SW_HIDE); CurrentPage= new_page; ShowWindow(hwndTab[CurrentPage], SW_SHOW); DisableControls(Dsn); /* Disabling prev button if needed*/ new_page= GetNextActiveTab(CurrentPage, -1); EnableWindow(GetDlgItem(hwndTab[CurrentPage], PB_PREV), (CurrentPage != new_page) ? TRUE : FALSE); /* Switching caption of the Next/Finish button if needed*/ new_page= GetNextActiveTab(CurrentPage, 1); SendMessage(GetDlgItem(hwndTab[CurrentPage], PB_NEXT), WM_SETTEXT, 0, (CurrentPage == new_page) ? (LPARAM)"Finish" : (LPARAM)"Next >"); SetFocus(hwndTab[CurrentPage]); /* If not a prompt - disable finish button in case of empty DS name(for prompt it may be empty/invalid) TODO: I think it rather has to check if the name is valid DS name */ if (Dsn->isPrompt == MAODBC_CONFIG && CurrentPage == new_page) { EnableWindow(GetDlgItem(hwndTab[CurrentPage], PB_NEXT), Edit_GetTextLength(GetDlgItem(hwndTab[0], txtDsnName)) ? TRUE : FALSE); } } void GetDialogFields() { int i= 0; MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); while (DsnMap[i].Key) { switch (DsnMap[i].Key->Type) { case DSN_TYPE_STRING: case DSN_TYPE_COMBO: { char **p= (char **)((char *)Dsn + DsnMap[i].Key->DsnOffset); if (Dsn->isPrompt != 0 && Dsn->free != NULL) { Dsn->free(*p); } *p= GetFieldStrVal(DsnMap[i].Page, DsnMap[i].Item, Dsn->allocator && Dsn->isPrompt ? Dsn->allocator : NULL); } break; case DSN_TYPE_INT: *(int *)((char *)Dsn + DsnMap[i].Key->DsnOffset)= GetFieldIntVal(DsnMap[i].Page, DsnMap[i].Item); break; case DSN_TYPE_BOOL: *GET_FIELD_PTR(Dsn, DsnMap[i].Key, my_bool)= IS_CB_CHECKED(i); break; case DSN_TYPE_CBOXGROUP: if (IS_CB_CHECKED(i) != '\0') { CBGROUP_SETBIT(Dsn, i); } else { CBGROUP_RESETBIT(Dsn, i); } } ++i; } i= 0; Dsn->Options= 0; while (OptionsMap[i].Item != 0) { if (GetButtonState(OptionsMap[i].Page, OptionsMap[i].Item)) Dsn->Options|= OptionsMap[i].value; i++; } } void DSN_Set_Database(SQLHANDLE Connection) { MADB_Stmt *Stmt= NULL; SQLRETURN ret= SQL_ERROR; char Database[65]; MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); HWND DbCombobox= GetDlgItem(hwndTab[1], cbDatabase); if (DBFilled) return; GetDialogFields(); if (SQLAllocHandle(SQL_HANDLE_STMT, Connection, (SQLHANDLE *)&Stmt) != SQL_SUCCESS) goto end; if (SQLExecDirect((SQLHSTMT)Stmt, (SQLCHAR *)"SHOW DATABASES", SQL_NTS) != SQL_SUCCESS) goto end; SQLBindCol(Stmt, 1, SQL_C_CHAR, Database, 65, 0); ComboBox_ResetContent(DbCombobox); while (SQLFetch(Stmt) == SQL_SUCCESS) { ComboBox_InsertString(DbCombobox, -1, Database); } if (Dsn->Catalog) { int Idx= ComboBox_FindString(DbCombobox, 0, Dsn->Catalog); ComboBox_SetCurSel(DbCombobox, Idx); } ComboBox_SetMinVisible(GetDlgItem(hwndTab[1], cbDatabase), 5); DBFilled= TRUE; end: if (Stmt) SQLFreeHandle(SQL_HANDLE_STMT, (SQLHANDLE)Stmt); } void DSN_Set_CharacterSets(SQLHANDLE Connection) { MADB_Stmt *Stmt= NULL; SQLRETURN ret= SQL_ERROR; char Charset[65]; MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); if (CSFilled) return; GetDialogFields(); if (SQLAllocHandle(SQL_HANDLE_STMT, Connection, (SQLHANDLE *)&Stmt) != SQL_SUCCESS) goto end; if (SQLExecDirect((SQLHSTMT)Stmt, (SQLCHAR *)"select character_set_name from information_schema.collations " "WHERE character_set_name NOT LIKE 'utf16%' AND " "character_set_name NOT LIKE 'utf32%' AND " "character_set_name NOT LIKE 'ucs2' " "group by character_set_name order by character_set_name" , SQL_NTS) != SQL_SUCCESS) goto end; SQLBindCol(Stmt, 1, SQL_C_CHAR, Charset, 65, 0); ComboBox_ResetContent(GetDlgItem(hwndTab[2], cbCharset)); while (SQLFetch(Stmt) == SQL_SUCCESS) ComboBox_InsertString(GetDlgItem(hwndTab[2], cbCharset), -1, Charset); if (Dsn->CharacterSet) { int Idx= ComboBox_FindString(GetDlgItem(hwndTab[2], cbCharset), 0, Dsn->CharacterSet); ComboBox_SetCurSel(GetDlgItem(hwndTab[2], cbCharset), Idx); } ComboBox_SetMinVisible(GetDlgItem(hwndTab[2], cbCharset),5); CSFilled= TRUE; end: if (Stmt) SQLFreeHandle(SQL_HANDLE_STMT, (SQLHANDLE)Stmt); } static SQLRETURN TestDSN(MADB_Dsn *Dsn, SQLHANDLE *Conn, SQLCHAR *ConnStrBuffer) { SQLHANDLE Connection= NULL; SQLRETURN Result; SQLCHAR LocalBuffer[CONNSTR_BUFFER_SIZE], *ConnStr= LocalBuffer; char *InitCommand= Dsn->InitCommand; char *DsName= Dsn->DSNName; char *Description= Dsn->Description; Dsn->InitCommand= NULL; Dsn->DSNName= NULL; Dsn->Description= NULL; if (ConnStrBuffer != NULL) { ConnStr= ConnStrBuffer; } DsnApplyDefaults(Dsn); /* If defaults has changed actual values - let them be reflected in the dialog(if it exists - SetDialogFields cares about that) */ SetDialogFields(); MADB_DsnToString(Dsn, ConnStr, CONNSTR_BUFFER_SIZE); SQLAllocHandle(SQL_HANDLE_DBC, Environment, (SQLHANDLE *)&Connection); assert(Connection != NULL); Result= SQLDriverConnect(Connection, NULL, ConnStr, CONNSTR_BUFFER_SIZE, NULL, 0, NULL, SQL_DRIVER_NOPROMPT); Dsn->InitCommand= InitCommand; Dsn->DSNName= DsName; Dsn->Description= Description; if (Conn != NULL) { *Conn= Connection; } else { SQLDisconnect(Connection); SQLFreeHandle(SQL_HANDLE_DBC, Connection); } return Result; } /* Connstr has to be null-terminated */ char* HidePwd(char *ConnStr) { char *Ptr= ConnStr; while (*Ptr) { BOOL IsPwd= FALSE; char *KeyValBorder= strchr(Ptr, '='); char StopChr= ';'; Ptr= ltrim(Ptr); if (_strnicmp(Ptr, "PWD", 3) == 0 || _strnicmp(Ptr, "PASSWORD", 8) == 0) { IsPwd= TRUE; } if (KeyValBorder != NULL) { Ptr= ltrim(KeyValBorder + 1); } if (*Ptr == '{') { StopChr= '}'; } while (*Ptr && *Ptr != StopChr) { if (IsPwd) { *Ptr= '*'; } ++Ptr; } ++Ptr; } return ConnStr; } void MADB_WIN_TestDsn(my_bool ShowSuccess) { SQLHANDLE Connection; SQLRETURN ret; MADB_Dsn *Dsn= (MADB_Dsn *)GetWindowLongPtr(GetParent(hwndTab[0]), DWLP_USER); SQLCHAR ConnStr[CONNSTR_BUFFER_SIZE]; GetDialogFields(); ret= TestDSN(Dsn, &Connection, ConnStr); if (ShowSuccess) { char Info[1024]; HidePwd(ConnStr); if (SQL_SUCCEEDED(ret)) { char DbmsName[16], DbmsVer[16]; SQLGetInfo(Connection, SQL_DBMS_NAME, DbmsName, sizeof(DbmsName), NULL); SQLGetInfo(Connection, SQL_DBMS_VER, DbmsVer, sizeof(DbmsVer), NULL); _snprintf(Info, sizeof(Info), "Connection successfully established\n\nServer Information: %s %s\n\nConnection String:\n\n%s", DbmsName, DbmsVer, ConnStr); MessageBox(hwndTab[CurrentPage], Info, "Connection test", MB_ICONINFORMATION| MB_OK); } else { SQLCHAR SqlState[6], ErrMsg[SQL_MAX_MESSAGE_LENGTH]; SQLGetDiagRec(SQL_HANDLE_DBC, Connection, 1, SqlState, NULL, ErrMsg, SQL_MAX_MESSAGE_LENGTH, NULL); _snprintf(Info, sizeof(Info), "Connection failed: [%s] %s\n\nConnection String:\n\n%s", SqlState, ErrMsg, ConnStr); MessageBox(hwndTab[CurrentPage], Info, "Connection test", MB_ICONINFORMATION| MB_OK); } } if (SQL_SUCCEEDED(ret)) { ConnectionOK= TRUE; DSN_Set_CharacterSets(Connection); DSN_Set_Database(Connection); SQLDisconnect(Connection); } SQLFreeHandle(SQL_HANDLE_DBC, Connection); } INT_PTR SelectPath(HWND ParentWnd, int BoundEditId, const wchar_t *Caption, BOOL FolderPath, BOOL OpenCurrentSelection) { if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { IFileDialog *PathDialog; if (SUCCEEDED(CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_ALL, &IID_IFileOpenDialog, (void**)&PathDialog))) { HWND BoundEditWnd= GetDlgItem(ParentWnd, BoundEditId); DWORD DialogOptions; TCHAR Path[MAX_PATH]; IShellItem *SelectedItem; int Length; LPWSTR wPath; if (FolderPath && SUCCEEDED(PathDialog->lpVtbl->GetOptions(PathDialog, &DialogOptions))) { PathDialog->lpVtbl->SetOptions(PathDialog, DialogOptions | FOS_PICKFOLDERS); } Edit_GetText(BoundEditWnd, Path, sizeof(Path)); Length= MultiByteToWideChar(GetACP(), 0, Path, -1, NULL, 0); wPath= (SQLWCHAR *)malloc(Length * sizeof(SQLWCHAR)); if (wPath == NULL) { return FALSE; } MultiByteToWideChar(GetACP(), 0, Path, -1, wPath, Length); SHCreateItemFromParsingName(wPath, NULL, &IID_IShellItem, (void**)&SelectedItem); if (OpenCurrentSelection) { PathDialog->lpVtbl->SetFolder(PathDialog, SelectedItem); } else { PathDialog->lpVtbl->SetDefaultFolder(PathDialog, SelectedItem); } SelectedItem->lpVtbl->Release(SelectedItem); free(wPath); PathDialog->lpVtbl->SetTitle(PathDialog, Caption); if (SUCCEEDED(PathDialog->lpVtbl->Show(PathDialog, ParentWnd))) { if (SUCCEEDED(PathDialog->lpVtbl->GetResult(PathDialog, &SelectedItem))) { LPWSTR SelectedValue; if (SUCCEEDED(SelectedItem->lpVtbl->GetDisplayName(SelectedItem, SIGDN_FILESYSPATH, &SelectedValue))) { BOOL Error; /* TODO: I guess conversions from/to utf8 has to be done when syncing edits with DSN */ WideCharToMultiByte(GetACP(), 0, SelectedValue, -1, Path, sizeof(Path), NULL, &Error); Edit_SetText(BoundEditWnd, Path); CoTaskMemFree(SelectedValue); return TRUE; } SelectedItem->lpVtbl->Release(SelectedItem); } } PathDialog->lpVtbl->Release(PathDialog); } CoUninitialize(); } return FALSE; } INT_PTR CALLBACK DialogDSNProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { INT_PTR res; switch(uMsg) { case WM_CTLCOLORDLG: if (!hbrBg) hbrBg= CreateSolidBrush(RGB(255,255,255)); return (INT_PTR)hbrBg; break; case WM_CTLCOLORSTATIC: { HDC hdcStatic = (HDC)wParam; SetTextColor(hdcStatic, RGB(0, 0, 0)); SetBkMode(hdcStatic, TRANSPARENT); return (INT_PTR)hbrBg; } case WM_COMMAND: switch(LOWORD(wParam)) { case IDCANCEL: SendMessage(GetParent(hDlg), WM_CLOSE, 0, 0); notCanceled= FALSE; return TRUE; case PB_PREV: SetPage(hDlg, -1); return TRUE; case PB_NEXT: SetPage(hDlg, 1); return TRUE; case pbTestDSN: MADB_WIN_TestDsn(TRUE); return TRUE; case cbDatabase: case cbCharset: if(HIWORD(wParam) == CBN_DROPDOWN) if (!ConnectionOK) { MessageBox(hwndTab[CurrentPage], "To load catalogs and character sets please test connection first", "Warning", MB_OK); return FALSE; } return TRUE; case pbPlugindirBrowse: return SelectPath(hDlg, txtPluginDir, L"Select Plugins Directory", /* Folder*/ TRUE, TRUE); case pbCaPathBrowse: res= SelectPath(hDlg, txtSslCaPath, L"Select CA Path", TRUE, OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbKeyBrowse: res= SelectPath(hDlg, txtSslKey, L"Select Client Private Key File", /* File */ FALSE, OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbCertBrowse: res= SelectPath(hDlg, txtSslCert, L"Select Public Key Certificate File", FALSE, OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbCaCertBrowse: res= SelectPath(hDlg, txtSslCertAuth, L"Select Certificate Authority Certificate File", FALSE,OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbServerKeyBrowse: res= SelectPath(hDlg, txtServerKey, L"Select Server Public Key File", FALSE, OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbFpListBrowse: res= SelectPath(hDlg, txtTlsPeerFpList, L"Select File with SHA1 fingerprints of server certificates", FALSE, OpenCurSelection); OpenCurSelection= OpenCurSelection && !res; return res; case pbCrlBrowse: res = SelectPath(hDlg, txtCrl, L"Select PEM File Certificate Revocation List(CRL)", FALSE, OpenCurSelection); OpenCurSelection = OpenCurSelection && !res; return res; case rbTCP: case rbPipe: if (HIWORD(wParam) == BN_CLICKED) { SendMessage(GetDlgItem(hwndTab[CurrentPage], lblServerName), WM_SETTEXT, 0, (LOWORD(wParam) == rbTCP) ? (LPARAM)"Server name:" : (LPARAM)"Named pipe:"); ShowWindow(GetDlgItem(hwndTab[CurrentPage], lblPort), (LOWORD(wParam) == rbTCP) ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(hwndTab[CurrentPage], txtPort), (LOWORD(wParam) == rbTCP) ? SW_SHOW : SW_HIDE); } return TRUE; } break; } return FALSE; } INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_CTLCOLORDLG: if (!hbrBg) hbrBg= CreateSolidBrush(RGB(255,255,255)); return (INT_PTR)hbrBg; break; case WM_CLOSE: if(MessageBox(hDlg, TEXT("Close the program?"), TEXT("Close"), MB_ICONQUESTION | MB_YESNO) == IDYES) { notCanceled= FALSE; DestroyWindow(hDlg); } return TRUE; case WM_DESTROY: DestroyWindow(hDlg); PostQuitMessage(0); return TRUE; case WM_INITDIALOG: { static int Dialogs[] = {Page_0, Page_1, Page_2, Page_3, Page_4, Page_5}; int i; RECT rc; GetWindowRect(hDlg, &rc); for (i= 0; i <= LASTPAGE; ++i) { hwndTab[i]= CreateDialog(hInstance, MAKEINTRESOURCE(Dialogs[i]), hDlg, DialogDSNProc); SetWindowPos(hwndTab[i], 0, 120, 5, (rc.right-rc.left)-120, (rc.bottom-rc.top), SWP_NOZORDER | SWP_NOACTIVATE); ShowWindow(hwndTab[i], (i == 0) ? SW_SHOW : SW_HIDE); } i= 0; while (DsnMap[i].Key) { if (DsnMap[i].MaxLength) MA_WIN_SET_MAXLEN(DsnMap[i].Page, DsnMap[i].Item, DsnMap[i].MaxLength); ++i; } return TRUE; } } return FALSE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { hInstance= hModule; return TRUE; } void CenterWindow(HWND hwndWindow) { RECT rectWindow; int nWidth, nHeight, nScreenWidth, nScreenHeight; GetWindowRect(hwndWindow, &rectWindow); nWidth = rectWindow.right - rectWindow.left; nHeight = rectWindow.bottom - rectWindow.top; nScreenWidth = GetSystemMetrics(SM_CXSCREEN); nScreenHeight = GetSystemMetrics(SM_CYSCREEN); MoveWindow(hwndWindow, (nScreenWidth - nWidth) / 2, (nScreenHeight - nHeight) / 2, nWidth, nHeight, TRUE); } BOOL DSNDialog(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes, MADB_Dsn *Dsn) { MSG msg; BOOL ret; char *DsnName= NULL; my_bool DsnExists= FALSE; char Delimiter= ';'; if (Dsn->isPrompt < 0 || Dsn->isPrompt > MAODBC_PROMPT_REQUIRED) { Dsn->isPrompt= MAODBC_CONFIG; } if (lpszAttributes) { Delimiter= '\0'; DsnName= strchr((char *)lpszAttributes, '='); } if (DsnName) { ++DsnName; /* In case of prompting we are supposed to show dialog even DSN name is incorrect */ if (!Dsn->isPrompt && !SQLValidDSN(DsnName)) { if (hwndParent) { MessageBox(hwndParent, "Validation of data source name failed", "Error", MB_ICONERROR | MB_OK); } return FALSE; } } if (!DsnName && Dsn && Dsn->DSNName) { DsnName= Dsn->DSNName; } else if (DsnName && Dsn) { MADB_RESET(Dsn->DSNName, DsnName); } /* Even if DsnName invalid(in case of prompt) - we should not have problem */ DsnExists= MADB_DSN_Exists(DsnName); SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &Environment); SQLSetEnvAttr(Environment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); switch (fRequest) { case ODBC_ADD_DSN: if (DsnExists) { if (hwndParent) { if (MessageBox(hwndParent, "Data source name already exists, do you want to replace it?", "Question", MB_ICONQUESTION | MB_YESNO) != IDYES) { return FALSE; } } } MADB_DSN_SetDefaults(Dsn); MADB_ParseConnString(Dsn, (char *)lpszAttributes, SQL_NTS, Delimiter); /* Need to set driver after connstring parsing, and before saving */ if (lpszDriver) { Dsn->Driver= _strdup(lpszDriver); } /* If we don't have parent window - we are not supposed to show the dialog, but only perform operation, if sufficient data has been provided */ if (hwndParent == NULL) { if (SQL_SUCCEEDED(TestDSN(Dsn, NULL, NULL))) { return MADB_SaveDSN(Dsn); } else { return FALSE; } } break; case ODBC_CONFIG_DSN: /* i.e. not a prompt */ if (Dsn->isPrompt == MAODBC_CONFIG && Dsn->SaveFile == NULL) { if (!DsnExists) { if (hwndParent != NULL) { MessageBox(0, "Data source name not found", "Error", MB_ICONERROR | MB_OK); } return FALSE; } else if (!MADB_ReadConnString(Dsn, (char *)lpszAttributes, SQL_NTS, Delimiter)) { SQLPostInstallerError(ODBC_ERROR_INVALID_DSN, Dsn->ErrorMsg); return FALSE; } if (hwndParent == NULL) { if (SQL_SUCCEEDED(TestDSN(Dsn, NULL, NULL))) { return MADB_SaveDSN(Dsn); } else { return FALSE; } } } } InitCommonControls(); EffectiveDisabledPages= DisabledPages[Dsn->isPrompt]; EffectiveDisabledControls= DisabledControls[Dsn->isPrompt]; if (fRequest == ODBC_ADD_DSN && Dsn->isPrompt == MAODBC_CONFIG && Dsn->DSNName != NULL) { EffectiveDisabledControls= PromptDisabledControls; } notCanceled= TRUE; hwndMain= CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc, 0); SetWindowLongPtr(hwndMain, DWLP_USER, (LONG_PTR)Dsn); /* Setting first not disabled page */ CurrentPage= -1; SetPage(hwndMain, 1); SetDialogFields(); CenterWindow(hwndMain); ShowWindow(hwndMain, SW_SHOW); while((ret = GetMessage(&msg, 0, 0, 0)) != 0) { if(ret == -1) break; if(!IsDialogMessage(hwndTab[CurrentPage], &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } SQLFreeHandle(SQL_HANDLE_ENV, Environment); if (notCanceled && fRequest == ODBC_CONFIG_DSN && DsnName != NULL && strcmp(DsnName, Dsn->DSNName) != 0) { SQLRemoveDSNFromIni(DsnName); } return notCanceled; } BOOL INSTAPI ConfigDSN( HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes) { MADB_Dsn Dsn; if (!hwndParent && fRequest != ODBC_REMOVE_DSN) { /* SQLPostInstallerError(ODBC_ERROR_INVALID_HWND, "Invalid Window Handle (NULL)"); return FALSE; */ } switch (fRequest) { case ODBC_ADD_DSN: case ODBC_CONFIG_DSN: memset(&Dsn, 0, sizeof(MADB_Dsn)); return DSNDialog(hwndParent, fRequest, lpszDriver, lpszAttributes, &Dsn); case ODBC_REMOVE_DSN: { char *Val= strchr((char *)lpszAttributes, '='); if (Val) { ++Val; return SQLRemoveDSNFromIni(Val); } } default: return FALSE; } } BOOL __stdcall DSNPrompt(HWND hwnd, MADB_Dsn *Dsn) { return DSNDialog(hwnd, ODBC_CONFIG_DSN, NULL, NULL, Dsn); } mariadb-connector-odbc-3.1.15/dsn/odbc_dsn.rc000066400000000000000000000360401414431724000207650ustar00rootroot00000000000000// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "WinResrc.h" #ifndef IDC_STATIC # define IDC_STATIC (-1) #endif ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // German (Germany) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) LANGUAGE LANG_GERMAN, SUBLANG_GERMAN #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_DIALOG1 DIALOGEX 0, 0, 385, 173 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Create a new Data Source to MariaDB" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL 106,IDC_STATIC,"Static",SS_BITMAP,0,0,80,170 END Page_0 DIALOGEX 0, 0, 299, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU | WS_TABSTOP FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Welcome to the MariaDB ODBC Data Source Wizard!",IDC_STATIC,7,7,287,25,0,WS_EX_TRANSPARENT LTEXT "This wizard will help you to create an ODBC data source that you can use to connect to a MariaDB server.",IDC_STATIC,7,33,287,21,0,WS_EX_TRANSPARENT LTEXT "What name do you want to use to refer to your data source ?",IDC_STATIC,7,60,287,12,0,WS_EX_TRANSPARENT LTEXT "Name:",IDC_STATIC,44,78,22,8,0,WS_EX_TRANSPARENT EDITTEXT txtDsnName,73,77,169,14,ES_AUTOHSCROLL | WS_GROUP LTEXT "How do you want to describe the data source ?",IDC_STATIC,7,101,287,12,0,WS_EX_TRANSPARENT LTEXT "Description:",IDC_STATIC,27,117,39,8,0,WS_EX_TRANSPARENT EDITTEXT txtDSNDescription,73,116,169,14,ES_AUTOHSCROLL PUSHBUTTON "< Previous",PB_PREV,50,149,50,14,WS_DISABLED PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 EDITTEXT txtDriver,233,7,53,15,ES_AUTOHSCROLL | NOT WS_VISIBLE END Tab_Cursor DIALOGEX 0, 0, 256, 172 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Don't cache results of forward only cursors",IDC_CHECK3, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,40,153,10 CONTROL "Force use of forward only cursors",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,55,124,10 CONTROL "Prefetch rows",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,70,61,10 CONTROL "Pad Char to fulllength with SPACE",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,113,124,10 CONTROL "Return SQL_NULL_DATA for zero date",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,129,139,10 END Tab_Connection DIALOGEX 0, 0, 256, 172 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL "Allow big result sets",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,78,79,10,WS_EX_TRANSPARENT END Page_1 DIALOGEX 0, 0, 299, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "How do you want to connect to MariaDB",IDC_STATIC,9,7,130,10,0,WS_EX_TRANSPARENT LTEXT "Server Name:",lblServerName,86,21,49,11,0,WS_EX_TRANSPARENT | WS_EX_RIGHT LTEXT "Please specify a user name and password to connect to MariaDB",IDC_STATIC,9,54,207,8,0,WS_EX_TRANSPARENT LTEXT "Please specify a database to use",IDC_STATIC,9,105,207,8,0,WS_EX_TRANSPARENT LTEXT "User name:",IDC_STATIC,26,70,38,8,0,WS_EX_TRANSPARENT | WS_EX_RIGHT LTEXT "Password:",IDC_STATIC,30,88,34,8,0,WS_EX_TRANSPARENT | WS_EX_RIGHT LTEXT "Database:",IDC_STATIC,20,121,34,8,0,WS_EX_TRANSPARENT | WS_EX_RIGHT CONTROL "TCP/IP",rbTCP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,7,21,38,10,WS_EX_TRANSPARENT CONTROL "Named Pipe",rbPipe,"Button",BS_AUTORADIOBUTTON,7,33,53,10,WS_EX_TRANSPARENT EDITTEXT txtServerName,139,18,141,14,ES_AUTOHSCROLL EDITTEXT txtPort,140,34,40,14,ES_AUTOHSCROLL LTEXT "Port:",lblPort,86,35,49,11,0,WS_EX_TRANSPARENT | WS_EX_RIGHT EDITTEXT txtUserName,71,68,141,14,ES_AUTOHSCROLL EDITTEXT txtPassword,71,86,142,14,ES_PASSWORD | ES_AUTOHSCROLL PUSHBUTTON "&Test DSN",pbTestDSN,223,86,50,14 COMBOBOX cbDatabase,71,119,93,80,CBS_DROPDOWN | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "< Previous",PB_PREV,50,149,50,14 PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 END Page_2 DIALOGEX 0, 0, 299, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "Do you want to send initial statement(s) after establishing connection to MariaDB?",IDC_STATIC,7,0,264,8,0,WS_EX_TRANSPARENT EDITTEXT txtInitCmd,74,11,197,39,ES_MULTILINE | ES_AUTOHSCROLL | WS_VSCROLL LTEXT "Statement(s):",IDC_STATIC,20,11,46,8,0,WS_EX_TRANSPARENT RTEXT "Timeouts in sec",IDC_STATIC,6,55,60,10,0,WS_EX_TRANSPARENT RTEXT "Connection:",IDC_STATIC,17,69,39,10,0,WS_EX_TRANSPARENT EDITTEXT txtConnectionTimeOut,57,68,30,12,ES_AUTOHSCROLL RTEXT "Read:",IDC_STATIC,89,69,20,10,0,WS_EX_TRANSPARENT EDITTEXT txtReadTimeOut,110,68,30,12,ES_AUTOHSCROLL RTEXT "Write:",IDC_STATIC,142,69,22,10,0,WS_EX_TRANSPARENT EDITTEXT txtWriteTimeOut,165,68,30,12,ES_AUTOHSCROLL CONTROL "Client is interactive",cbInteractive,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,200,69,84,10,WS_EX_TRANSPARENT CONTROL "Enable automatic reconnect",ckReconnect,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,83,107,10,WS_EX_TRANSPARENT CONTROL "Don't prompt when connecting",ckConnectPrompt,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,97,113,10,WS_EX_TRANSPARENT CONTROL "Use compression",ckCompressed,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,163,83,107,10,WS_EX_TRANSPARENT CONTROL "Read odbc section from my.cnf",ckUseMycnf,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,163,97,198,10,WS_EX_TRANSPARENT COMBOBOX cbCharset,113,110,160,80,CBS_DROPDOWN | CBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP RTEXT "Connection Character Set:",IDC_STATIC,7,113,96,8,0,WS_EX_TRANSPARENT RTEXT "Server RSA public key:",IDC_STATIC,15,128,88,8,0,WS_EX_TRANSPARENT EDITTEXT txtServerKey,113,127,110,12,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbServerKeyBrowse,224,126,30,14 PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "< Previous",PB_PREV,50,149,50,14,WS_DISABLED PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 END Page_3 DIALOGEX 0, 0, 299, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN GROUPBOX "Miscellanous Options",IDC_STATIC,7,7,277,124,0,WS_EX_TRANSPARENT CONTROL "Ignore schema in column specification",ckIgnoreSchema, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,25,136,10,WS_EX_TRANSPARENT CONTROL "Allow multiple statements",ckMultiStmt,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,39,97,10,WS_EX_TRANSPARENT CONTROL "No error on schema parameters in catalog functions",ckSchParamNoError,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,53,177,10,WS_EX_TRANSPARENT LTEXT "Plugins Directory",IDC_STATIC,15,67,56,8,0,WS_EX_TRANSPARENT EDITTEXT txtPluginDir,72,67,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbPlugindirBrowse,183,65,30,14 // CONTROL "Disable catalog support",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,39,91,10,WS_EX_TRANSPARENT // CONTROL "Ignore space after function names",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,53,127,10,WS_EX_TRANSPARENT PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "< Previous",PB_PREV,50,149,50,14,WS_DISABLED PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 END Page_4 DIALOGEX -10, -9, 320, 188 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN GROUPBOX "TLS Settings",IDC_STATIC,1,-2,303,151,0,WS_EX_TRANSPARENT LTEXT "Key",IDC_STATIC,15,10,56,8,0,WS_EX_TRANSPARENT EDITTEXT txtSslKey,84,10,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbKeyBrowse,195,8,30,14 LTEXT "Certificate",IDC_STATIC,15,24,56,8,0,WS_EX_TRANSPARENT EDITTEXT txtSslCert,84,24,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbCertBrowse,195,22,30,14 LTEXT "Certificate Authority",IDC_STATIC,15,38,68,8,0,WS_EX_TRANSPARENT EDITTEXT txtSslCertAuth,84,38,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbCaCertBrowse,195,36,30,14 LTEXT "CA Path",IDC_STATIC,15,52,56,8,0,WS_EX_TRANSPARENT EDITTEXT txtSslCaPath,84,52,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbCaPathBrowse,195,50,30,14 LTEXT "Cipher",IDC_STATIC,15,66,56,8,0,WS_EX_TRANSPARENT EDITTEXT txtSslCipher,84,66,110,10,ES_AUTOHSCROLL CONTROL "Verify Certificate",cbSslVerify,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,80,68,10,WS_EX_TRANSPARENT CONTROL "Force TLS Use",cbForceTls,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,84,80,97,10,WS_EX_TRANSPARENT LTEXT "Permit only specific TLS versions",IDC_STATIC,15,91,110,8,0,WS_EX_TRANSPARENT CONTROL "v.1.1",cbTls11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,91,30,10,WS_EX_TRANSPARENT CONTROL "v.1.2",cbTls12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,155,91,30,10,WS_EX_TRANSPARENT CONTROL "v.1.3",cbTls13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,91,30,10,WS_EX_TRANSPARENT LTEXT "CRL File",IDC_STATIC,15,104,68,8,0,WS_EX_TRANSPARENT EDITTEXT txtCrl,84,104,110,10,ES_AUTOHSCROLL PUSHBUTTON "Browse",pbCrlBrowse,195,102,30,14 LTEXT "TLS Peer Fingerprint", IDC_STATIC, 15, 118, 68, 8, 0, WS_EX_TRANSPARENT EDITTEXT txtTlsPeerFp, 84, 118, 110, 10, ES_AUTOHSCROLL LTEXT "Fingerprints List File", IDC_STATIC, 15, 132, 68, 8, 0, WS_EX_TRANSPARENT EDITTEXT txtTlsPeerFpList, 84, 132, 110, 10, ES_AUTOHSCROLL PUSHBUTTON "Browse", pbFpListBrowse, 195, 130, 30, 14 PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "< Previous",PB_PREV,50,149,50,14,WS_DISABLED PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 END Page_5 DIALOGEX 0, 0, 299, 182 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN PUSHBUTTON "Cancel",IDCANCEL,178,149,50,14 PUSHBUTTON "Next >",PB_NEXT,104,149,50,14 PUSHBUTTON "< Previous",PB_PREV,50,149,50,14,WS_DISABLED PUSHBUTTON "Help",IDCANCEL4,236,149,50,14 GROUPBOX "Cursor/Results",IDC_STATIC,7,7,277,132,0,WS_EX_TRANSPARENT CONTROL "Enable dynamic cursors",ckEnableDynamicCursor,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,19,91,10,WS_EX_TRANSPARENT CONTROL "Disable driver provided cursor support",ckDisableDriverCursor, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,33,138,10,WS_EX_TRANSPARENT CONTROL "Debug",ckDebug,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,197,19,37,10,WS_EX_TRANSPARENT CONTROL "Return matched rows instead of affected rows",ckReturnMatchedRows, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,48,165,10,WS_EX_TRANSPARENT CONTROL "Enable SQL_AUTO_IS_NULL",ckEnableSQLAutoIsNull,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,63,105,10,WS_EX_TRANSPARENT CONTROL "Allow spaces after function names",ckIgnoreSpace, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,78,124,10,WS_EX_TRANSPARENT CONTROL "Return SQL_NULL_DATA for zero date",ckNullDate,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,93,139,10,WS_EX_TRANSPARENT // CONTROL "Don't cache results of forward only cursors",ckDontCacheForwardCursor, // "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,108,153,10,WS_EX_TRANSPARENT CONTROL "Force use of forward only cursors",ckForwardCursorOnly,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,108,124,10,WS_EX_TRANSPARENT // CONTROL "Prefetch rows",ckPrefetchRows,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,197,33,61,10,WS_EX_TRANSPARENT END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_DIALOG1, DIALOG BEGIN LEFTMARGIN, 3 BOTTOMMARGIN, 172 END Page_0, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END Tab_Cursor, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 249 TOPMARGIN, 7 BOTTOMMARGIN, 163 END Tab_Metadata, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 249 TOPMARGIN, 7 BOTTOMMARGIN, 163 END Tab_Connection, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 249 TOPMARGIN, 7 BOTTOMMARGIN, 163 END Page_1, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END Page_2, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END Page_3, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END Page_4, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END Page_5, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 294 TOPMARGIN, 7 BOTTOMMARGIN, 173 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Bitmap // IDB_BITMAP1 BITMAP "odbc_logo_left.bmp" #endif // German (Germany) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED mariadb-connector-odbc-3.1.15/dsn/odbc_logo_left.bmp000066400000000000000000004036261414431724000223350ustar00rootroot00000000000000BM6(x `  y]}ieJxIws}`Q}]nBrMz_DtqIw}BrZ_WKyBrBrBrlSLzYTCsBrSBrBrcYZnKyBrMz_CsWKy}BrO{IxSaFuBrIxCsCsMzlBrMz_N{Cs}BrWYSFuDtWfBrdBrHv_Brc}BrCsSplZJxCsBrFuBrBr_N{}BriSUvUiHvO{BrBr_Etv}BraSBrlKyBrbqsBrBr_BrP}}BrCsSl~R~WR~T|kz}BrIxBrBr_l`Br}BrYSGvHvBrBrIwBrLyBr`EtR~xBrEtQ}_KycUf}BrjzSe`BrzR~zmHvFuCs[HvCsBrMzBrR~_iBrN{WBr`}BrFuSCsDtBrBrBrBr`USP}\Q}wBrBrpBrSBrwIwsBrgBrZSyzKy]ZBrBrBrBrIx]qSBrBrGv[uYvCT1pS«j3 d+c2d+d+~N*rϽfhIP,s?X6w\ʷîd+QayIfT\k`<&d+d+fGƺx]b?&^`hYeuVjUiVcrXZb^@.d+e,iKŹuYc/De<^<^Ee\LFd+d+Q.neF2{xl`\YV{OqRdz[D9d+}M)uB^D6=`<_=_=`Nfb9d+q<vd4trf\ZV}TyPuNoPbwa5q<ͺ¬d+T`u@bAdAdBeEh[UYd+n8«h1uxk^ZXU|RxOsMoU[hc-uY{K&b1 GiChEiEiFjGk\VXd+eFp;oXG|~|}|pb]YWUzRwOsOn[MHs?˸d+YY`GlHmHnJnJoKq^SQd+|L(rf]\ZXU|SyPuRja=%tX~N*d-OoKqLrLsNsNtPu_MDh1X6r>zJ%U3rUɾkMxaRug`^][ZV~U{QwY`mf1 w[ĸrlN~O+i2 d+d+\Z_OvOvPwPxQxRzUxc3tA|ad+d+d+`/ _2]4_2d+T1o]3i2ʿȳux]dEn9e=!enzedba^][YW}\k~^Z^`I]=Z~;X{9Vx:Tu`5d+`YXYZZ\\]]_{d+d+d+d+d+IM]<^<^<^<^T?7h1iKqc_vqiihfecba__^\[YXWV}U{SyQwPuOsMpKoJmIkGhEgDeBbSWad+c<]]^^`aabdG5d+`>(YPPd+^3=\<^<^<^c-sWtAy~wqllkjihgfdcba`^]\[YXW~V}T{SzQxPvOtMqKoMkc,d-aabbcceeetd+ZPN=^R_td+V=1<^<^CUrd+˸Ƚg1~wollllllkjiigfedca`_^][ZYXV}T|Sy]LEd+eeeffgghie8UZf?b@bMfb/ S@:<^KJTd+{kA$wmlllllllllllkihgfedcba_^]\[qd+iiijjkkllhfhMbCfDgDhNcd+SAY7~p<f1 ~fU~xollllllllllllllllld\RxGiN[of-Ʊy^wEd+g2 {`Myolllllllllllllllf^U|KnC`c.̹dze|K'd+g1 }cQvllllllllllllllhaWNrDeZE:e̹m{K&d+j9rd|qllllllllllllkcZQxIkRWbwEgHi2n@tlllllllllllle^U|LpMb~d+wEg1wlvlllllllllllh`XPuJkc-ʶU3i6vlllllllllljc[TzMp^D3qT¶s?qL1tlllllllllle_W~QuYY`vDcCd+}qllllllllliaZU{Vid+td+~slylllllllllkd^WTwd,о}cd+~rlllllllllga[V~bA+{iJe.vllllllllljd_Z^YZdD|K'h5ymlllllllllgb]]om7e,kN:ynlllllllllid`^d+jd+osymllllllllllgc`c< vCe2 klllllllllljfccUOkM¶d+gRElllllllllllifdwf-w\d+jllllllllllkige7mq<e?#lllllllllllkigmwd+ͻd+ikqlllllllllllkkgcde-˸x]e2 klllllllllllllh\Vh0ȽvCgVKllllllllllllllimui6ȳǼe,iwllllllllllllllke?#qme3klllllllllllllklgN=dD|K'fE-lllllkiuir~kllj|jlij|h]YmOl6 gJ8pnllimtd+d+e3klkjxixjwiwlhddgo:kN9iF,d+d+g[Tlliuip{iujvllf@&q=iB&~vkinvithjqllimtyH#qTe-reoqwhjokljd+ƻ|L(d+zP2{tf?#d+d+ȴpSr>d+d+d+d+d+oG)h6d+d+d+μfbBtAd+d+d+d+d+ȳ|R/g/_>mariadb-connector-odbc-3.1.15/dsn/resource.h000066400000000000000000000116471414431724000206720ustar00rootroot00000000000000//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by odbc_dsn.rc // #define PB_NEXT 3 #define IDCANCEL3 4 #define PB_PREV 5 #define IDCANCEL4 6 #define IDD_DIALOG1 101 #define Tab_DSN 102 #define Page_0 103 #define Tab_Cursor 104 #define IDB_BITMAP1 105 #define Tab_Metadata 106 #define Tab_Connection 108 #define Tab_Connect 109 #define Page_1 110 #define Page_2 111 #define Page_3 112 #define Page_4 114 #define Page_5 115 #define DSN_Name 1002 #define DSN_Description 1003 #define DSN_Database 1005 #define DSN_UserName 1013 #define DSN_Password 1014 #define DSN_Test 1015 #define IDC_CHECK2 1017 #define rbTCP 1018 #define IDC_CHECK3 1019 #define rbPipe 1020 #define IDC_CHECK4 1021 #define txtServerName 1022 #define IDC_CHECK5 1023 #define lblServerName 1024 #define txtPort 1026 #define lblPort 1028 #define IDC_CHECK8 1029 #define IDC_CHECK9 1030 #define txtServer 1031 #define txtUserName 1032 #define IDC_EDIT4 1033 #define txtPassword 1034 #define pbTestDSN 1035 #define IDC_COMBO1 1036 #define cbDatabase 1037 #define txtInitCmd 1038 #define ckReconnect 1039 #define cbCharset 1040 #define ckConnectPrompt 1041 #define IDC_EDIT1 1042 #define txtConnectionTimeOut 1043 #define txtReadTimeOut 1230 #define txtWriteTimeOut 1231 #define txtDriver 1200 #define ckEnableDC 1045 #define ckReconnect2 1046 #define ckDisableDriverCursorSupport 1047 #define ckNoCacheForwardOnly 1048 #define ckPrefetch 1049 #define IckUseIS 1050 #define ckUseIS 1051 #define ckIgnoreSchema 1052 #define txtDsnName 1053 #define txtDSNDescription 1054 #define ckEnableDynamicCursor 1100 #define ckDisableDriverCursor 1101 #define ckDontCacheForwardCursor 1102 #define ckForwardCursorOnly 1103 #define ckReturnMatchedRows 1104 #define ckEnableSQLAutoIsNull 1105 #define ckPadCharFullLength 1106 #define ckNullDate 1107 #define ckPrefetchRows 1108 #define ckCompressed 1109 #define ckDebug 1110 #define ckPrefetchRows2 1111 #define ckIgnoreSpace 1112 #define ckMultiStmt 1113 #define ckMatchedRows 1114 #define txtPluginDir 1115 #define pbPlugindirBrowse 1116 #define txtSslKey 1117 #define txtSslCert 1118 #define txtSslCertAuth 1119 #define txtSslCaPath 1120 #define txtSslCipher 1121 #define cbSslVerify 1122 #define pbCaPathBrowse 1123 #define pbKeyBrowse 1124 #define pbCertBrowse 1125 #define pbCaCertBrowse 1126 #define ckUseMycnf 1127 /* TLSVERSION checkbox group. Id determines bit */ #define cbTls11 1128 #define cbTls12 1129 #define cbTls13 1131 /* End of TLSVERSION */ #define cbForceTls 1300 #define txtServerKey 1200 #define pbServerKeyBrowse 1201 #define txtTlsPeerFp 1210 #define txtTlsPeerFpList 1211 #define pbFpListBrowse 1212 #define txtCrl 1213 #define pbCrlBrowse 1214 #define cbInteractive 1220 #define ckSchParamNoError 1221 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 107 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1044 #define _APS_NEXT_SYMED_VALUE 113 #endif #endif mariadb-connector-odbc-3.1.15/dsn_test.c000066400000000000000000000107141414431724000200670ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013 SkySQL AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #include #include #include typedef BOOL (*DSNDialog)(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes); int main() { HMODULE hmod= NULL; BOOL ret; DSNDialog DsnFunc= NULL; HWND hWnd; DWORD dwProcID= GetCurrentProcessId(); const char *driver= getenv("TEST_DRIVER"); if (driver == NULL) { printf("Test requires environment variable TEST_DRIVER to be set!\n"); return 1; } else { printf("# Using Driver=%s for testing.\n", driver); } hWnd= GetConsoleWindow(); if (hWnd == NULL) { hWnd= GetTopWindow(GetDesktopWindow()); while (hWnd) { DWORD dwWndProcID = 0; GetWindowThreadProcessId(hWnd, &dwWndProcID); if (dwWndProcID == dwProcID) break; hWnd = GetNextWindow(hWnd, GW_HWNDNEXT); } } if ((hmod= LoadLibrary("maodbcs.dll"))) { if (DsnFunc= (DSNDialog)GetProcAddress(hmod, "ConfigDSN")) { ret= DsnFunc(NULL, ODBC_ADD_DSN, driver, "DSN=dsn_test\0OPTIONS=2\0\0"); printf("%s 1 Null hWnd and not enough info\n", ret ? "not ok" : "ok"); printf("# The dialog is supposed to show up now - please complete info for connection\n"); ret= DsnFunc(hWnd, ODBC_ADD_DSN, driver, "DSN=dsn_test\0OPTIONS=2\0\0"); if (ret != FALSE) { printf("ok 2 hWnd and not enough info\n"); ret= DsnFunc(NULL, ODBC_ADD_DSN, driver, "DSN=dsn_test\0OPTIONS=2\0\0"); printf("%s 3 Null hWnd trying to add existing dsn \n", ret ? "not ok" : "ok"); ret= DsnFunc(NULL, ODBC_CONFIG_DSN, driver, "DSN=dsn_test\0UID=garbage\0PWD=DoubleGarbage\0OPTIONS=2\0\0"); printf("%s 4 Null hWnd trying to config with insufficient data \n", ret ? "not ok" : "ok"); printf("# The dialog asking if you want to overwrite existing DSN is supposed to show up. Please say 'No'. Otherwise please cancel config dialog\n"); ret= DsnFunc(hWnd, ODBC_ADD_DSN, driver, "DSN=dsn_test\0UID=garbage\0PWD=DoubleGarbage\0OPTIONS=2\0\0"); printf("%s 5 Replace Prompt \n", ret ? "not ok" : "ok"); ret= DsnFunc(NULL, ODBC_CONFIG_DSN, driver, "DSN=inexistent_dsn\0UID=garbage\0PWD=DoubleGarbage\0OPTIONS=2\0\0"); printf("%s 6 Null hWnd trying to config inexisting DSN\n", ret ? "not ok" : "ok"); ret= DsnFunc(NULL, ODBC_CONFIG_DSN, driver, "DSN=inexistent_dsn\0UID=garbage\0PWD=DoubleGarbage\0OPTIONS=2\0\0"); printf("%s 7 hWnd trying to config inexisting DSN\n", ret ? "not ok" : "ok"); ret= DsnFunc(NULL, ODBC_CONFIG_DSN, driver, "DSN=dsn_test\0OPTIONS=0\0\0"); printf("%s 8 Null hWnd config with sufficient data \n", ret ? "ok" : "not ok"); printf("# Please make sure that in dialog Named pipe is selected, and all options are un-checked\n"); ret= DsnFunc(hWnd, ODBC_CONFIG_DSN, driver, "DSN=dsn_test\0NamedPipe=1\0\0"); printf("%s 9 hWnd config with sufficient data \n", ret ? "ok" : "not ok"); } else { printf("not ok 2 hWnd and not enough info\n"); printf("skip 3\n"); printf("skip 4\n"); printf("skip 5\n"); printf("skip 6\n"); printf("skip 7\n"); printf("skip 8\n"); printf("skip 9\n"); } ret= DsnFunc(hWnd, ODBC_REMOVE_DSN, getenv("TEST_DRIVER"), "DSN=dsn_test\0\0"); printf("%s 10 removing dsn\n", ret ? "ok" : "not ok"); } FreeLibrary(hmod); } return 0; } mariadb-connector-odbc-3.1.15/libmaodbc.pc.in000066400000000000000000000010471414431724000207440ustar00rootroot00000000000000# # pkg_config.pc.in # # pkg_config configuration file # For a detailed description of options, please visit # Dan Nicholson’s Guide to pkg-config (http://www.freedesktop.org/wiki/Software/pkg-config/) # prefix=@libmaodbc_prefix@ includedir=${prefix}/@INSTALL_INCLUDEDIR@/@SUFFIX_INSTALL_DIR@ libdir=${prefix}/@INSTALL_LIBDIR@/@SUFFIX_INSTALL_DIR@ Name: libmariadb Version: @CPACK_PACKAGE_VERSION@ Description: MariaDB Connector/ODBC dynamic library Cflags: -I${includedir} Libs: -L${libdir} -lmaodbc Libs.private: @extra_dynamic_LDFLAGS@ mariadb-connector-odbc-3.1.15/ma_api_internal.c000066400000000000000000000323641414431724000213730ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2020 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /** * "Internal" ODBC API functions - functions, that have to be called internally if API * function needs to be executed * * Calling SQLFunction itself inside the connector on non-Windows platforms will result * in the driver manager function instead of our own function. */ #include /* {{{ MA_SQLAllocHandle */ SQLRETURN MA_SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr) { SQLRETURN ret= SQL_ERROR; switch(HandleType) { case SQL_HANDLE_DBC: EnterCriticalSection(&((MADB_Env *)InputHandle)->cs); MADB_CLEAR_ERROR(&((MADB_Env *)InputHandle)->Error); if ((*OutputHandlePtr= (SQLHANDLE)MADB_DbcInit((MADB_Env *)InputHandle)) != NULL) { ret= SQL_SUCCESS; } LeaveCriticalSection(&((MADB_Env *)InputHandle)->cs); break; case SQL_HANDLE_DESC: EnterCriticalSection(&((MADB_Dbc *)InputHandle)->cs); MADB_CLEAR_ERROR(&((MADB_Dbc *)InputHandle)->Error); if ((*OutputHandlePtr= (SQLHANDLE)MADB_DescInit((MADB_Dbc *)InputHandle, MADB_DESC_UNKNOWN, TRUE)) != NULL) { ret= SQL_SUCCESS; } LeaveCriticalSection(&((MADB_Dbc *)InputHandle)->cs); break; case SQL_HANDLE_ENV: if ((*OutputHandlePtr= (SQLHANDLE)MADB_EnvInit()) != NULL) { ret= SQL_SUCCESS; } break; case SQL_HANDLE_STMT: { MADB_Dbc *Connection= (MADB_Dbc *)InputHandle; MDBUG_C_ENTER(InputHandle, "MA_SQLAllocHandle(Stmt)"); MDBUG_C_DUMP(InputHandle, InputHandle, 0x); MDBUG_C_DUMP(InputHandle, OutputHandlePtr, 0x); MADB_CLEAR_ERROR(&Connection->Error); if (!CheckConnection(Connection)) { MADB_SetError(&Connection->Error, MADB_ERR_08003, NULL, 0); break; } ret= MADB_StmtInit(Connection, OutputHandlePtr); MDBUG_C_DUMP(InputHandle, *OutputHandlePtr, 0x); MDBUG_C_RETURN(InputHandle,ret, &Connection->Error); } break; default: /* todo: set error message */ break; } return ret; } /* }}} */ /* {{{ MA_SQLBindParameter */ SQLRETURN MA_SQLBindParameter(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MDBUG_C_ENTER(Stmt->Connection, "SQLBindParameter"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, ParameterNumber, u); MDBUG_C_DUMP(Stmt->Connection, InputOutputType, d); MDBUG_C_DUMP(Stmt->Connection, ValueType, d); MDBUG_C_DUMP(Stmt->Connection, ParameterType, d); MDBUG_C_DUMP(Stmt->Connection, ColumnSize, u); MDBUG_C_DUMP(Stmt->Connection, DecimalDigits, d); MDBUG_C_DUMP(Stmt->Connection, ParameterValuePtr, 0x); MDBUG_C_DUMP(Stmt->Connection, BufferLength, d); MDBUG_C_DUMP(Stmt->Connection, StrLen_or_IndPtr, 0x); MADB_CHECK_STMT_HANDLE(Stmt,stmt); ret= Stmt->Methods->BindParam(Stmt, ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ MA_SQLCancel */ SQLRETURN MA_SQLCancel(SQLHSTMT StatementHandle) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret= SQL_ERROR; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLCancel"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); if (TryEnterCriticalSection(&Stmt->Connection->cs)) { LeaveCriticalSection(&Stmt->Connection->cs); ret= Stmt->Methods->StmtFree(Stmt, SQL_CLOSE); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } else { MYSQL *MariaDb, *Kill=Stmt->Connection->mariadb; char StmtStr[30]; if (!(MariaDb= mysql_init(NULL))) { ret= SQL_ERROR; goto end; } if (!(mysql_real_connect(MariaDb, Kill->host, Kill->user, Kill->passwd, "", Kill->port, Kill->unix_socket, 0))) { mysql_close(MariaDb); goto end; } _snprintf(StmtStr, 30, "KILL QUERY %ld", mysql_thread_id(Kill)); if (mysql_query(MariaDb, StmtStr)) { mysql_close(MariaDb); goto end; } mysql_close(MariaDb); ret= SQL_SUCCESS; } end: LeaveCriticalSection(&Stmt->Connection->cs); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* {{{ MA_SQLEndTran */ SQLRETURN MA_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { SQLRETURN ret= SQL_SUCCESS; switch (HandleType) { case SQL_HANDLE_ENV: { MADB_Env *Env= (MADB_Env *)Handle; MADB_List *List= Env->Dbcs; for (List= Env->Dbcs; List; List= List->next) ((MADB_Dbc *)List->data)->Methods->EndTran((MADB_Dbc *)List->data, CompletionType); } break; case SQL_HANDLE_DBC: { MADB_Dbc *Dbc= (MADB_Dbc *)Handle; if (!Dbc->mariadb) MADB_SetError(&Dbc->Error, MADB_ERR_08002, NULL, 0); else Dbc->Methods->EndTran(Dbc, CompletionType); ret= Dbc->Error.ReturnValue; } break; default: /* todo: Do we need to set an error ?! */ break; } return ret; } /* }}} */ /* {{{ MA_SQLGetDiagRecW */ SQLRETURN SQL_API MA_SQLGetDiagRecW(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLWCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLWCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr) { if (!Handle) return SQL_INVALID_HANDLE; /* Maria ODBC driver doesn't support error lists, so only the first record can be retrieved */ if (RecNumber != 1) return SQL_NO_DATA_FOUND; switch (HandleType) { case SQL_HANDLE_DBC: { MADB_Dbc *Dbc= (MADB_Dbc *)Handle; return MADB_GetDiagRec(&Dbc->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, TRUE, Dbc->Environment->OdbcVersion); } break; case SQL_HANDLE_STMT: { MADB_Stmt *Stmt= (MADB_Stmt *)Handle; return MADB_GetDiagRec(&Stmt->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, TRUE, Stmt->Connection->Environment->OdbcVersion); } break; case SQL_HANDLE_DESC: { MADB_Desc *Desc= (MADB_Desc *)Handle; return MADB_GetDiagRec(&Desc->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, TRUE, SQL_OV_ODBC3); } break; case SQL_HANDLE_ENV: { MADB_Env *Env= (MADB_Env *)Handle; return MADB_GetDiagRec(&Env->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, TRUE, Env->OdbcVersion); } default: return SQL_ERROR; break; } } /* }}} */ /* {{{ MA_SQLGetConnectAttr */ SQLRETURN MA_SQLGetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MDBUG_C_ENTER(Dbc, "SQLGetConnectAttr"); MDBUG_C_DUMP(Dbc, Attribute, d); MDBUG_C_DUMP(Dbc, ValuePtr, 0x); MDBUG_C_DUMP(Dbc, BufferLength, d); MDBUG_C_DUMP(Dbc, StringLengthPtr, 0x); ret= Dbc->Methods->GetAttr(Dbc, Attribute, ValuePtr, BufferLength, StringLengthPtr, FALSE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLGetDiagRec */ SQLRETURN MA_SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr) { SQLRETURN ret= SQL_ERROR; if (!Handle) MDBUG_RETURN(SQL_INVALID_HANDLE); if (RecNumber < 1 || BufferLength < 0) MDBUG_RETURN(SQL_ERROR); /* Maria ODBC driver doesn't support error lists, so only the first record can be retrieved */ if (RecNumber != 1) MDBUG_RETURN(SQL_NO_DATA_FOUND); switch (HandleType) { case SQL_HANDLE_DBC: { MADB_Dbc *Dbc= (MADB_Dbc *)Handle; MDBUG_C_ENTER(Dbc, "SQLGetDiagRec"); MDBUG_C_DUMP(Dbc, HandleType, d); MDBUG_C_DUMP(Dbc, Handle, 0x); MDBUG_C_DUMP(Dbc, MessageText, 0x); MDBUG_C_DUMP(Dbc, BufferLength, d); MDBUG_C_DUMP(Dbc, TextLengthPtr, 0x); ret= MADB_GetDiagRec(&Dbc->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *) MessageText, BufferLength, TextLengthPtr, FALSE, Dbc->Environment->OdbcVersion); } break; case SQL_HANDLE_STMT: { MADB_Stmt *Stmt= (MADB_Stmt *)Handle; MDBUG_C_ENTER(Stmt->Connection, "SQLGetDiagRec"); MDBUG_C_DUMP(Stmt->Connection, HandleType, d); MDBUG_C_DUMP(Stmt->Connection, Handle, 0x); MDBUG_C_DUMP(Stmt->Connection, MessageText, 0x); MDBUG_C_DUMP(Stmt->Connection, BufferLength, d); MDBUG_C_DUMP(Stmt->Connection, TextLengthPtr, 0x); ret= MADB_GetDiagRec(&Stmt->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, FALSE, Stmt->Connection->Environment->OdbcVersion); } break; case SQL_HANDLE_DESC: { MADB_Desc *Desc= (MADB_Desc *)Handle; MDBUG_C_ENTER(Desc->Dbc, "SQLGetDiagRec"); MDBUG_C_DUMP(Desc->Dbc, HandleType, d); MDBUG_C_DUMP(Desc->Dbc, Handle, 0x); MDBUG_C_DUMP(Desc->Dbc, MessageText, 0x); MDBUG_C_DUMP(Desc->Dbc, BufferLength, d); MDBUG_C_DUMP(Desc->Dbc, TextLengthPtr, 0x); ret= MADB_GetDiagRec(&Desc->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, FALSE, SQL_OV_ODBC3); } break; case SQL_HANDLE_ENV: { MADB_Env *Env= (MADB_Env *)Handle; ret= MADB_GetDiagRec(&Env->Error, RecNumber, (void *)SQLState, NativeErrorPtr, (void *)MessageText, BufferLength, TextLengthPtr, FALSE, Env->OdbcVersion); } break; } MDBUG_RETURN(ret); } /* }}} */ /* {{{ MA_SQLGetStmtAttr */ SQLRETURN MA_SQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; return Stmt->Methods->GetAttr(Stmt, Attribute, ValuePtr, BufferLength, StringLengthPtr); } /* }}} */ /* {{{ SQLSetConnectAttr */ SQLRETURN MA_SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MDBUG_C_ENTER(Dbc, "SQLSetConnectAttr"); MDBUG_C_DUMP(Dbc, Attribute, d); MDBUG_C_DUMP(Dbc, ValuePtr, 0x); MDBUG_C_DUMP(Dbc, StringLength, d); ret= Dbc->Methods->SetAttr(Dbc, Attribute, ValuePtr, StringLength, FALSE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLSetStmtAttr */ SQLRETURN MA_SQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MDBUG_C_ENTER(Stmt->Connection, "SQLSetStmtAttr"); MDBUG_C_DUMP(Stmt->Connection, Attribute, d); MDBUG_C_DUMP(Stmt->Connection, ValuePtr, 0x); MDBUG_C_DUMP(Stmt->Connection, StringLength, d); ret= Stmt->Methods->SetAttr(Stmt, Attribute, ValuePtr, StringLength); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ mariadb-connector-odbc-3.1.15/ma_api_internal.h000066400000000000000000000057521414431724000214010ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2020 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _MA_API_INTERNAL_ #define _MA_API_INTERNAL_ /** * "Internal" ODBC API functions - functions, that have to be called internally if API * function needs to be executed * * Calling SQLFunction itself inside the connector on non-Windows platforms will result * in the driver manager function instead of our own function. */ SQLRETURN MA_SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr); SQLRETURN MA_SQLBindParameter(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr); SQLRETURN MA_SQLCancel(SQLHSTMT StatementHandle); SQLRETURN MA_SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType); SQLRETURN SQL_API MA_SQLGetDiagRecW(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLWCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLWCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr); SQLRETURN MA_SQLGetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr); SQLRETURN MA_SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr); SQLRETURN MA_SQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr); SQLRETURN MA_SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength); SQLRETURN MA_SQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength); #endif mariadb-connector-odbc-3.1.15/ma_bulk.c000066400000000000000000000307751414431724000176670ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2017,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* Code allowing to deploy MariaDB bulk operation functionality. * i.e. adapting ODBC param arrays to MariaDB arrays */ #include #define MAODBC_DATTIME_AS_PTR_ARR 1 static BOOL CanUseStructArrForDatetime(MADB_Stmt *Stmt) { #ifdef MAODBC_DATTIME_AS_PTR_ARR return FALSE; #endif return TRUE; } char MADB_MapIndicatorValue(SQLLEN OdbcInd) { switch (OdbcInd) { case SQL_NTS: return STMT_INDICATOR_NTS; case SQL_COLUMN_IGNORE: return STMT_INDICATOR_IGNORE; case SQL_NULL_DATA: return STMT_INDICATOR_NULL; case SQL_PARAM_IGNORE: return STMT_INDICATOR_IGNORE_ROW; /*STMT_INDICATOR_DEFAULT*/ } return '\0'; } BOOL MADB_AppBufferCanBeUsed(SQLSMALLINT CType, SQLSMALLINT SqlType) { switch (CType) { case CHAR_BINARY_TYPES: /*if (SqlType != SQL_BIT) { break; }*/ case WCHAR_TYPES: case SQL_C_NUMERIC: case DATETIME_TYPES: return FALSE; } return TRUE; } void MADB_CleanBulkOperData(MADB_Stmt *Stmt, unsigned int ParamOffset) { if (MADB_DOING_BULK_OPER(Stmt)) { MADB_DescRecord *CRec; void *DataPtr= NULL; MYSQL_BIND *MaBind= NULL; int i; for (i= ParamOffset; i < MADB_STMT_PARAM_COUNT(Stmt); ++i) { if ((CRec= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ)) != NULL) { MaBind= &Stmt->params[i - ParamOffset]; DataPtr= GetBindOffset(Stmt->Apd, CRec, CRec->DataPtr, 0, CRec->OctetLength); if (MaBind->buffer != DataPtr) { switch (CRec->ConciseType) { case DATETIME_TYPES: if (CanUseStructArrForDatetime(Stmt) == FALSE) { MADB_FREE(MaBind->buffer); break; } /* Otherwise falling through and do the same as for others */ case SQL_C_WCHAR: case SQL_C_NUMERIC: { unsigned int i; for (i= 0; i < Stmt->Bulk.ArraySize; ++i) { MADB_FREE(((char**)MaBind->buffer)[i]); } } /* falling through */ default: MADB_FREE(MaBind->buffer); } } MADB_FREE(MaBind->length); MADB_FREE(MaBind->u.indicator); } } Stmt->Bulk.ArraySize= 0; Stmt->Bulk.HasRowsToSkip= 0; } } SQLRETURN MADB_InitIndicatorArray(MADB_Stmt *Stmt, MYSQL_BIND *MaBind, char InitValue) { MaBind->u.indicator= MADB_ALLOC(Stmt->Bulk.ArraySize); if (MaBind->u.indicator == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } memset(MaBind->u.indicator, InitValue, Stmt->Bulk.ArraySize); return SQL_SUCCESS; } SQLRETURN MADB_SetBulkOperLengthArr(MADB_Stmt *Stmt, MADB_DescRecord *CRec, SQLLEN *OctetLengthPtr, SQLLEN *IndicatorPtr, void *DataPtr, MYSQL_BIND *MaBind, BOOL VariableLengthMadbType) { /* Leaving it so far here commented, but it comlicates things w/out much gains */ /*if (sizeof(SQLLEN) == sizeof(long) && MADB_AppBufferCanBeUsed()) { if (OctetLengthPtr) { MaBind->length= OctetLengthPtr; } } else {*/ unsigned int row; if (VariableLengthMadbType) { MaBind->length= MADB_REALLOC(MaBind->length, Stmt->Bulk.ArraySize*sizeof(long)); if (MaBind->length == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } } for (row= 0; row < Stmt->Apd->Header.ArraySize; ++row, DataPtr= (char*)DataPtr + CRec->OctetLength) { if (Stmt->Apd->Header.ArrayStatusPtr != NULL && Stmt->Apd->Header.ArrayStatusPtr[row] == SQL_PARAM_IGNORE) { Stmt->Bulk.HasRowsToSkip= 1; continue; } if ((OctetLengthPtr != NULL && OctetLengthPtr[row] == SQL_NULL_DATA) || (IndicatorPtr != NULL && IndicatorPtr[row] != SQL_NULL_DATA)) { RETURN_ERROR_OR_CONTINUE(MADB_SetIndicatorValue(Stmt, MaBind, row, SQL_NULL_DATA)); continue; } if ((OctetLengthPtr != NULL && OctetLengthPtr[row] == SQL_COLUMN_IGNORE) || (IndicatorPtr != NULL && IndicatorPtr[row] != SQL_COLUMN_IGNORE)) { RETURN_ERROR_OR_CONTINUE(MADB_SetIndicatorValue(Stmt, MaBind, row, SQL_COLUMN_IGNORE)); continue; } if (VariableLengthMadbType) { MaBind->length[row]= (unsigned long)MADB_CalculateLength(Stmt, OctetLengthPtr != NULL ? &OctetLengthPtr[row] : NULL, CRec, DataPtr); } } return SQL_SUCCESS; } /* {{{ MADB_InitBulkOperBuffers */ /* Allocating data and length arrays, if needed, and initing them in certain cases. DataPtr should be ensured to be not NULL */ SQLRETURN MADB_InitBulkOperBuffers(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void *DataPtr, SQLLEN *OctetLengthPtr, SQLLEN *IndicatorPtr, SQLSMALLINT SqlType, MYSQL_BIND *MaBind) { BOOL VariableLengthMadbType= TRUE; MaBind->buffer_length= 0; MaBind->buffer_type= MADB_GetMaDBTypeAndLength(CRec->ConciseType, &MaBind->is_unsigned, &MaBind->buffer_length); /* For fixed length types MADB_GetMaDBTypeAndLength has set buffer_length */ if (MaBind->buffer_length != 0) { VariableLengthMadbType= FALSE; } switch (CRec->ConciseType) { case CHAR_BINARY_TYPES: if (SqlType == SQL_BIT) { CRec->InternalBuffer= MADB_CALLOC(Stmt->Bulk.ArraySize); MaBind->buffer_length= 1; break; } case DATETIME_TYPES: if (CanUseStructArrForDatetime(Stmt) == TRUE) { CRec->InternalBuffer= MADB_ALLOC(Stmt->Bulk.ArraySize*sizeof(MYSQL_TIME)); MaBind->buffer_length= sizeof(MYSQL_TIME); break; } /* Otherwise falling thru and allocating array of pointers */ case WCHAR_TYPES: case SQL_C_NUMERIC: CRec->InternalBuffer= MADB_CALLOC(Stmt->Bulk.ArraySize*sizeof(char*)); MaBind->buffer_length= sizeof(char*); break; default: MaBind->buffer= DataPtr; if (MaBind->buffer_length == 0) { MaBind->buffer_length= sizeof(char*); } } if (MaBind->buffer != DataPtr) { MaBind->buffer= CRec->InternalBuffer; if (MaBind->buffer == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } CRec->InternalBuffer= NULL; /* Need to reset this pointer, so the memory won't be freed (accidentally) */ } return MADB_SetBulkOperLengthArr(Stmt, CRec, OctetLengthPtr, IndicatorPtr, DataPtr, MaBind, VariableLengthMadbType); } /* }}} */ /* {{{ MADB_SetIndicatorValue */ SQLRETURN MADB_SetIndicatorValue(MADB_Stmt *Stmt, MYSQL_BIND *MaBind, unsigned int row, SQLLEN OdbcIndicator) { if (MaBind->u.indicator == NULL) { RETURN_ERROR_OR_CONTINUE(MADB_InitIndicatorArray(Stmt, MaBind, STMT_INDICATOR_NONE)); } MaBind->u.indicator[row]= MADB_MapIndicatorValue(OdbcIndicator); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_ExecuteBulk */ /* Assuming that bulk insert can't go with DAE(and that unlikely ever changes). And that it has been checked before this call, and we can't have DAE here */ SQLRETURN MADB_ExecuteBulk(MADB_Stmt *Stmt, unsigned int ParamOffset) { unsigned int i, IndIdx= -1; unsigned long Dummy; for (i= ParamOffset; i < ParamOffset + MADB_STMT_PARAM_COUNT(Stmt); ++i) { MADB_DescRecord *CRec, *SqlRec; SQLLEN *IndicatorPtr= NULL; SQLLEN *OctetLengthPtr= NULL; void *DataPtr= NULL; MYSQL_BIND *MaBind= &Stmt->params[i - ParamOffset]; SQLULEN row, Start= Stmt->ArrayOffset; if ((CRec= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ)) && (SqlRec= MADB_DescGetInternalRecord(Stmt->Ipd, i, MADB_DESC_READ))) { /* check if parameter was bound */ if (!CRec->inUse) { return MADB_SetError(&Stmt->Error, MADB_ERR_07002, NULL, 0); } if (MADB_ConversionSupported(CRec, SqlRec) == FALSE) { return MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); } MaBind->length= NULL; IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->IndicatorPtr, 0, sizeof(SQLLEN)); OctetLengthPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->OctetLengthPtr, 0, sizeof(SQLLEN)); DataPtr= GetBindOffset(Stmt->Apd, CRec, CRec->DataPtr, 0, CRec->OctetLength); /* If these are the same pointers, setting indicator to NULL to simplify things a bit */ if (IndicatorPtr == OctetLengthPtr) { IndicatorPtr= NULL; } /* Well, specs kinda say, that both values and lenghts arrays should be set(in instruction to param array operations) But there is no error/sqlstate for the case if any of those pointers is not set. Thus we assume that is possible */ if (DataPtr == NULL) { /* Special case - DataPtr is not set, we treat it as all values are NULL. Setting indicators and moving on next param */ RETURN_ERROR_OR_CONTINUE(MADB_InitIndicatorArray(Stmt, MaBind, MADB_MapIndicatorValue(SQL_NULL_DATA))); continue; } /* Sets Stmt->Bulk.HasRowsToSkip if needed, since it traverses and checks status array anyway */ RETURN_ERROR_OR_CONTINUE(MADB_InitBulkOperBuffers(Stmt, CRec, DataPtr, OctetLengthPtr, IndicatorPtr, SqlRec->ConciseType, MaBind)); if (MaBind->u.indicator != NULL && IndIdx == (unsigned int)-1) { IndIdx= i - ParamOffset; } /* Doing it on last parameter - just to do do this once, and to use already allocated indicator array. Little stupid optimization. But it's actually even a bit simpler this way */ if (i == ParamOffset + MADB_STMT_PARAM_COUNT(Stmt) - 1 && Stmt->Bulk.HasRowsToSkip) { if (IndIdx == (unsigned int)-1) { IndIdx= 0; } for (row= Start; row < Start + Stmt->Apd->Header.ArraySize; ++row) { if (Stmt->Apd->Header.ArrayStatusPtr[row] == SQL_PARAM_IGNORE) { MADB_SetIndicatorValue(Stmt, &Stmt->params[IndIdx], (unsigned int)row, SQL_PARAM_IGNORE); } } } if (MADB_AppBufferCanBeUsed(CRec->ConciseType, SqlRec->ConciseType)) { /* Everything has been done for such column already */ continue; } /* We either have skipped rows or need to convert parameter values/convert array */ for (row= Start; row < Start + Stmt->Apd->Header.ArraySize; ++row, DataPtr= (char*)DataPtr + CRec->OctetLength) { void *Buffer= (char*)MaBind->buffer + row*MaBind->buffer_length; void **BufferPtr= (void**)Buffer; /* For the case when Buffer points to the pointer already */ if (Stmt->Apd->Header.ArrayStatusPtr != NULL && Stmt->Apd->Header.ArrayStatusPtr[row] == SQL_PARAM_IGNORE) { continue; } if (MaBind->u.indicator && MaBind->u.indicator[row] > STMT_INDICATOR_NONE) { continue; } switch (CRec->ConciseType) { case SQL_C_CHAR: if (SqlRec->ConciseType != SQL_BIT) { break; } case DATETIME_TYPES: if (CanUseStructArrForDatetime(Stmt)) { BufferPtr= &Buffer; } } /* Need &Dummy here as a length ptr, since NULL is not good here. It would make MADB_ConvertC2Sql to use MaBind->buffer_length by default */ if (!SQL_SUCCEEDED(MADB_ConvertC2Sql(Stmt, CRec, DataPtr, MaBind->length != NULL ? MaBind->length[row] : 0, SqlRec, MaBind, BufferPtr, MaBind->length != NULL ? MaBind->length + row : &Dummy))) { /* Perhaps it's better to move to Clean function */ CRec->InternalBuffer= NULL; return Stmt->Error.ReturnValue; } CRec->InternalBuffer= NULL; } } } return MADB_DoExecute(Stmt, FALSE); } /* }}} */ mariadb-connector-odbc-3.1.15/ma_bulk.h000066400000000000000000000046011414431724000176610ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2017,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* Code allowing to deploy MariaDB bulk operation functionality. * i.e. adapting ODBC param arrays to MariaDB arrays */ #ifndef _ma_bulk_h #define _ma_bulk_h #define MADB_DOING_BULK_OPER(_stmt) ((_stmt)->Bulk.ArraySize > 1) /* Couple defined to make "switch"s look at least shorter, if not nicer */ #define CHAR_BINARY_TYPES SQL_C_CHAR:\ case SQL_C_BINARY:\ case SQL_LONGVARBINARY:\ case SQL_VARBINARY:\ case SQL_VARCHAR:\ case SQL_LONGVARCHAR #define WCHAR_TYPES SQL_C_WCHAR:\ case SQL_WVARCHAR:\ case SQL_WLONGVARCHAR #define DATETIME_TYPES SQL_C_TIMESTAMP:\ case SQL_TYPE_TIMESTAMP:\ case SQL_C_TIME:\ case SQL_TYPE_TIME:\ case SQL_C_INTERVAL_HOUR_TO_MINUTE:\ case SQL_C_INTERVAL_HOUR_TO_SECOND:\ case SQL_C_DATE:\ case SQL_TYPE_DATE char MADB_MapIndicatorValue(SQLLEN OdbcInd); unsigned int MADB_UsedParamSets(MADB_Stmt *Stmt); BOOL MADB_AppBufferCanBeUsed(SQLSMALLINT CType, SQLSMALLINT SqlType); void MADB_CleanBulkOperData(MADB_Stmt *Stmt, unsigned int ParamOffset); SQLRETURN MADB_InitBulkOperBuffers(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void *DataPtr, SQLLEN *OctetLengthPtr, SQLLEN *IndicatorPtr, SQLSMALLINT SqlType, MYSQL_BIND *MaBind); SQLRETURN MADB_SetIndicatorValue(MADB_Stmt *Stmt, MYSQL_BIND *MaBind, unsigned int row, SQLLEN OdbcIndicator); SQLRETURN MADB_ExecuteBulk(MADB_Stmt *Stmt, unsigned int ParamOffset); #endif mariadb-connector-odbc-3.1.15/ma_catalog.c000066400000000000000000001137461414431724000203440ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2021 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include /* * Group of helper functions to add condition to the query based on SQL_ATTR_METADATA_ID attribute value * Pv - pattern value * Oa - ordinary argument * Id - identifier * * if bufferLen is -1, them buffer is assumed to be the dynamic string. Plain char buffer otherwise */ /* {{{ AddPvCondition */ static int AddPvCondition(MADB_Dbc *dbc, void* buffer, size_t bufferLen, char* value, SQLSMALLINT len) { char escaped[2*NAME_LEN + 1]; /* We should probably compare to SQL_NTS, and throw error if just < 0, but I think this way it will work just fine */ if (len < 0) { len= (SQLSMALLINT)strlen(value); } len= (SQLSMALLINT)mysql_real_escape_string(dbc->mariadb, escaped, value, len); /* If DynString */ if (bufferLen == (size_t)-1) { if (MADB_DYNAPPENDCONST((MADB_DynString*)buffer, " LIKE '") || MADB_DynstrAppendMem((MADB_DynString*)buffer, escaped, len) || MADB_DynstrAppendMem((MADB_DynString*)buffer, "' ", 2)) { return 1; } return 0; } return _snprintf((char*)buffer, bufferLen, " LIKE '%.*s' ", len, escaped); } /* }}} */ /* {{{ AddOaCondition */ static int AddOaCondition(MADB_Dbc *dbc, void* buffer, size_t bufferLen, char* value, SQLSMALLINT len) { char escaped[2 * NAME_LEN + 1]; if (len < 0) { len = (SQLSMALLINT)strlen(value); } len = (SQLSMALLINT)mysql_real_escape_string(dbc->mariadb, escaped, value, len); /* If DynString */ if (bufferLen == (size_t)-1) { if (MADB_DYNAPPENDCONST((MADB_DynString*)buffer, " = BINARY '") || MADB_DynstrAppendMem((MADB_DynString*)buffer, escaped, len) || MADB_DynstrAppendMem((MADB_DynString*)buffer, "' ", 2)) { return 1; } return 0; } return _snprintf((char*)buffer, bufferLen, " = BINARY '%.*s' ", len, escaped); } /* }}} */ /* {{{ AddIdCondition */ static int AddIdCondition(void* buffer, size_t bufferLen, char* value, SQLSMALLINT len) { if (len < 0) { len= (SQLSMALLINT)strlen(value); } /* If DynString */ if (bufferLen == (size_t)-1) { MADB_DynstrAppendMem((MADB_DynString*)buffer, "=`", 2); MADB_DynstrAppendMem((MADB_DynString*)buffer, value, len); MADB_DynstrAppendMem((MADB_DynString*)buffer, "` ", 2); return 0; /* Doesn't really matter in case of dynamic string */ } return _snprintf((char*)buffer, bufferLen, "=`%.*s` ", (int)len, value); } /* }}} */ /* {{{ AddPvOrIdCondition */ static int AddPvOrIdCondition(MADB_Stmt* Stmt, void *buffer, size_t bufferLen, char *value, SQLSMALLINT len) { SQLULEN MetadataId; Stmt->Methods->GetAttr(Stmt, SQL_ATTR_METADATA_ID, &MetadataId, 0, (SQLINTEGER*)NULL); if (MetadataId == SQL_TRUE) { return AddIdCondition(buffer, bufferLen, value, len); } else { return AddPvCondition(Stmt->Connection, buffer, bufferLen, value, len); } } /* }}} */ /* {{{ AddOaOrIdCondition */ static int AddOaOrIdCondition(MADB_Stmt* Stmt, void* buffer, size_t bufferLen, char* value, SQLSMALLINT len) { SQLULEN MetadataId; Stmt->Methods->GetAttr(Stmt, SQL_ATTR_METADATA_ID, &MetadataId, 0, (SQLINTEGER*)NULL); if (MetadataId == SQL_TRUE) { return AddIdCondition(buffer, bufferLen, value, len); } else { return AddOaCondition(Stmt->Connection, buffer, bufferLen, value, len); } } /* }}} */ #define SCHEMA_PARAMETER_ERRORS_ALLOWED(STMT) ((STMT)->Connection->Dsn->NeglectSchemaParam == 0) /* {{{ MADB_StmtColumnPrivileges */ SQLRETURN MADB_StmtColumnPrivileges(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4) { char StmtStr[2048] /* This should cover 3 names * NAME_LEN * 2 escaped + query(~300bytes) */; char *p; MADB_CLEAR_ERROR(&Stmt->Error); /* TableName cannot be NULL, but nothing said it can't be empty string */ if (TableName == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY009, "Tablename is required", 0); } ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p= StmtStr; p+= _snprintf(StmtStr, sizeof(StmtStr), "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL as TABLE_SCHEM, TABLE_NAME," "COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE," "IS_GRANTABLE FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE "); /* Empty schema name means tables w/out schema. We could get here only if it is empty string */ if (SchemaName != NULL && *SchemaName == '\0') { p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "0"); } else { p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "TABLE_SCHEMA"); if (CatalogName) { p+= AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE() "); } p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND TABLE_NAME"); p+= AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), TableName, NameLength3); if (ColumnName) { p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND COLUMN_NAME"); p+= AddPvOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), ColumnName, NameLength4); } p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "ORDER BY TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, PRIVILEGE"); } return Stmt->Methods->ExecDirect(Stmt, StmtStr, (SQLINTEGER)strlen(StmtStr)); } /* }}} */ /* {{{ MADB_StmtTablePrivileges */ SQLRETURN MADB_StmtTablePrivileges(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3) { char StmtStr[2048], /* This should cover 2 names * NAME_LEN * 2 escaped + query(~300bytes) */ *p; MADB_CLEAR_ERROR(&Stmt->Error); ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && *SchemaName != '%' && NameLength2 > 1 && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p= StmtStr; p += _snprintf(StmtStr, sizeof(StmtStr), "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " "NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE " "FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE "); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL && *SchemaName == '\0') { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "0"); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "TABLE_SCHEMA"); if (CatalogName) { p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE()"); } if (TableName) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), " AND TABLE_NAME"); p += AddPvOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), TableName, NameLength3); } p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "ORDER BY TABLE_SCHEM, TABLE_NAME, PRIVILEGE"); } return Stmt->Methods->ExecDirect(Stmt, StmtStr, (SQLINTEGER)strlen(StmtStr)); } /* }}} */ /* {{{ MADB_StmtTables */ SQLRETURN MADB_StmtTables(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT CatalogNameLength, char *SchemaName, SQLSMALLINT SchemaNameLength, char *TableName, SQLSMALLINT TableNameLength, char *TableType, SQLSMALLINT TableTypeLength) { MADB_DynString StmtStr; SQLRETURN ret; /* METADATA_ID CatalogName SchemaName TableName TableType --------------------------------------------------------------------------------- ODBC_V3: SQL_FALSE Pattern Pattern Pattern ValueList SQL_TRUE Identifier Identifier Identifier ValueList ODBC_V2: Identifier Identifier Identifier ValueList -------------------------------------------------------------------------------- */ MDBUG_C_ENTER(Stmt->Connection, "MADB_StmtTables"); ADJUST_LENGTH(CatalogName, CatalogNameLength); ADJUST_LENGTH(SchemaName, SchemaNameLength); ADJUST_LENGTH(TableName, TableNameLength); ADJUST_LENGTH(TableType, TableTypeLength); /* TODO: Here we need compare character length in fact. Comparing with both either NAME_CHAR_LEN or NAME_LEN don't make quite sense, but if > NAME_LEN, then it is for sure too big */ if (CatalogNameLength > NAME_LEN || TableNameLength > NAME_LEN) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY090, "Table and catalog names are limited to 64 chars", 0); } /* Schemas are not supported. Thus error except special cases of SQLTables use*/ if (SchemaName != NULL && *SchemaName != '\0' && *SchemaName != '%' && SchemaNameLength > 1 && (strcmp(SchemaName, SQL_ALL_SCHEMAS) || CatalogName == NULL || CatalogNameLength != 0 && TableName == NULL || TableNameLength != 0) && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } /* SQL_ALL_CATALOGS If CatalogName is SQL_ALL_CATALOGS and SchemaName and TableName are empty strings, the result set contains a list of valid catalogs for the data source. (All columns except the TABLE_CAT column contain NULLs */ if (CatalogName && CatalogNameLength && TableName != NULL && !TableNameLength && SchemaName != NULL && SchemaNameLength == 0 && !strcmp(CatalogName, SQL_ALL_CATALOGS)) { MADB_InitDynamicString(&StmtStr, "SELECT SCHEMA_NAME AS TABLE_CAT, CONVERT(NULL,CHAR(64)) AS TABLE_SCHEM, " "CONVERT(NULL,CHAR(64)) AS TABLE_NAME, NULL AS TABLE_TYPE, NULL AS REMARKS " "FROM INFORMATION_SCHEMA.SCHEMATA " "GROUP BY SCHEMA_NAME ORDER BY SCHEMA_NAME", 8192, 512); } /* SQL_ALL_TABLE_TYPES If TableType is SQL_ALL_TABLE_TYPES and CatalogName, SchemaName, and TableName are empty strings, the result set contains a list of valid table types for the data source. (All columns except the TABLE_TYPE column contain NULLs.) */ else if (CatalogName != NULL && !CatalogNameLength && TableName != NULL && !TableNameLength && SchemaName != NULL && SchemaNameLength == 0 && TableType && TableTypeLength && !strcmp(TableType, SQL_ALL_TABLE_TYPES)) { MADB_InitDynamicString(&StmtStr, "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, " "NULL AS TABLE_NAME, 'TABLE' AS TABLE_TYPE, NULL AS REMARKS " "FROM DUAL " "UNION " "SELECT NULL, NULL, NULL, 'VIEW', NULL FROM DUAL " "UNION " "SELECT NULL, NULL, NULL, 'SYSTEM VIEW', NULL FROM DUAL", 8192, 512); } /* Since we treat our databases as catalogs, the only acceptable value for schema is NULL or "%", if that is not the special case of call for schemas list or tables w/out schema(empty string in schema name) - empty resultsets then. */ else if (SchemaName && (!strcmp(SchemaName,SQL_ALL_SCHEMAS) && CatalogName && CatalogNameLength == 0 && TableName && TableNameLength == 0 || *SchemaName == '\0')) { MADB_InitDynamicString(&StmtStr, "SELECT NULL AS TABLE_CAT, NULL AS TABLE_SCHEM, " "NULL AS TABLE_NAME, NULL AS TABLE_TYPE, NULL AS REMARKS " "FROM DUAL WHERE 1=0", 8192, 512); } else { MADB_InitDynamicString(&StmtStr, "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " "if(TABLE_TYPE='BASE TABLE' OR TABLE_TYPE='SYSTEM VERSIONED','TABLE',TABLE_TYPE) AS TABLE_TYPE ," "TABLE_COMMENT AS REMARKS FROM INFORMATION_SCHEMA.TABLES WHERE 1=1 ", 8192, 512); if (CatalogName != NULL) { MADB_DYNAPPENDCONST(&StmtStr, " AND TABLE_SCHEMA"); AddPvOrIdCondition(Stmt, (void*)&StmtStr, (size_t)-1, CatalogName, CatalogNameLength); } else if (Stmt->Connection->Environment->AppType == ATypeMSAccess) { MADB_DYNAPPENDCONST(&StmtStr, " AND TABLE_SCHEMA=DATABASE()"); } if (TableName && TableNameLength) { MADB_DYNAPPENDCONST(&StmtStr, " AND TABLE_NAME"); AddPvOrIdCondition(Stmt, (void*)&StmtStr, (size_t)-1, TableName, TableNameLength); } if (TableType && TableTypeLength && strcmp(TableType, SQL_ALL_TABLE_TYPES) != 0) { unsigned int i; char *myTypes[3]= {"TABLE", "VIEW", "SYNONYM"}; MADB_DYNAPPENDCONST(&StmtStr, " AND TABLE_TYPE IN (''"); for (i= 0; i < 3; i++) { if (strstr(TableType, myTypes[i])) { if (strstr(myTypes[i], "TABLE")) MADB_DYNAPPENDCONST(&StmtStr, ", 'BASE TABLE', 'SYSTEM VERSIONED'"); else { MADB_DYNAPPENDCONST(&StmtStr, ", '"); MADB_DynstrAppend(&StmtStr, myTypes[i]); MADB_DYNAPPENDCONST(&StmtStr, "'"); } } } MADB_DYNAPPENDCONST(&StmtStr, ") "); } MADB_DYNAPPENDCONST(&StmtStr, " ORDER BY TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE"); } MDBUG_C_PRINT(Stmt->Connection, "SQL Statement: %s", StmtStr.str); ret= Stmt->Methods->ExecDirect(Stmt, StmtStr.str, SQL_NTS); MADB_DynstrFree(&StmtStr); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ static MADB_ShortTypeInfo SqlStatsColType[13]= /*1*/ {{SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_VARCHAR, 0, SQL_NO_NULLS, 0}, {SQL_SMALLINT, 0, SQL_NULLABLE, 0}, /*5*/ {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_SMALLINT, 0, SQL_NO_NULLS, 0}, {SQL_SMALLINT, 0, SQL_NULLABLE, 0}, /*9*/ {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_CHAR, 0, SQL_NULLABLE, 2}, {SQL_INTEGER, 0, SQL_NULLABLE, 0}, {SQL_INTEGER, 0, SQL_NULLABLE, 0}, /*13*/ {SQL_VARCHAR, 0, SQL_NULLABLE, 0}}; /* {{{ MADB_StmtStatistics */ SQLRETURN MADB_StmtStatistics(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Unique, SQLUSMALLINT Reserved) { char StmtStr[2048]; char *p= StmtStr; SQLRETURN ret; MADB_CLEAR_ERROR(&Stmt->Error); /* TableName is mandatory */ if (TableName == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, "Tablename is required", 0); return Stmt->Error.ReturnValue; } ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p+= _snprintf(StmtStr, sizeof(StmtStr), "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, " "NON_UNIQUE, NULL AS INDEX_QUALIFIER, INDEX_NAME, " "%d AS TYPE, " "SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME, COLLATION AS ASC_OR_DESC, " "CARDINALITY, NULL AS PAGES, NULL AS FILTER_CONDITION " "FROM INFORMATION_SCHEMA.STATISTICS ", SQL_INDEX_OTHER); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "WHERE 0"); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "WHERE TABLE_SCHEMA"); /* Same comments as for SQLPrimaryKeys including TODOs */ if (CatalogName) { p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE() "); } if (TableName) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND TABLE_NAME"); p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), TableName, NameLength3); } if (Unique == SQL_INDEX_UNIQUE) p += _snprintf(p, 1023 - strlen(StmtStr), "AND NON_UNIQUE=0 "); _snprintf(p, 1023 - strlen(StmtStr), "ORDER BY NON_UNIQUE, INDEX_NAME, ORDINAL_POSITION"); } ret= Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); if (SQL_SUCCEEDED(ret)) { MADB_FixColumnDataTypes(Stmt, SqlStatsColType); } return ret; } /* }}} */ static MADB_ShortTypeInfo SqlColumnsColType[18]= /*1*/ {{SQL_VARCHAR, 0, SQL_NO_NULLS, 0}, {SQL_VARCHAR, 0, SQL_NO_NULLS, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, /*5*/ {SQL_SMALLINT, 0, SQL_NO_NULLS, 0}, {SQL_VARCHAR, 0, SQL_NO_NULLS, 0}, {SQL_INTEGER, 0, SQL_NULLABLE, 0}, {SQL_INTEGER, 0, SQL_NULLABLE, 0}, /*9*/ {SQL_SMALLINT, 0, SQL_NULLABLE, 0}, {SQL_SMALLINT, 0, SQL_NULLABLE, 0}, {SQL_SMALLINT, 0, SQL_NO_NULLS, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, /*13*/ {SQL_VARCHAR, 0, SQL_NULLABLE, 0}, {SQL_SMALLINT, 0, SQL_NO_NULLS, 0}, {SQL_SMALLINT, 0, SQL_NULLABLE, 0}, /*16*/ {SQL_INTEGER, 0, SQL_NULLABLE, 0}, {SQL_INTEGER, 0, SQL_NO_NULLS, 0}, {SQL_VARCHAR, 0, SQL_NULLABLE, 0}}; /* {{{ MADB_StmtColumns */ SQLRETURN MADB_StmtColumns(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4) { MADB_DynString StmtStr; SQLRETURN ret; size_t Length= strlen(MADB_CATALOG_COLUMNSp3); char *ColumnsPart= MADB_CALLOC(Length); unsigned int OctetsPerChar= Stmt->Connection->Charset.cs_info->char_maxlen > 0 && Stmt->Connection->Charset.cs_info->char_maxlen < 10 ? Stmt->Connection->Charset.cs_info->char_maxlen : 1; MDBUG_C_ENTER(Stmt->Connection, "StmtColumns"); ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && *SchemaName != '%' && NameLength2 > 1 && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } if (ColumnsPart == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } _snprintf(ColumnsPart, Length, MADB_CATALOG_COLUMNSp3, OctetsPerChar); MADB_InitDynamicString(&StmtStr, "", 8192, 1024); MADB_CLEAR_ERROR(&Stmt->Error); if (MADB_DYNAPPENDCONST(&StmtStr, MADB_CATALOG_COLUMNSp1)) goto dynerror; if (MADB_DynstrAppend(&StmtStr, MADB_SQL_DATATYPE(Stmt))) goto dynerror; if (MADB_DynstrAppend(&StmtStr, ColumnsPart)) goto dynerror; if (MADB_DynstrAppend(&StmtStr, MADB_DEFAULT_COLUMN(Stmt->Connection))) goto dynerror; if (MADB_DYNAPPENDCONST(&StmtStr, MADB_CATALOG_COLUMNSp4)) goto dynerror; ADJUST_LENGTH(CatalogName, NameLength1); ADJUST_LENGTH(TableName, NameLength3); ADJUST_LENGTH(ColumnName, NameLength4); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL && *SchemaName == '\0') { if(MADB_DynstrAppend(&StmtStr, "0")) goto dynerror; } else { if (MADB_DYNAPPENDCONST(&StmtStr, "TABLE_SCHEMA")) goto dynerror; if (CatalogName) { if (AddOaOrIdCondition(Stmt, (void*)&StmtStr, (size_t)-1, CatalogName, NameLength1)) { goto dynerror; } } else if (MADB_DYNAPPENDCONST(&StmtStr, "=DATABASE()")) goto dynerror; if (TableName && NameLength3) { if (MADB_DynstrAppend(&StmtStr, "AND TABLE_NAME") || AddPvOrIdCondition(Stmt, (void*)&StmtStr, (size_t)-1, TableName, NameLength3)) { goto dynerror; } } if (ColumnName && NameLength4) { if (MADB_DynstrAppend(&StmtStr, "AND COLUMN_NAME") || AddPvOrIdCondition(Stmt, (void*)&StmtStr, (size_t)-1, ColumnName, NameLength4)) { goto dynerror; } } if (MADB_DYNAPPENDCONST(&StmtStr, " ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION")) goto dynerror; MDBUG_C_DUMP(Stmt->Connection, StmtStr.str, s); } ret= Stmt->Methods->ExecDirect(Stmt, StmtStr.str, SQL_NTS); if (SQL_SUCCEEDED(ret)) { MADB_FixColumnDataTypes(Stmt, SqlColumnsColType); } MADB_FREE(ColumnsPart); MADB_DynstrFree(&StmtStr); MDBUG_C_DUMP(Stmt->Connection, ret, d); return ret; dynerror: MADB_FREE(ColumnsPart); MADB_DynstrFree(&StmtStr); return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } /* }}} */ /* {{{ MADB_StmtProcedureColumns */ SQLRETURN MADB_StmtProcedureColumns(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4) { char *StmtStr, *p; size_t Length= strlen(MADB_PROCEDURE_COLUMNS(Stmt)) + 2048; SQLRETURN ret; unsigned int OctetsPerChar= Stmt->Connection->Charset.cs_info->char_maxlen > 0 ? Stmt->Connection->Charset.cs_info->char_maxlen: 1; MADB_CLEAR_ERROR(&Stmt->Error); ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && *SchemaName != '%' && NameLength2 > 1 && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } if (!(StmtStr= MADB_CALLOC(Length))) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } p= StmtStr; p+= _snprintf(p, Length, MADB_PROCEDURE_COLUMNS(Stmt), OctetsPerChar); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL && *SchemaName == '\0') { p += _snprintf(p, Length - strlen(StmtStr), "WHERE 0"); } else { p += _snprintf(p, Length - strlen(StmtStr), "WHERE SPECIFIC_SCHEMA"); if (CatalogName) p += AddOaOrIdCondition(Stmt, p, Length - strlen(StmtStr), CatalogName, NameLength1); else p += _snprintf(p, Length - strlen(StmtStr), "=DATABASE() "); if (ProcName && ProcName[0]) { p += _snprintf(p, Length - strlen(StmtStr), "AND SPECIFIC_NAME"); p += AddPvOrIdCondition(Stmt, p, Length - strlen(StmtStr), ProcName, NameLength3); } if (ColumnName) { if (ColumnName[0]) { p += _snprintf(p, Length - strlen(StmtStr), "AND PARAMETER_NAME"); p += AddPvOrIdCondition(Stmt, p, Length - strlen(StmtStr), ColumnName, NameLength4); } else { p += _snprintf(p, Length - strlen(StmtStr), "AND PARAMETER_NAME IS NULL "); } } p += _snprintf(p, Length - strlen(StmtStr), " ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ORDINAL_POSITION"); } ret= Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); MADB_FREE(StmtStr); return ret; } /* }}} */ /* {{{ MADB_StmtPrimaryKeys */ SQLRETURN MADB_StmtPrimaryKeys(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3) { char StmtStr[2048], *p; MADB_CLEAR_ERROR(&Stmt->Error); /* TableName is mandatory */ if (TableName == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, "Tablename is required", 0); return Stmt->Error.ReturnValue; } ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p= StmtStr; p+= _snprintf(p, sizeof(StmtStr), "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, " "TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION KEY_SEQ, " "'PRIMARY' PK_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE " "COLUMN_KEY = 'pri' AND "); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL) { _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "0"); } else { p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "TABLE_SCHEMA"); /* Empty catalog name means table without catalog(schema). MariaDB/MySQL do not have such. Thus should be empty resultset. TABLE_SCHEMA='' will do the job. TODO: that can be done without sending query to the server. Catalog(schema) cannot be a search pattern. Thus = and not LIKE here */ if (CatalogName != NULL) { p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { /* If Catalog is NULL we return for current DB. If no schema(aka catalog here) is selected, then that means table without schema. Since we don't have such tables, that means empty resultset. TODO: We should be aboe to construct resultset - empty in this case, avoiding sending query to the server */ p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE() "); } p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND TABLE_NAME"); p+= AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), TableName, NameLength3); p+= _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), " ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION"); } return Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); } /* }}} */ /* {{{ MADB_StmtSpecialColumns */ SQLRETURN MADB_StmtSpecialColumns(MADB_Stmt *Stmt, SQLUSMALLINT IdentifierType, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Scope, SQLUSMALLINT Nullable) { char StmtStr[2048], *p= StmtStr; MADB_CLEAR_ERROR(&Stmt->Error); /* TableName is mandatory */ if (TableName == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, "Tablename is required", 0); return Stmt->Error.ReturnValue; } ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p+= _snprintf(p, sizeof(StmtStr), "SELECT NULL AS SCOPE, COLUMN_NAME, %s," "DATA_TYPE TYPE_NAME," "CASE" " WHEN DATA_TYPE in ('bit', 'tinyint', 'smallint', 'year', 'mediumint', 'int'," "'bigint', 'decimal', 'float', 'double') THEN NUMERIC_PRECISION " " WHEN DATA_TYPE='date' THEN 10" " WHEN DATA_TYPE='time' THEN 8" " WHEN DATA_TYPE in ('timestamp', 'datetime') THEN 19 " "END AS COLUMN_SIZE," "CHARACTER_OCTET_LENGTH AS BUFFER_LENGTH," "NUMERIC_SCALE DECIMAL_DIGITS, " XSTR(SQL_PC_UNKNOWN) " PSEUDO_COLUMN " "FROM INFORMATION_SCHEMA.COLUMNS WHERE 1 ", MADB_SQL_DATATYPE(Stmt)); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND 0"); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND TABLE_SCHEMA"); if (CatalogName) { p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE() "); } if (TableName && TableName[0]) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND TABLE_NAME"); p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), TableName, NameLength3); } if (Nullable == SQL_NO_NULLS) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND IS_NULLABLE <> 'YES' "); } if (IdentifierType == SQL_BEST_ROWID) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND COLUMN_KEY IN ('PRI', 'UNI') "); } else if (IdentifierType == SQL_ROWVER) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND DATA_TYPE='timestamp' AND EXTRA LIKE '%%CURRENT_TIMESTAMP%%' "); } p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "ORDER BY TABLE_SCHEMA, TABLE_NAME, COLUMN_KEY"); } return Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); } /* }}} */ /* {{{ MADB_StmtProcedures */ SQLRETURN MADB_StmtProcedures(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3) { char StmtStr[2048], *p; MADB_CLEAR_ERROR(&Stmt->Error); ADJUST_LENGTH(SchemaName, NameLength2); if (SchemaName != NULL && *SchemaName != '\0' && *SchemaName != '%' && NameLength2 > 1 && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } p= StmtStr; p+= _snprintf(p, sizeof(StmtStr), "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, " "SPECIFIC_NAME PROCEDURE_NAME, NULL NUM_INPUT_PARAMS, " "NULL NUM_OUTPUT_PARAMS, NULL NUM_RESULT_SETS, " "ROUTINE_COMMENT REMARKS, " "CASE ROUTINE_TYPE " " WHEN 'FUNCTION' THEN " XSTR(SQL_PT_FUNCTION) " WHEN 'PROCEDURE' THEN " XSTR(SQL_PT_PROCEDURE) " ELSE " XSTR(SQL_PT_UNKNOWN) " " "END PROCEDURE_TYPE " "FROM INFORMATION_SCHEMA.ROUTINES "); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise the error would have been already thrown */ if (SchemaName != NULL && *SchemaName == '\0') { p += _snprintf(p, sizeof(StmtStr)- strlen(StmtStr), "WHERE 0"); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "WHERE ROUTINE_SCHEMA"); /* Catalog is ordinary argument, but schema is pattern value. Since we treat is catalog as a schema, using more permissive PV here On other hand we do not do this everywhere. Need to be consistent */ if (CatalogName != NULL) { p += AddOaOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), CatalogName, NameLength1); } else { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "=DATABASE() "); } if (ProcName != NULL) { p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), "AND SPECIFIC_NAME"); p += AddPvOrIdCondition(Stmt, p, sizeof(StmtStr) - strlen(StmtStr), ProcName, NameLength3); } p += _snprintf(p, sizeof(StmtStr) - strlen(StmtStr), " ORDER BY ROUTINE_SCHEMA, SPECIFIC_NAME"); } return Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); } /* }}} */ /* {{{ SQLForeignKeys */ SQLRETURN MADB_StmtForeignKeys(MADB_Stmt *Stmt, char *PKCatalogName, SQLSMALLINT NameLength1, char *PKSchemaName, SQLSMALLINT NameLength2, char *PKTableName, SQLSMALLINT NameLength3, char *FKCatalogName, SQLSMALLINT NameLength4, char *FKSchemaName, SQLSMALLINT NameLength5, char *FKTableName, SQLSMALLINT NameLength6) { SQLRETURN ret= SQL_ERROR; MADB_DynString StmtStr; MADB_CLEAR_ERROR(&Stmt->Error); /* PKTableName and FKTableName are mandatory. The requirement is only about NULL names */ if (PKTableName == NULL && FKTableName == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, "PKTableName or FKTableName are required", 0); return Stmt->Error.ReturnValue; } ADJUST_LENGTH(PKSchemaName, NameLength2); ADJUST_LENGTH(FKSchemaName, NameLength5); if (((PKSchemaName != NULL && *PKSchemaName != '\0') || (FKSchemaName != NULL && *FKSchemaName != '\0')) && SCHEMA_PARAMETER_ERRORS_ALLOWED(Stmt)) { return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Schemas are not supported. Use CatalogName parameter instead", 0); } ADJUST_LENGTH(PKCatalogName, NameLength1); ADJUST_LENGTH(PKTableName, NameLength3); ADJUST_LENGTH(FKCatalogName, NameLength4); ADJUST_LENGTH(FKTableName, NameLength6); /* modified from JDBC driver */ MADB_InitDynamicString(&StmtStr, "SELECT A.REFERENCED_TABLE_SCHEMA PKTABLE_CAT, NULL PKTABLE_SCHEM, " "A.REFERENCED_TABLE_NAME PKTABLE_NAME, " "A.REFERENCED_COLUMN_NAME PKCOLUMN_NAME, " "A.TABLE_SCHEMA FKTABLE_CAT, NULL FKTABLE_SCHEM, " "A.TABLE_NAME FKTABLE_NAME, A.COLUMN_NAME FKCOLUMN_NAME, " "A.POSITION_IN_UNIQUE_CONSTRAINT KEY_SEQ, " "CASE update_rule " " WHEN 'RESTRICT' THEN " XSTR(SQL_RESTRICT) " WHEN 'NO ACTION' THEN " XSTR(SQL_NO_ACTION) " WHEN 'CASCADE' THEN " XSTR(SQL_CASCADE) " WHEN 'SET NULL' THEN " XSTR(SQL_SET_NULL) " WHEN 'SET DEFAULT' THEN " XSTR(SQL_SET_DEFAULT) " " "END UPDATE_RULE, " "CASE DELETE_RULE" " WHEN 'RESTRICT' THEN " XSTR(SQL_RESTRICT) " WHEN 'NO ACTION' THEN " XSTR(SQL_NO_ACTION) " WHEN 'CASCADE' THEN " XSTR(SQL_CASCADE) " WHEN 'SET NULL' THEN " XSTR(SQL_SET_NULL) " WHEN 'SET DEFAULT' THEN " XSTR(SQL_SET_DEFAULT) " " " END DELETE_RULE," "A.CONSTRAINT_NAME FK_NAME, " "'PRIMARY' PK_NAME," XSTR(SQL_NOT_DEFERRABLE) " AS DEFERRABILITY " " FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE A" " JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE B" " ON (B.TABLE_SCHEMA = A.REFERENCED_TABLE_SCHEMA" " AND B.TABLE_NAME = A.REFERENCED_TABLE_NAME" " AND B.COLUMN_NAME = A.REFERENCED_COLUMN_NAME)" " JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC" " ON (RC.CONSTRAINT_NAME = A.CONSTRAINT_NAME" " AND RC.TABLE_NAME = A.TABLE_NAME" " AND RC.CONSTRAINT_SCHEMA = A.TABLE_SCHEMA)" " WHERE B.CONSTRAINT_NAME= 'PRIMARY'", 8192, 128); /* Empty schema name means tables w/out schema. We could get here only if it is empty string, otherwise error would have been already thrown */ if (PKSchemaName != NULL || FKSchemaName != NULL) { MADB_DYNAPPENDCONST(&StmtStr, " AND 0"); } else { if (PKTableName != NULL) { MADB_DYNAPPENDCONST(&StmtStr, " AND A.REFERENCED_TABLE_SCHEMA"); if (PKCatalogName) { AddOaOrIdCondition(Stmt, &StmtStr, (size_t)-1, PKCatalogName, NameLength1); } else { MADB_DYNAPPENDCONST(&StmtStr, "=DATABASE()"); } MADB_DYNAPPENDCONST(&StmtStr, " AND A.REFERENCED_TABLE_NAME"); AddOaOrIdCondition(Stmt, &StmtStr, (size_t)-1, PKTableName, NameLength3); } if (FKTableName != NULL) { MADB_DYNAPPENDCONST(&StmtStr, " AND A.TABLE_SCHEMA"); if (FKCatalogName != NULL) { AddOaOrIdCondition(Stmt, &StmtStr, (size_t)-1, FKCatalogName, NameLength4); } else { MADB_DYNAPPENDCONST(&StmtStr, "=DATABASE() "); } MADB_DYNAPPENDCONST(&StmtStr, " AND A.TABLE_NAME"); AddOaOrIdCondition(Stmt, &StmtStr, (size_t)-1, FKTableName, NameLength6); } MADB_DYNAPPENDCONST(&StmtStr, "ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ, PKTABLE_NAME"); } ret= Stmt->Methods->ExecDirect(Stmt, StmtStr.str, SQL_NTS); MADB_DynstrFree(&StmtStr); return ret; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_catalog.h000066400000000000000000000324161414431724000203430ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2021 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_catalog_h_ #define _ma_catalog_h_ #include SQLRETURN MADB_StmtColumnPrivileges(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN MADB_StmtTablePrivileges(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3); SQLRETURN MADB_StmtTables(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT CatalogNameLength, char *SchemaName, SQLSMALLINT SchemaNameLength, char *TableName, SQLSMALLINT TableNameLength, char *TableType, SQLSMALLINT TableTypeLength); SQLRETURN MADB_StmtStatistics(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Unique, SQLUSMALLINT Reserved); SQLRETURN MADB_StmtColumns(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN MADB_StmtProcedureColumns(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN MADB_StmtPrimaryKeys(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3); SQLRETURN MADB_StmtSpecialColumns(MADB_Stmt *Stmt, SQLUSMALLINT IdentifierType, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Scope, SQLUSMALLINT Nullable); SQLRETURN MADB_StmtProcedures(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3); SQLRETURN MADB_StmtForeignKeys(MADB_Stmt *Stmt, char *PKCatalogName, SQLSMALLINT NameLength1, char *PKSchemaName, SQLSMALLINT NameLength2, char *PKTableName, SQLSMALLINT NameLength3, char *FKCatalogName, SQLSMALLINT NameLength4, char *FKSchemaName, SQLSMALLINT NameLength5, char *FKTableName, SQLSMALLINT NameLength6); #define MADB_TRANSFER_OCTET_LENGTH(TYPE_DEF_COL_NAME)\ "@tol:=CAST(CASE @dt"\ " WHEN " XSTR(SQL_BIT) " THEN 1 "\ " WHEN " XSTR(SQL_TINYINT) " THEN 1 "\ " WHEN " XSTR(SQL_SMALLINT) " THEN 2 "\ " WHEN " XSTR(SQL_INTEGER) " THEN IF(DATA_TYPE='mediumint',3,4) "\ " WHEN " XSTR(SQL_BIGINT) " THEN 20 "\ " WHEN " XSTR(SQL_REAL) " THEN 4 "\ " WHEN " XSTR(SQL_FLOAT) " THEN 8 "\ " WHEN " XSTR(SQL_DOUBLE) " THEN 8 "\ " WHEN " XSTR(SQL_DATE) " THEN 6 "\ " WHEN " XSTR(SQL_TYPE_DATE) " THEN 6 "\ " WHEN " XSTR(SQL_TIME) " THEN 6 "\ " WHEN " XSTR(SQL_TYPE_TIME) " THEN 6 "\ " WHEN " XSTR(SQL_TIMESTAMP) " THEN 16 "\ " WHEN " XSTR(SQL_TYPE_TIMESTAMP) " THEN 16 "\ " WHEN " XSTR(SQL_GUID) " THEN 16 "\ " WHEN " XSTR(SQL_DECIMAL) " THEN @ColSize + IF(" #TYPE_DEF_COL_NAME " LIKE '%%unsigned',1,2) "\ " WHEN " XSTR(SQL_BINARY) " THEN IF(DATA_TYPE='bit', CAST(((NUMERIC_PRECISION + 7) / 8) AS SIGNED), CHARACTER_OCTET_LENGTH) "\ " WHEN " XSTR(SQL_VARBINARY) " THEN CHARACTER_OCTET_LENGTH "\ " WHEN " XSTR(SQL_LONGVARBINARY) " THEN CHARACTER_OCTET_LENGTH "\ " ELSE CHARACTER_MAXIMUM_LENGTH*%u "\ "END AS SIGNED)" /* CASE for DATA_TYPE glued in 2 parts for ODBC v2 or v3 */ #define MADB_SQL_DATATYPEp1\ "CASE DATA_TYPE"\ " WHEN 'bit' THEN @dt:= IF(NUMERIC_PRECISION=1," XSTR(SQL_BIT) ", " XSTR(SQL_BINARY) ")"\ " WHEN 'tinyint' THEN @dt:=" XSTR(SQL_TINYINT)\ " WHEN 'smallint' THEN @dt:=" XSTR(SQL_SMALLINT)\ " WHEN 'year' THEN @dt:= " XSTR(SQL_SMALLINT)\ " WHEN 'mediumint' THEN @dt:=" XSTR(SQL_INTEGER)\ " WHEN 'int' THEN @dt:=" XSTR(SQL_INTEGER)\ " WHEN 'bigint' THEN @dt:=" XSTR(SQL_BIGINT)\ " WHEN 'blob' THEN @dt:=" XSTR(SQL_LONGVARBINARY)\ " WHEN 'tinyblob' THEN @dt:=" XSTR(SQL_LONGVARBINARY)\ " WHEN 'mediumblob' THEN @dt:=" XSTR(SQL_LONGVARBINARY)\ " WHEN 'longblob' THEN @dt:=" XSTR(SQL_LONGVARBINARY)\ " WHEN 'decimal' THEN @dt:=" XSTR(SQL_DECIMAL)\ " WHEN 'float' THEN @dt:=IF(NUMERIC_SCALE IS NULL," XSTR(SQL_REAL) ", " XSTR(SQL_DECIMAL) ")"\ " WHEN 'double' THEN @dt:=IF(NUMERIC_SCALE IS NULL," XSTR(SQL_DOUBLE) ", " XSTR(SQL_DECIMAL) ")"\ " WHEN 'binary' THEN @dt:=" XSTR(SQL_BINARY)\ " WHEN 'varbinary' THEN @dt:=" XSTR(SQL_VARBINARY) #define MADB_SQL_DATATYPEp1U\ " WHEN 'text' THEN @dt:=" XSTR(SQL_WLONGVARCHAR)\ " WHEN 'tinytext' THEN @dt:=" XSTR(SQL_WLONGVARCHAR)\ " WHEN 'mediumtext' THEN @dt:=" XSTR(SQL_WLONGVARCHAR)\ " WHEN 'longtext' THEN @dt:=" XSTR(SQL_WLONGVARCHAR)\ " WHEN 'char' THEN @dt:=" XSTR(SQL_WCHAR)\ " WHEN 'enum' THEN @dt:=" XSTR(SQL_WCHAR)\ " WHEN 'set' THEN @dt:=" XSTR(SQL_WCHAR)\ " WHEN 'varchar' THEN @dt:=" XSTR(SQL_WVARCHAR) #define MADB_SQL_DATATYPEp1A\ " WHEN 'text' THEN @dt:=" XSTR(SQL_LONGVARCHAR)\ " WHEN 'tinytext' THEN @dt:=" XSTR(SQL_LONGVARCHAR)\ " WHEN 'mediumtext' THEN @dt:=" XSTR(SQL_LONGVARCHAR)\ " WHEN 'longtext' THEN @dt:=" XSTR(SQL_LONGVARCHAR)\ " WHEN 'char' THEN @dt:=" XSTR(SQL_CHAR)\ " WHEN 'enum' THEN @dt:=" XSTR(SQL_CHAR)\ " WHEN 'set' THEN @dt:=" XSTR(SQL_CHAR)\ " WHEN 'varchar' THEN @dt:=" XSTR(SQL_VARCHAR) #define MADB_SQL_DATATYPEp2_ODBC3\ " WHEN 'date' THEN @dt:=" XSTR(SQL_TYPE_DATE)\ " WHEN 'time' THEN @dt:=" XSTR(SQL_TYPE_TIME)\ " WHEN 'datetime' THEN @dt:=" XSTR(SQL_TYPE_TIMESTAMP)\ " WHEN 'timestamp' THEN @dt:=" XSTR(SQL_TYPE_TIMESTAMP)\ " ELSE @dt:=" XSTR(SQL_LONGVARBINARY)\ "END AS DATA_TYPE" #define MADB_SQL_DATATYPEp2_ODBC2\ " WHEN 'date' THEN @dt:=" XSTR(SQL_DATE)\ " WHEN 'time' THEN @dt:=" XSTR(SQL_TIME)\ " WHEN 'datetime' THEN @dt:=" XSTR(SQL_TIMESTAMP)\ " WHEN 'timestamp' THEN @dt:=" XSTR(SQL_TIMESTAMP)\ " ELSE @dt:=" XSTR(SQL_LONGVARBINARY)\ "END AS DATA_TYPE" #define MADB_SQL_DATATYPE_ODBC3U MADB_SQL_DATATYPEp1 MADB_SQL_DATATYPEp1U MADB_SQL_DATATYPEp2_ODBC3 #define MADB_SQL_DATATYPE_ODBC3A MADB_SQL_DATATYPEp1 MADB_SQL_DATATYPEp1A MADB_SQL_DATATYPEp2_ODBC3 #define MADB_SQL_DATATYPE_ODBC2U MADB_SQL_DATATYPEp1 MADB_SQL_DATATYPEp1U MADB_SQL_DATATYPEp2_ODBC2 #define MADB_SQL_DATATYPE_ODBC2A MADB_SQL_DATATYPEp1 MADB_SQL_DATATYPEp1A MADB_SQL_DATATYPEp2_ODBC2 #define MADB_SQL_DATATYPE(StmtHndl) (StmtHndl->Connection->Environment->OdbcVersion >= SQL_OV_ODBC3 ?\ (StmtHndl->Connection->IsAnsi ? MADB_SQL_DATATYPE_ODBC3A : MADB_SQL_DATATYPE_ODBC3U) :\ (StmtHndl->Connection->IsAnsi ? MADB_SQL_DATATYPE_ODBC2A : MADB_SQL_DATATYPE_ODBC2U)) /************** End of DATA_TYPE *************/ /************** SQLColumns *************/ #define MADB_COLUMN_SIZE\ "CAST(CASE" \ " WHEN DATA_TYPE = 'bit' THEN @ColSize:=((NUMERIC_PRECISION + 7) / 8) "\ " WHEN DATA_TYPE in ('tinyint', 'smallint', 'mediumint', 'int',"\ "'bigint', 'decimal') THEN @ColSize:=NUMERIC_PRECISION "\ " WHEN DATA_TYPE = 'float' THEN if(NUMERIC_SCALE IS NULL, @ColSize:=7, @ColSize:=NUMERIC_PRECISION)"\ " WHEN DATA_TYPE = 'double' THEN if(NUMERIC_SCALE IS NULL, @ColSize:=15, @ColSize:=NUMERIC_PRECISION)"\ " WHEN DATA_TYPE = 'date' THEN @ColSize:=10"\ " WHEN DATA_TYPE = 'time' THEN @ColSize:=8"\ " WHEN DATA_TYPE = 'year' THEN @ColSize:=4"\ " WHEN DATA_TYPE in ('timestamp', 'datetime') THEN @ColSize:=19 "\ " ELSE @ColSize:=CHARACTER_MAXIMUM_LENGTH "\ "END AS UNSIGNED)" #define MADB_CATALOG_COLUMNSp1 "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, " #define MADB_CATALOG_COLUMNSp3 ", UCASE(IF(COLUMN_TYPE LIKE '%%(%%)%%', CONCAT(SUBSTRING(COLUMN_TYPE,1, LOCATE('(',COLUMN_TYPE) - 1 ), SUBSTRING(COLUMN_TYPE,1+locate(')',COLUMN_TYPE))), COLUMN_TYPE )) AS TYPE_NAME, "\ MADB_COLUMN_SIZE " AS COLUMN_SIZE,"\ MADB_TRANSFER_OCTET_LENGTH(COLUMN_TYPE) " AS BUFFER_LENGTH, "\ "NUMERIC_SCALE DECIMAL_DIGITS, IF(CHARACTER_OCTET_LENGTH IS NOT NULL, NULL, 10) AS NUM_PREC_RADIX,"\ "IF(DATA_TYPE='timestamp', 1, IF(IS_NULLABLE='YES',1,IF(EXTRA='auto_increment', 1, 0))) AS NULLABLE, "\ "COLUMN_COMMENT AS REMARKS," #define MADB_DEFAULT_COLUMN_OLD "IF(COLLATION_NAME IS NOT NULL AND COLUMN_DEFAULT IS NOT NULL, CONCAT(CHAR(39), COLUMN_DEFAULT, CHAR(39)), COLUMN_DEFAULT)" #define MADB_DEFAULT_COLUMN_NEW "COLUMN_DEFAULT" #define MADB_DEFAULT_COLUMN(DbcHndl) (MADB_ServerSupports(DbcHndl,MADB_ENCLOSES_COLUMN_DEF_WITH_QUOTES) ? MADB_DEFAULT_COLUMN_NEW : MADB_DEFAULT_COLUMN_OLD) #define MADB_CATALOG_TYPE_SUB "CAST(CASE"\ " WHEN DATA_TYPE = 'date' THEN " XSTR(SQL_DATETIME)\ " WHEN DATA_TYPE = 'time' THEN " XSTR(SQL_DATETIME)\ " WHEN DATA_TYPE = 'datetime' THEN " XSTR(SQL_DATETIME)\ " WHEN DATA_TYPE = 'timestamp' THEN " XSTR(SQL_DATETIME)\ " ELSE @dt "\ "END AS SIGNED) SQL_DATA_TYPE,"\ "CAST(CASE"\ " WHEN DATA_TYPE = 'date' THEN " XSTR(SQL_CODE_DATE)\ " WHEN DATA_TYPE = 'time' THEN " XSTR(SQL_CODE_TIME)\ " WHEN DATA_TYPE = 'datetime' THEN " XSTR(SQL_CODE_TIMESTAMP)\ " WHEN DATA_TYPE = 'timestamp' THEN " XSTR(SQL_CODE_TIMESTAMP)\ " ELSE NULL "\ "END AS SIGNED) SQL_DATETIME_SUB," #define MADB_CATALOG_COLUMNSp4 " AS COLUMN_DEF," MADB_CATALOG_TYPE_SUB\ "IF(CHARACTER_OCTET_LENGTH IS NOT NULL, @tol, IF(DATA_TYPE='bit' AND NUMERIC_PRECISION =1, NULL, CAST((NUMERIC_PRECISION + 7)/8 AS SIGNED))) AS CHAR_OCTET_LENGTH, "\ "ORDINAL_POSITION,"\ "IF(DATA_TYPE='timestamp', 'YES', IF(IS_NULLABLE='YES','YES',IF(EXTRA='auto_increment', 'YES', 'NO'))) AS IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS WHERE " /************** End of SQLColumns ************/ /************** SQLProcedureColumns **********/ #define MADB_PROCEDURE_COLUMNSp1 \ "SELECT SPECIFIC_SCHEMA AS PROCEDURE_CAT, NULL AS PROCEDURE_SCHEM, "\ "SPECIFIC_NAME PROCEDURE_NAME, IF(PARAMETER_NAME IS NULL, '', PARAMETER_NAME) COLUMN_NAME, "\ "CASE PARAMETER_MODE "\ " WHEN 'IN' THEN " XSTR(SQL_PARAM_INPUT)\ " WHEN 'OUT' THEN " XSTR(SQL_PARAM_OUTPUT)\ " WHEN 'INOUT' THEN " XSTR(SQL_PARAM_INPUT_OUTPUT)\ " ELSE IF(PARAMETER_MODE IS NULL, " XSTR(SQL_RETURN_VALUE) ", " XSTR(SQL_PARAM_TYPE_UNKNOWN) ")"\ "END COLUMN_TYPE, " #define MADB_PROCEDURE_COLUMNSp3\ ", DATA_TYPE TYPE_NAME, "\ MADB_COLUMN_SIZE " AS COLUMN_SIZE, "\ MADB_TRANSFER_OCTET_LENGTH(DTD_IDENTIFIER) " AS BUFFER_LENGTH, "\ "NUMERIC_SCALE DECIMAL_DIGITS, IF(NUMERIC_PRECISION IS NULL, NULL, 10) AS NUM_PREC_RADIX,"\ XSTR(SQL_NULLABLE_UNKNOWN) " NULLABLE,"\ "NULL REMARKS, NULL COLUMN_DEF," MADB_CATALOG_TYPE_SUB \ "IF(CHARACTER_MAXIMUM_LENGTH IS NULL, NULL, @tol) CHAR_OCTET_LENGTH, "\ "ORDINAL_POSITION, 'YES' IS_NULLABLE FROM INFORMATION_SCHEMA.PARAMETERS " #define MADB_PROCEDURE_COLUMNS_ODBC3U MADB_PROCEDURE_COLUMNSp1 MADB_SQL_DATATYPE_ODBC3U MADB_PROCEDURE_COLUMNSp3 #define MADB_PROCEDURE_COLUMNS_ODBC2U MADB_PROCEDURE_COLUMNSp1 MADB_SQL_DATATYPE_ODBC2U MADB_PROCEDURE_COLUMNSp3 #define MADB_PROCEDURE_COLUMNS_ODBC3A MADB_PROCEDURE_COLUMNSp1 MADB_SQL_DATATYPE_ODBC3A MADB_PROCEDURE_COLUMNSp3 #define MADB_PROCEDURE_COLUMNS_ODBC2A MADB_PROCEDURE_COLUMNSp1 MADB_SQL_DATATYPE_ODBC2A MADB_PROCEDURE_COLUMNSp3 #define MADB_PROCEDURE_COLUMNS(StmtHndl) (StmtHndl->Connection->Environment->OdbcVersion >= SQL_OV_ODBC3 ?\ (StmtHndl->Connection->IsAnsi ? MADB_PROCEDURE_COLUMNS_ODBC3A : MADB_PROCEDURE_COLUMNS_ODBC3U) : \ (StmtHndl->Connection->IsAnsi ? MADB_PROCEDURE_COLUMNS_ODBC2A : MADB_PROCEDURE_COLUMNS_ODBC2U)) /************** SQLProcedureColumns **********/ #endif mariadb-connector-odbc-3.1.15/ma_common.c000066400000000000000000000040551414431724000202120ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* Common functions used in both connector and setup library. * Moved to avoid redundant dependencies */ #include #include static unsigned int ValidChar(const char *start, const char *end) { return 4; } static unsigned CharOctetLen(unsigned int utf32) { return 4; } MARIADB_CHARSET_INFO dummyUtf32le= {0, 1, "utf32le", "utf32_general_ci", "", 0, "UTF-32LE", 4, 4, CharOctetLen, ValidChar}; MARIADB_CHARSET_INFO* DmUnicodeCs= NULL; Client_Charset utf8= {CP_UTF8, NULL}; /* {{{ ltrim */ char* ltrim(char *Str) { /* I am not sure using iswspace, and not isspace makes any sense here. But probably does not hurt either */ while (Str && iswspace(Str[0])) ++Str; return Str; } /* }}} */ /* {{{ trim */ char* trim(char *Str) { char *end; Str= ltrim(Str); end= Str + strlen(Str) - 1; while (iswspace(*end)) *end--= 0; return Str; } /* }}} */ /* Windows only common functions */ #ifdef _WIN32 /* {{{ strcasestr() */ char* strcasestr(const char* HayStack, const char* Needle) { return StrStrIA(HayStack, Needle); } /* }}} */ #endif mariadb-connector-odbc-3.1.15/ma_connection.c000066400000000000000000002375111414431724000210660ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2021 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include extern const char* DefaultPluginLocation; static const char* utf8mb3 = "utf8mb3"; struct st_madb_isolation MADB_IsolationLevel[] = { {SQL_TRANSACTION_REPEATABLE_READ, "REPEATABLE READ", "REPEATABLE-READ"}, {SQL_TRANSACTION_READ_COMMITTED, "READ COMMITTED", "READ-COMMITTED"}, {SQL_TRANSACTION_READ_UNCOMMITTED, "READ UNCOMMITTED", "READ-UNCOMMITTED"}, {SQL_TRANSACTION_SERIALIZABLE, "SERIALIZABLE", "SERIALIZABLE"}, {0, 0} }; static long TranslateTxIsolation(const char* txIsolation, size_t len) { unsigned int i; for (i = 0; i < 4; i++) { if (strncmp(txIsolation, MADB_IsolationLevel[i].StrIsolation, len) == 0 || strncmp(txIsolation, MADB_IsolationLevel[i].TrackStr, len) == 0) { return MADB_IsolationLevel[i].SqlIsolation; break; } } return SQL_TRANSACTION_REPEATABLE_READ; } /* used by SQLGetFunctions */ SQLUSMALLINT MADB_supported_api[]= { SQL_API_SQLALLOCCONNECT, SQL_API_SQLALLOCENV, SQL_API_SQLALLOCHANDLE, SQL_API_SQLALLOCSTMT, SQL_API_SQLBINDCOL, SQL_API_SQLBINDPARAM, SQL_API_SQLCANCEL, SQL_API_SQLCLOSECURSOR, SQL_API_SQLCOLATTRIBUTE, SQL_API_SQLCOLUMNS, SQL_API_SQLCONNECT, SQL_API_SQLCOPYDESC, SQL_API_SQLDATASOURCES, SQL_API_SQLDESCRIBECOL, SQL_API_SQLDISCONNECT, SQL_API_SQLENDTRAN, SQL_API_SQLERROR, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLFETCH, SQL_API_SQLFETCHSCROLL, SQL_API_SQLFREECONNECT, SQL_API_SQLFREEENV, SQL_API_SQLFREEHANDLE, SQL_API_SQLFREESTMT, SQL_API_SQLGETCONNECTATTR, SQL_API_SQLGETCONNECTOPTION, SQL_API_SQLGETCURSORNAME, SQL_API_SQLGETDATA, SQL_API_SQLGETDESCFIELD, SQL_API_SQLGETDESCREC, SQL_API_SQLGETDIAGFIELD, SQL_API_SQLGETDIAGREC, SQL_API_SQLGETENVATTR, SQL_API_SQLGETFUNCTIONS, SQL_API_SQLGETINFO, SQL_API_SQLGETSTMTATTR, SQL_API_SQLGETSTMTOPTION, SQL_API_SQLGETTYPEINFO, SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLPARAMDATA, SQL_API_SQLPREPARE, SQL_API_SQLPUTDATA, SQL_API_SQLROWCOUNT, SQL_API_SQLSETCONNECTATTR, SQL_API_SQLSETCONNECTOPTION, SQL_API_SQLSETCURSORNAME, SQL_API_SQLSETDESCFIELD, SQL_API_SQLSETDESCREC, SQL_API_SQLSETENVATTR, SQL_API_SQLSETPARAM, SQL_API_SQLSETSTMTATTR, SQL_API_SQLSETSTMTOPTION, SQL_API_SQLSPECIALCOLUMNS, SQL_API_SQLSTATISTICS, SQL_API_SQLTABLES, SQL_API_SQLTRANSACT, SQL_API_SQLBULKOPERATIONS, SQL_API_SQLBINDPARAMETER, SQL_API_SQLBROWSECONNECT, SQL_API_SQLCOLATTRIBUTES, SQL_API_SQLCOLUMNPRIVILEGES , SQL_API_SQLDESCRIBEPARAM, SQL_API_SQLDRIVERCONNECT, SQL_API_SQLDRIVERS, SQL_API_SQLEXTENDEDFETCH, SQL_API_SQLFOREIGNKEYS, SQL_API_SQLMORERESULTS, SQL_API_SQLNATIVESQL, SQL_API_SQLNUMPARAMS, SQL_API_SQLPARAMOPTIONS, SQL_API_SQLPRIMARYKEYS, SQL_API_SQLPROCEDURECOLUMNS, SQL_API_SQLPROCEDURES, SQL_API_SQLSETPOS, SQL_API_SQLSETSCROLLOPTIONS, SQL_API_SQLTABLES, SQL_API_SQLTABLEPRIVILEGES }; struct st_ma_connection_methods MADB_Dbc_Methods; /* declared at the end of file */ SQLRETURN MADB_DbcDummyTrackSession(MADB_Dbc* Dbc); SQLRETURN MADB_DbcGetServerTxIsolation(MADB_Dbc* Dbc, SQLINTEGER*txIsolation); my_bool CheckConnection(MADB_Dbc *Dbc) { if (!Dbc->mariadb) return FALSE; if (mysql_get_socket(Dbc->mariadb) == MARIADB_INVALID_SOCKET) { /* Check if reconnect option was set */ if (DSN_OPTION(Dbc, MADB_OPT_FLAG_AUTO_RECONNECT)) { if (!mysql_ping(Dbc->mariadb)) return TRUE; } return FALSE; } return TRUE; } /* {{{ MADB_DbcSetAttr */ SQLRETURN MADB_DbcSetAttr(MADB_Dbc *Dbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength, my_bool isWChar) { MADB_CLEAR_ERROR(&Dbc->Error); if (!Dbc) { /* Todo: check */ if (Attribute != SQL_ATTR_TRACE && Attribute != SQL_ATTR_TRACEFILE) return SQL_INVALID_HANDLE; return SQL_SUCCESS; } switch(Attribute) { case SQL_ATTR_ACCESS_MODE: if ((SQLPOINTER)SQL_MODE_READ_WRITE != ValuePtr) MADB_SetError(&Dbc->Error, MADB_ERR_01S02, NULL, 0); Dbc->AccessMode= SQL_MODE_READ_WRITE; break; #if (ODBCVER >= 0x0351) case SQL_ATTR_ANSI_APP: if (ValuePtr != NULL) { Dbc->IsAnsi= 1; Dbc->ConnOrSrcCharset= &SourceAnsiCs; CopyClientCharset(&SourceAnsiCs, &Dbc->Charset); } else { Dbc->IsAnsi= 0; } break; #endif case SQL_ATTR_ASYNC_ENABLE: if ((SQLPOINTER)SQL_ASYNC_ENABLE_OFF != ValuePtr) MADB_SetError(&Dbc->Error, MADB_ERR_01S02, NULL, 0); Dbc->AsyncEnable= SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_AUTO_IPD: /* read only */ MADB_SetError(&Dbc->Error, MADB_ERR_HY092, NULL, 0); break; case SQL_ATTR_AUTOCOMMIT: { SQLULEN ValidAttrs[]= {2, SQL_AUTOCOMMIT_ON, SQL_AUTOCOMMIT_OFF}; MADB_CHECK_ATTRIBUTE(Dbc, ValuePtr, ValidAttrs); /* if a connection is open, try to apply setting to the connection */ if (Dbc->mariadb) { if (Dbc->EnlistInDtc) { return MADB_SetError(&Dbc->Error, MADB_ERR_25000, NULL, 0); } if (mysql_autocommit(Dbc->mariadb, (my_bool)(size_t)ValuePtr)) { return MADB_SetError(&Dbc->Error, MADB_ERR_HY001, mysql_error(Dbc->mariadb), mysql_errno(Dbc->mariadb)); } } Dbc->AutoCommit= (SQLUINTEGER)(SQLULEN)ValuePtr; } break; case SQL_ATTR_CONNECTION_DEAD: /* read only! */ return MADB_SetError(&Dbc->Error, MADB_ERR_HY092, NULL, 0); case SQL_ATTR_CURRENT_CATALOG: { MADB_FREE(Dbc->CatalogName); if (isWChar) { /* IsAnsi will be set before this, even if it is set before connection */ Dbc->CatalogName= MADB_ConvertFromWChar((SQLWCHAR *)ValuePtr, StringLength, NULL, Dbc->ConnOrSrcCharset, NULL); } else Dbc->CatalogName= _strdup((char *)ValuePtr); if (Dbc->mariadb && mysql_select_db(Dbc->mariadb, Dbc->CatalogName)) { return MADB_SetError(&Dbc->Error, MADB_ERR_HY001, mysql_error(Dbc->mariadb), mysql_errno(Dbc->mariadb)); } MADB_RESET(Dbc->CurrentSchema, Dbc->CatalogName); } break; case SQL_ATTR_LOGIN_TIMEOUT: Dbc->LoginTimeout= (SQLUINTEGER)(SQLULEN)ValuePtr; break; case SQL_ATTR_METADATA_ID: Dbc->MetadataId= (SQLUINTEGER)(SQLULEN)ValuePtr; break; case SQL_ATTR_ODBC_CURSORS: { SQLULEN ValidAttrs[]= {3, SQL_CUR_USE_IF_NEEDED, SQL_CUR_USE_ODBC, SQL_CUR_USE_DRIVER}; MADB_CHECK_ATTRIBUTE(Dbc, ValuePtr, ValidAttrs); if ((SQLULEN)ValuePtr != SQL_CUR_USE_ODBC) MADB_SetError(&Dbc->Error, MADB_ERR_01S02, NULL, 0); Dbc->OdbcCursors= SQL_CUR_USE_ODBC; } break; case SQL_ATTR_ENLIST_IN_DTC: /* MS Distributed Transaction Coordinator not supported */ return MADB_SetError(&Dbc->Error, MADB_ERR_HYC00, NULL, 0); case SQL_ATTR_PACKET_SIZE: /* if connection was made, return HY001 */ if (Dbc->mariadb) { return MADB_SetError(&Dbc->Error, MADB_ERR_HY001, NULL, 0); } Dbc->PacketSize= (SQLUINTEGER)(SQLULEN)ValuePtr; break; case SQL_ATTR_QUIET_MODE: Dbc->QuietMode= (HWND)ValuePtr; break; case SQL_ATTR_TRACE: break; case SQL_ATTR_TRACEFILE: break; case SQL_ATTR_TRANSLATE_LIB: break; case SQL_ATTR_TRANSLATE_OPTION: break; case SQL_ATTR_TXN_ISOLATION: if (Dbc->mariadb) { my_bool ValidTx= FALSE; unsigned int i; for (i=0; i < 4; i++) { if (MADB_IsolationLevel[i].SqlIsolation == (SQLLEN)ValuePtr) { char StmtStr[128]; int len= _snprintf(StmtStr, sizeof(StmtStr), "SET SESSION TRANSACTION ISOLATION LEVEL %s", MADB_IsolationLevel[i].StrIsolation); LOCK_MARIADB(Dbc); if (mysql_real_query(Dbc->mariadb, StmtStr, (unsigned long)len)) { UNLOCK_MARIADB(Dbc); return MADB_SetError(&Dbc->Error, MADB_ERR_HY001, mysql_error(Dbc->mariadb), mysql_errno(Dbc->mariadb)); } Dbc->Methods->TrackSession(Dbc); UNLOCK_MARIADB(Dbc); ValidTx= TRUE; break; } } if (!ValidTx) { return MADB_SetError(&Dbc->Error, MADB_ERR_HY024, NULL, 0); } } Dbc->TxnIsolation= (SQLINTEGER)(SQLLEN)ValuePtr; break; default: break; } return Dbc->Error.ReturnValue; } /* }}} */ /* {{{ MADB_DbcHetAttr */ SQLRETURN MADB_DbcGetAttr(MADB_Dbc *Dbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr, my_bool isWChar) { MADB_CLEAR_ERROR(&Dbc->Error); if (!Dbc) return SQL_INVALID_HANDLE; if (!ValuePtr && Attribute != SQL_ATTR_CURRENT_CATALOG) return SQL_SUCCESS; if (Attribute == SQL_ATTR_CURRENT_CATALOG && !StringLengthPtr && (!ValuePtr || !BufferLength)) { return MADB_SetError(&Dbc->Error, MADB_ERR_01004, NULL, 0); } switch(Attribute) { case SQL_ATTR_ACCESS_MODE: *(SQLUINTEGER *)ValuePtr= SQL_MODE_READ_WRITE; break; case SQL_ATTR_ASYNC_ENABLE: *(SQLULEN *)ValuePtr= SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_AUTO_IPD: *(SQLUINTEGER *)ValuePtr= SQL_FALSE; break; case SQL_ATTR_AUTOCOMMIT: /* Not sure why Dbc->AutoCommit is initialized with 4. Probably the idea has been lost in time. But keeping it so far, and workarounding here. Looks like all DMs but iOdbc corrects the value to SQL_AUTOCOMMIT_ON, when returning */ *(SQLUINTEGER *)ValuePtr= (Dbc->AutoCommit != 0 ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); break; case SQL_ATTR_CONNECTION_DEAD: /* ping may fail if status isn't ready, so we need to check errors */ if (mysql_ping(Dbc->mariadb)) *(SQLUINTEGER *)ValuePtr= (mysql_errno(Dbc->mariadb) == CR_SERVER_GONE_ERROR || mysql_errno(Dbc->mariadb) == CR_SERVER_LOST) ? SQL_CD_TRUE : SQL_CD_FALSE; else *(SQLUINTEGER *)ValuePtr= SQL_CD_FALSE; break; case SQL_ATTR_CURRENT_CATALOG: { SQLSMALLINT StrLen; SQLRETURN ret; ret= Dbc->Methods->GetCurrentDB(Dbc, ValuePtr, BufferLength, &StrLen, isWChar); /* if we weren't able to determine the current db, we will return the cached catalog name */ if (!SQL_SUCCEEDED(ret) && Dbc->CatalogName) { MADB_CLEAR_ERROR(&Dbc->Error); StrLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : 0, ValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), Dbc->CatalogName, strlen(Dbc->CatalogName), &Dbc->Error); ret= SQL_SUCCESS; } if (StringLengthPtr != NULL) { *StringLengthPtr= (SQLINTEGER)StrLen; } return ret; } case SQL_ATTR_LOGIN_TIMEOUT: *(SQLUINTEGER *)ValuePtr= Dbc->LoginTimeout; break; case SQL_ATTR_CONNECTION_TIMEOUT: *(SQLUINTEGER *)ValuePtr= 0; break; case SQL_ATTR_METADATA_ID: /* SQL_ATTR_METADATA_ID is SQLUINTEGER attribute on connection level, but SQLULEN on statement level :/ */ *(SQLUINTEGER *)ValuePtr= Dbc->MetadataId; case SQL_ATTR_ODBC_CURSORS: *(SQLULEN*)ValuePtr= SQL_CUR_USE_ODBC; break; case SQL_ATTR_ENLIST_IN_DTC: /* MS Distributed Transaction Coordinator not supported */ MADB_SetError(&Dbc->Error, MADB_ERR_HYC00, NULL, 0); break; case SQL_ATTR_PACKET_SIZE: { unsigned long packet_size= 0; mysql_get_option(Dbc->mariadb, MYSQL_OPT_NET_BUFFER_LENGTH, &packet_size); *(SQLUINTEGER *)ValuePtr= (SQLUINTEGER)packet_size; } break; case SQL_ATTR_QUIET_MODE: #ifdef _WIN32 (HWND)ValuePtr= Dbc->QuietMode; #endif // _WIN32 break; case SQL_ATTR_TRACE: break; case SQL_ATTR_TRACEFILE: break; case SQL_ATTR_TRANSLATE_LIB: break; case SQL_ATTR_TRANSLATE_OPTION: break; case SQL_ATTR_TXN_ISOLATION: /* TxnIsolation wasn't set before we retrieve it from open connection or assume a default of REPETABLE_READ */ if (!Dbc->TxnIsolation) { Dbc->TxnIsolation= SQL_TRANSACTION_REPEATABLE_READ; if (Dbc->mariadb) { Dbc->Methods->GetTxIsolation(Dbc, (SQLINTEGER*)ValuePtr); } } else *(SQLINTEGER*)ValuePtr= Dbc->TxnIsolation; break; default: MADB_SetError(&Dbc->Error, MADB_ERR_HYC00, NULL, 0); break; } return Dbc->Error.ReturnValue; } /* }}} */ /* {{{ MADB_DbcInit() */ MADB_Dbc *MADB_DbcInit(MADB_Env *Env) { MADB_Dbc *Connection= NULL; MADB_CLEAR_ERROR(&Env->Error); if (!(Connection = (MADB_Dbc *)MADB_CALLOC(sizeof(MADB_Dbc)))) goto cleanup; Connection->AutoCommit= 4; Connection->Environment= Env; Connection->Methods= &MADB_Dbc_Methods; //CopyClientCharset(&SourceAnsiCs, &Connection->Charset); InitializeCriticalSection(&Connection->cs); InitializeCriticalSection(&Connection->ListsCs); /* Not sure that critical section is really needed here - this init routine is called when no one has the handle yet */ EnterCriticalSection(&Connection->Environment->cs); /* Save connection in Environment list */ Connection->ListItem.data= (void *)Connection; Connection->Environment->Dbcs= MADB_ListAdd(Connection->Environment->Dbcs, &Connection->ListItem); LeaveCriticalSection(&Connection->Environment->cs); MADB_PutErrorPrefix(NULL, &Connection->Error); return Connection; cleanup: if (Connection) free(Connection); else MADB_SetError(&Env->Error, MADB_ERR_HY001, NULL, 0); return NULL; } /* }}} */ /* {{{ MADB_DbcFree() */ SQLRETURN MADB_DbcFree(MADB_Dbc *Connection) { MADB_Env *Env= NULL; if (!Connection) return SQL_ERROR; MDBUG_C_PRINT(Connection, "%sMADB_DbcFree", "\t->"); MDBUG_C_DUMP(Connection, Connection, 0x); Env= Connection->Environment; /* TODO: If somebody uses connection it won't help if lock it here. At least it requires more fingers movements LOCK_MARIADB(Dbc);*/ if (Connection->mariadb) { mysql_close(Connection->mariadb); Connection->mariadb= NULL; } /*UNLOCK_MARIADB(Dbc);*/ /* todo: delete all descriptors */ EnterCriticalSection(&Env->cs); Connection->Environment->Dbcs= MADB_ListDelete(Connection->Environment->Dbcs, &Connection->ListItem); LeaveCriticalSection(&Env->cs); MADB_FREE(Connection->CatalogName); CloseClientCharset(&Connection->Charset); MADB_FREE(Connection->CurrentSchema); MADB_DSN_Free(Connection->Dsn); DeleteCriticalSection(&Connection->cs); free(Connection); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_DbcGetCurrentDB Fetches current DB. For use without session tracking */ SQLRETURN MADB_DbcGetCurrentDB(MADB_Dbc *Connection, SQLPOINTER CurrentDB, SQLINTEGER CurrentDBLength, SQLSMALLINT *StringLengthPtr, my_bool isWChar) { SQLLEN Size; MYSQL_RES *res; MYSQL_ROW row; MADB_CLEAR_ERROR(&Connection->Error); if (CheckConnection(Connection) == FALSE) { return MADB_SetError(&Connection->Error, MADB_ERR_08003, NULL, 0); } LOCK_MARIADB(Connection); if (mysql_real_query(Connection->mariadb, "SELECT DATABASE()", 17) || (res= mysql_store_result(Connection->mariadb)) == NULL) { MADB_SetNativeError(&Connection->Error, SQL_HANDLE_DBC, Connection->mariadb);; goto end; } row = mysql_fetch_row(res); Size= (SQLSMALLINT)MADB_SetString(isWChar ? & Connection->Charset : 0, (void *)CurrentDB, BUFFER_CHAR_LEN(CurrentDBLength, isWChar), row[0] != NULL ? row[0] : "null", SQL_NTS, &Connection->Error); mysql_free_result(res); if (StringLengthPtr) *StringLengthPtr= isWChar ? (SQLSMALLINT)Size * sizeof(SQLWCHAR) : (SQLSMALLINT)Size; end: UNLOCK_MARIADB(Connection); return Connection->Error.ReturnValue; } /* }}} */ /* {{{ MADB_DbcGetTrackedCurrentDB Fetches current DB. For use without session tracking */ SQLRETURN MADB_DbcGetTrackedCurrentDB(MADB_Dbc* Dbc, SQLPOINTER CurrentDB, SQLINTEGER CurrentDBLength, SQLSMALLINT* StringLengthPtr, my_bool isWChar) { SQLLEN Size; MADB_CLEAR_ERROR(&Dbc->Error); Size = (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : 0, (void*)CurrentDB, BUFFER_CHAR_LEN(CurrentDBLength, isWChar), Dbc->CurrentSchema != NULL ? Dbc->CurrentSchema : "null", SQL_NTS, &Dbc->Error); if (StringLengthPtr) *StringLengthPtr = isWChar ? (SQLSMALLINT)Size * sizeof(SQLWCHAR) : (SQLSMALLINT)Size; return Dbc->Error.ReturnValue; } /* }}} */ BOOL MADB_SqlMode(MADB_Dbc *Connection, enum enum_madb_sql_mode SqlMode) { unsigned int ServerStatus; mariadb_get_infov(Connection->mariadb, MARIADB_CONNECTION_SERVER_STATUS, (void*)&ServerStatus); switch (SqlMode) { case MADB_NO_BACKSLASH_ESCAPES: return test(ServerStatus & SERVER_STATUS_NO_BACKSLASH_ESCAPES); case MADB_ANSI_QUOTES: return test(ServerStatus & SERVER_STATUS_ANSI_QUOTES); } return FALSE; } /* }}} */ /* {{{ MADB_DbcEndTran */ SQLRETURN MADB_DbcEndTran(MADB_Dbc *Dbc, SQLSMALLINT CompletionType) { MADB_CLEAR_ERROR(&Dbc->Error); if (!Dbc) return SQL_INVALID_HANDLE; LOCK_MARIADB(Dbc); switch (CompletionType) { case SQL_ROLLBACK: if (Dbc->mariadb && mysql_rollback(Dbc->mariadb)) MADB_SetNativeError(&Dbc->Error, SQL_HANDLE_DBC, Dbc->mariadb); break; case SQL_COMMIT: if (Dbc->mariadb && mysql_commit(Dbc->mariadb)) MADB_SetNativeError(&Dbc->Error, SQL_HANDLE_DBC, Dbc->mariadb); break; default: MADB_SetError(&Dbc->Error, MADB_ERR_HY012, NULL, 0); } Dbc->Methods->TrackSession(Dbc); UNLOCK_MARIADB(Dbc); return Dbc->Error.ReturnValue; } /* }}} */ /* {{{ MADB_AddInitCommand */ static int MADB_AddInitCommand(MYSQL* mariadb, MADB_DynString *InitCmd, unsigned long MultiStmtAllowed, const char *StmtToAdd) { if (MultiStmtAllowed == 0) { mysql_optionsv(mariadb, MYSQL_INIT_COMMAND, StmtToAdd); return 0; } else { if (InitCmd->length != 0) { if (MADB_DynstrAppendMem(InitCmd, ";", 1)) { return 1; } } return MADB_DynstrAppend(InitCmd, StmtToAdd); } } /* }}} */ /* {{{ MADB_Dbc_ConnectDB Mind that this function is used for establishing connection from the setup lib */ SQLRETURN MADB_DbcConnectDB(MADB_Dbc *Connection, MADB_Dsn *Dsn) { char StmtStr[128]; my_bool ReportDataTruncation= 1; unsigned int i; unsigned long client_flags= CLIENT_MULTI_RESULTS; my_bool my_reconnect= 1; int protocol = MYSQL_PROTOCOL_TCP; MADB_DynString InitCmd; const char* defaultSchema= NULL; if (!Connection || !Dsn) return SQL_ERROR; MADB_CLEAR_ERROR(&Connection->Error); if (Connection->mariadb == NULL) { if (!(Connection->mariadb= mysql_init(NULL))) { MADB_SetError(&Connection->Error, MADB_ERR_HY001, NULL, 0); goto end; } } if( !MADB_IS_EMPTY(Dsn->ConnCPluginsDir)) { mysql_optionsv(Connection->mariadb, MYSQL_PLUGIN_DIR, Dsn->ConnCPluginsDir); } else { if (DefaultPluginLocation != NULL) { mysql_optionsv(Connection->mariadb, MYSQL_PLUGIN_DIR, DefaultPluginLocation); } } if (Dsn->ReadMycnf != '\0') { mysql_optionsv(Connection->mariadb, MYSQL_READ_DEFAULT_GROUP, (void *)"odbc"); } /* If a client character set was specified in DSN, we will always use it. Otherwise for ANSI applications we will use the current character set, for unicode connections we use utf8 */ { const char* cs_name= NULL; if (!MADB_IS_EMPTY(Dsn->CharacterSet)) { if (strcmp(Dsn->CharacterSet, "utf8") == 0) { cs_name= utf8mb3; } cs_name= Dsn->CharacterSet; } else if (Connection->IsAnsi) { MARIADB_CHARSET_INFO *cs= mariadb_get_charset_by_name("auto"); cs_name= cs->csname; } if (InitClientCharset(&Connection->Charset, MADB_IS_EMPTY(cs_name) ? "utf8mb4" : cs_name)) { /* Memory allocation error */ MADB_SetError(&Connection->Error, MADB_ERR_HY001, NULL, 0); goto end; } if (iOdbc() && strcmp(Connection->Charset.cs_info->csname, "swe7") == 0) { MADB_SetError(&Connection->Error, MADB_ERR_HY001, "Charset SWE7 is not supported with iODBC", 0); goto end; } if (!Connection->IsAnsi || iOdbc()) { /* If application is not ansi, we should convert wchar into connection string */ Connection->ConnOrSrcCharset= &Connection->Charset; } } /* todo: error handling */ mysql_optionsv(Connection->mariadb, MYSQL_SET_CHARSET_NAME, Connection->Charset.cs_info->csname); /* This should go before any DSN_OPTION macro use. I don't know why can't we use Dsn directly, though */ Connection->Options = Dsn->Options; if (DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS)) { client_flags|= CLIENT_MULTI_STATEMENTS; MADB_InitDynamicString(&InitCmd, "", 1024, 1024); } if (Dsn->InitCommand && Dsn->InitCommand[0]) { MADB_AddInitCommand(Connection->mariadb, &InitCmd, DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS), Dsn->InitCommand); } /* Turn sql_auto_is_null behavior off. For more details see: http://bugs.mysql.com/bug.php?id=47005 */ MADB_AddInitCommand(Connection->mariadb, &InitCmd, DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS), "SET SESSION SQL_AUTO_IS_NULL=0"); /* set autocommit behavior */ if (Connection->AutoCommit != 0) { MADB_AddInitCommand(Connection->mariadb, &InitCmd, DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS), "SET autocommit=1"); } else { MADB_AddInitCommand(Connection->mariadb, &InitCmd, DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS), "SET autocommit=0"); } /* Set isolation level */ if (Connection->IsolationLevel) { for (i = 0; i < 4; i++) { if (MADB_IsolationLevel[i].SqlIsolation == Connection->IsolationLevel) { _snprintf(StmtStr, sizeof(StmtStr), "SET SESSION TRANSACTION ISOLATION LEVEL %s", MADB_IsolationLevel[i].StrIsolation); MADB_AddInitCommand(Connection->mariadb, &InitCmd, DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS), StmtStr); break; } } } /* If multistmts allowed - we've put all queries to run in InitCmd. Now need to set it to MYSQL_INIT_COMMAND option */ if (DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS)) { mysql_optionsv(Connection->mariadb, MYSQL_INIT_COMMAND, InitCmd.str); } if (Dsn->ConnectionTimeout) mysql_optionsv(Connection->mariadb, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&Dsn->ConnectionTimeout); if (Dsn->ReadTimeout) mysql_optionsv(Connection->mariadb, MYSQL_OPT_READ_TIMEOUT, (const char *)&Dsn->ReadTimeout); if (Dsn->WriteTimeout) mysql_optionsv(Connection->mariadb, MYSQL_OPT_WRITE_TIMEOUT, (const char *)&Dsn->WriteTimeout); if (DSN_OPTION(Connection, MADB_OPT_FLAG_AUTO_RECONNECT)) mysql_optionsv(Connection->mariadb, MYSQL_OPT_RECONNECT, &my_reconnect); if (DSN_OPTION(Connection, MADB_OPT_FLAG_NO_SCHEMA)) client_flags|= CLIENT_NO_SCHEMA; if (DSN_OPTION(Connection, MADB_OPT_FLAG_IGNORE_SPACE)) client_flags|= CLIENT_IGNORE_SPACE; if (DSN_OPTION(Connection, MADB_OPT_FLAG_FOUND_ROWS)) client_flags|= CLIENT_FOUND_ROWS; if (DSN_OPTION(Connection, MADB_OPT_FLAG_COMPRESSED_PROTO)) client_flags|= CLIENT_COMPRESS; if (Dsn->InteractiveClient) { mysql_optionsv(Connection->mariadb, MARIADB_OPT_INTERACTIVE, 1); } /* enable truncation reporting */ mysql_optionsv(Connection->mariadb, MYSQL_REPORT_DATA_TRUNCATION, &ReportDataTruncation); if (Dsn->Socket) { protocol= MYSQL_PROTOCOL_SOCKET; } else if (Dsn->IsNamedPipe) /* DSN_OPTION(Connection, MADB_OPT_FLAG_NAMED_PIPE) */ { mysql_optionsv(Connection->mariadb, MYSQL_OPT_NAMED_PIPE, (void*)Dsn->ServerName); protocol = MYSQL_PROTOCOL_PIPE; } else if (Dsn->Port > 0 || Dsn->IsTcpIp) { protocol = MYSQL_PROTOCOL_TCP; if (Dsn->Port == 0) { Dsn->Port= 3306; } } mysql_optionsv(Connection->mariadb, MYSQL_OPT_PROTOCOL, (void*)&protocol); { /* I don't think it's possible to have empty strings or only spaces in the string here, but I prefer to have this paranoid check to make sure we dont' them */ const char *SslKey= ltrim(Dsn->SslKey); const char *SslCert= ltrim(Dsn->SslCert); const char *SslCa= ltrim(Dsn->SslCa); const char *SslCaPath= ltrim(Dsn->SslCaPath); const char *SslCipher= ltrim(Dsn->SslCipher); if (!MADB_IS_EMPTY(SslCa) || !MADB_IS_EMPTY(SslCaPath) || !MADB_IS_EMPTY(SslCipher) || !MADB_IS_EMPTY(SslCert) || !MADB_IS_EMPTY(SslKey)) { char Enable= 1; mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_ENFORCE, &Enable); if (!MADB_IS_EMPTY(SslKey)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_KEY, SslKey); } if (!MADB_IS_EMPTY(SslCert)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CERT, SslCert); } if (!MADB_IS_EMPTY(SslCa)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CA, SslCa); } if (!MADB_IS_EMPTY(SslCaPath)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CAPATH, SslCaPath); } if (!MADB_IS_EMPTY(SslCipher)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CIPHER, SslCipher); } if (Dsn->TlsVersion > 0) { char TlsVersion[sizeof(TlsVersionName) + sizeof(TlsVersionBits) - 1], *Ptr= TlsVersion; /* All names + (n-1) comma */ unsigned int i, NeedComma= 0; for (i= 0; i < sizeof(TlsVersionBits); ++i) { if (Dsn->TlsVersion & TlsVersionBits[i]) { if (NeedComma != 0) { *Ptr++= ','; } else { NeedComma= 1; } strcpy(Ptr, TlsVersionName[i]); Ptr += strlen(TlsVersionName[i]); } } mysql_optionsv(Connection->mariadb, MARIADB_OPT_TLS_VERSION, (void *)TlsVersion); } } if (Dsn->SslVerify) { const unsigned int verify= 0x01010101; mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char*)&verify); } else { const unsigned int verify= 0; mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char*)&verify); } } if (Dsn->ForceTls != '\0') { const unsigned int ForceTls= 0x01010101; mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_ENFORCE, (const char*)&ForceTls); } if (!MADB_IS_EMPTY(Dsn->SslCrl)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CRL, Dsn->SslCrl); } if (!MADB_IS_EMPTY(Dsn->SslCrlPath)) { mysql_optionsv(Connection->mariadb, MYSQL_OPT_SSL_CRLPATH, Dsn->SslCrlPath); } if (!MADB_IS_EMPTY(Dsn->ServerKey)) { mysql_optionsv(Connection->mariadb, MYSQL_SERVER_PUBLIC_KEY, Dsn->ServerKey); } if (!MADB_IS_EMPTY(Dsn->TlsPeerFp)) { mysql_optionsv(Connection->mariadb, MARIADB_OPT_TLS_PEER_FP, (void*)Dsn->TlsPeerFp); } if (!MADB_IS_EMPTY(Dsn->TlsPeerFpList)) { mysql_optionsv(Connection->mariadb, MARIADB_OPT_TLS_PEER_FP_LIST, (void*)Dsn->TlsPeerFpList); } if (!MADB_IS_EMPTY(Dsn->TlsKeyPwd)) { mysql_optionsv(Connection->mariadb, MARIADB_OPT_TLS_PASSPHRASE, (void*)Dsn->TlsKeyPwd); } /* set default catalog. If we have value set via SQL_ATTR_CURRENT_CATALOG - it takes priority. Otherwise - DSN */ if (Connection->CatalogName && Connection->CatalogName[0]) { defaultSchema= Connection->CatalogName; } else if (Dsn->Catalog && Dsn->Catalog[0]) { //MADB_RESET(Connection->CatalogName, Dsn->Catalog); defaultSchema= Dsn->Catalog; } if (!mysql_real_connect(Connection->mariadb, Dsn->Socket ? "localhost" : Dsn->ServerName, Dsn->UserName, Dsn->Password, defaultSchema, Dsn->Port, Dsn->Socket, client_flags)) { goto err; } /* I guess it is better not to do that at all. Besides SQL_ATTR_PACKET_SIZE is actually not for max packet size */ if (Connection->PacketSize) { /*_snprintf(StmtStr, 128, "SET GLOBAL max_allowed_packet=%ld", Connection-> PacketSize); if (mysql_query(Connection->mariadb, StmtStr)) goto err;*/ } MADB_SetCapabilities(Connection, mysql_get_server_version(Connection->mariadb), mysql_get_server_name(Connection->mariadb)); if (MADB_ServerSupports(Connection, MADB_SESSION_TRACKING) == TRUE) { int len; if (DSN_OPTION(Connection, MADB_OPT_FLAG_MULTI_STATEMENTS)) { char buffer[sizeof("SET session_track_schema= ON;SET session_track_system_variables='autocommit'") + 1/*','*/ + 21/*transaction_isolation*/]; len= _snprintf(buffer, sizeof(buffer), "SET session_track_schema= ON;SET session_track_system_variables='autocommit,%s'", MADB_GetTxIsolationVarName(Connection)); if (mysql_real_query(Connection->mariadb, buffer, (unsigned long)len) || mysql_next_result(Connection->mariadb)) { goto err; } } else { char buffer[sizeof("SET session_track_system_variables='autocommit'") + 1/*','*/ + 21/*transaction_isolation*/]; len= _snprintf(buffer, sizeof(buffer), "SET session_track_system_variables='autocommit,%s'", MADB_GetTxIsolationVarName(Connection)); if (mysql_real_query(Connection->mariadb, "SET session_track_schema= ON", 28) || mysql_real_query(Connection->mariadb, buffer, (unsigned long)len)) { goto err; } } if (defaultSchema != NULL) { Connection->CurrentSchema= _strdup(defaultSchema); } } else { /* Default methods are for use of session tracking */ Connection->Methods->GetCurrentDB= &MADB_DbcGetCurrentDB; Connection->Methods->TrackSession= &MADB_DbcDummyTrackSession; Connection->Methods->GetTxIsolation= &MADB_DbcGetServerTxIsolation; } goto end; err: MADB_SetNativeError(&Connection->Error, SQL_HANDLE_DBC, Connection->mariadb); end: if (Connection->Error.ReturnValue == SQL_ERROR && Connection->mariadb) { mysql_close(Connection->mariadb); Connection->mariadb= NULL; } return Connection->Error.ReturnValue; } /* }}} */ /* {{{ MADB_DbcGetFunctions */ SQLRETURN MADB_DbcGetFunctions(MADB_Dbc *Dbc, SQLUSMALLINT FunctionId, SQLUSMALLINT *SupportedPtr) { unsigned int i, Elements= sizeof(MADB_supported_api) / sizeof(SQLUSMALLINT); switch(FunctionId) { case SQL_API_ODBC3_ALL_FUNCTIONS: /* clear ptr */ memset(SupportedPtr, 0, sizeof(SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE); for (i=0; i < Elements; ++i) { SQLUSMALLINT function= MADB_supported_api[i]; SupportedPtr[function >> 4]|= (1 << (function & 0x000F)); } break; case SQL_API_ALL_FUNCTIONS: /* Set all to SQL_FALSE (0) */ memset(SupportedPtr, 0, sizeof(SQLUSMALLINT) * 100); for (i=0; i < Elements; i++) if (MADB_supported_api[i] < 100) SupportedPtr[MADB_supported_api[i]]= SQL_TRUE; break; default: *SupportedPtr= SQL_FALSE; for (i=0; i < Elements; i++) if (MADB_supported_api[i] == FunctionId) { *SupportedPtr= SQL_TRUE; break; } break; } return SQL_SUCCESS; } /* }}} */ /* {{{ IsStringInfoType */ int IsStringInfoType(SQLSMALLINT InfoType) { switch (InfoType) { case SQL_ACCESSIBLE_PROCEDURES: case SQL_ACCESSIBLE_TABLES: case SQL_CATALOG_NAME: case SQL_CATALOG_NAME_SEPARATOR: case SQL_CATALOG_TERM: case SQL_COLLATION_SEQ: case SQL_COLUMN_ALIAS: case SQL_DATA_SOURCE_NAME: case SQL_DATABASE_NAME: case SQL_DBMS_NAME: case SQL_DBMS_VER: case SQL_DESCRIBE_PARAMETER: case SQL_DRIVER_NAME: case SQL_DRIVER_ODBC_VER: case SQL_DRIVER_VER: case SQL_EXPRESSIONS_IN_ORDERBY: case SQL_INTEGRITY: case SQL_KEYWORDS: case SQL_LIKE_ESCAPE_CLAUSE: case SQL_MAX_ROW_SIZE_INCLUDES_LONG: case SQL_MULT_RESULT_SETS: case SQL_MULTIPLE_ACTIVE_TXN: case SQL_NEED_LONG_DATA_LEN: case SQL_ORDER_BY_COLUMNS_IN_SELECT: case SQL_PROCEDURE_TERM: case SQL_PROCEDURES: case SQL_ROW_UPDATES: case SQL_SCHEMA_TERM: case SQL_SEARCH_PATTERN_ESCAPE: case SQL_SERVER_NAME: case SQL_SPECIAL_CHARACTERS: case SQL_TABLE_TERM: case SQL_USER_NAME: case SQL_XOPEN_CLI_YEAR: case SQL_DATA_SOURCE_READ_ONLY: case SQL_IDENTIFIER_QUOTE_CHAR: return 1; } return 0; } /* }}} */ /* {{{ MADB_DbcGetInfo */ SQLRETURN MADB_DbcGetInfo(MADB_Dbc *Dbc, SQLUSMALLINT InfoType, SQLPOINTER InfoValuePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, my_bool isWChar) { SQLSMALLINT SLen= 0; extern Client_Charset utf8; if (!InfoValuePtr && !StringLengthPtr) return SQL_SUCCESS; /* Prety special case - on Windows DM passes NULL instead of InfoValuePtr and own pointer instead of StringLengthPtr. The logic here is not quite clear - I would imagine that truncated status is more appropriate. But UnixODBC does not do so, and we are making connector's behavior consistent */ if (InfoValuePtr != NULL && BufferLength == 0 && StringLengthPtr == NULL && IsStringInfoType(InfoType)) { return SQL_SUCCESS; } MADB_CLEAR_ERROR(&Dbc->Error); switch(InfoType) { case SQL_ACCESSIBLE_PROCEDURES: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_ACCESSIBLE_TABLES: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_ACTIVE_ENVIRONMENTS: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_AGGREGATE_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT | SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM, StringLengthPtr); break; case SQL_ALTER_DOMAIN: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_ALTER_TABLE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_AT_ADD_COLUMN | SQL_AT_DROP_COLUMN, StringLengthPtr); break; #ifdef SQL_ASYNC_DBC_FUNCTIONS case SQL_ASYNC_DBC_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_ASYNC_DBC_NOT_CAPABLE, StringLengthPtr); break; #endif #ifdef SQL_ASYNC_MODE case SQL_ASYNC_MODE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_AM_NONE, StringLengthPtr); break; #endif #ifdef SQL_ASYNC_NOTIFICATION case SQL_ASYNC_NOTIFICATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_ASYNC_NOTIFICATION_NOT_CAPABLE, StringLengthPtr); break; #endif case SQL_BATCH_ROW_COUNT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_BRC_EXPLICIT, StringLengthPtr); break; case SQL_BATCH_SUPPORT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_BS_SELECT_EXPLICIT | SQL_BS_ROW_COUNT_EXPLICIT | SQL_BS_SELECT_PROC | SQL_BS_ROW_COUNT_PROC, StringLengthPtr); break; case SQL_BOOKMARK_PERSISTENCE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CATALOG_LOCATION: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_CL_START, StringLengthPtr); break; case SQL_CATALOG_NAME: /* Todo: MyODBC Driver has a DSN configuration for diabling catalog usage: but it's not implemented in MAODBC */ SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_CATALOG_NAME_SEPARATOR: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), ".", SQL_NTS, &Dbc->Error); break; case SQL_CATALOG_TERM: /* todo: See comment for SQL_CATALOG_NAME */ SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "database", SQL_NTS, &Dbc->Error); break; case SQL_CATALOG_USAGE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CU_DML_STATEMENTS | SQL_CU_INDEX_DEFINITION | SQL_CU_PROCEDURE_INVOCATION | SQL_CU_PRIVILEGE_DEFINITION | SQL_CU_TABLE_DEFINITION, StringLengthPtr); break; case SQL_COLLATION_SEQ: { MY_CHARSET_INFO cs; mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_MARIADB_CHARSET_INFO, (void*)&cs); SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), cs.name, SQL_NTS, &Dbc->Error); break; } case SQL_COLUMN_ALIAS: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_CONCAT_NULL_BEHAVIOR: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_CB_NULL, StringLengthPtr); break; case SQL_CONVERT_BIGINT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_BINARY: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CONVERT_BIT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_CHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_WCHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; #ifdef SQL_CONVERT_GUID case SQL_CONVERT_GUID: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; #endif case SQL_CONVERT_DATE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_DECIMAL: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_DOUBLE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_FLOAT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_INTEGER: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_INTERVAL_YEAR_MONTH: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CONVERT_INTERVAL_DAY_TIME: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CONVERT_LONGVARBINARY: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CONVERT_LONGVARCHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_WLONGVARCHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_NUMERIC: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_REAL: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_SMALLINT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_TIME: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_TIMESTAMP: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_TINYINT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_VARBINARY: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CONVERT_VARCHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_WVARCHAR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, MADB_SUPPORTED_CONVERSIONS, StringLengthPtr); break; case SQL_CONVERT_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CORRELATION_NAME: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_CN_DIFFERENT, StringLengthPtr); break; case SQL_CREATE_ASSERTION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_CHARACTER_SET: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_COLLATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_DOMAIN: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_SCHEMA: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_TABLE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CT_COLUMN_COLLATION | SQL_CT_COLUMN_DEFAULT | SQL_CT_COMMIT_DELETE | SQL_CT_CREATE_TABLE | SQL_CT_LOCAL_TEMPORARY, StringLengthPtr); break; case SQL_CREATE_TRANSLATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_CREATE_VIEW: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CV_CASCADED | SQL_CV_CHECK_OPTION | SQL_CV_CREATE_VIEW, StringLengthPtr); break; case SQL_CURSOR_COMMIT_BEHAVIOR: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_CB_PRESERVE, StringLengthPtr); break; case SQL_CURSOR_ROLLBACK_BEHAVIOR: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_CB_PRESERVE, StringLengthPtr); break; case SQL_CURSOR_SENSITIVITY: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_UNSPECIFIED, StringLengthPtr); break; case SQL_DATA_SOURCE_NAME: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), Dbc->Dsn ? Dbc->Dsn->DSNName : "", SQL_NTS, &Dbc->Error); break; case SQL_DATABASE_NAME: return Dbc->Methods->GetCurrentDB(Dbc, InfoValuePtr, BufferLength, (SQLSMALLINT *)StringLengthPtr, isWChar); break; case SQL_DATETIME_LITERALS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_DL_SQL92_DATE | SQL_DL_SQL92_TIME | SQL_DL_SQL92_TIMESTAMP, StringLengthPtr); break; case SQL_DBMS_NAME: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), Dbc->mariadb ? (char *)mysql_get_server_name(Dbc->mariadb) : "MariaDB", SQL_NTS, &Dbc->Error); break; case SQL_DBMS_VER: { char Version[13]; unsigned long ServerVersion= 0L; if (Dbc->mariadb) { ServerVersion= mysql_get_server_version(Dbc->mariadb); _snprintf(Version, sizeof(Version), "%02u.%02u.%06u", ServerVersion / 10000, (ServerVersion % 10000) / 100, ServerVersion % 100); } else Version[0]= 0; SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &utf8 : 0, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), Version[0] ? Version : "", SQL_NTS, &Dbc->Error); } break; case SQL_DDL_INDEX: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX, StringLengthPtr); break; case SQL_DEFAULT_TXN_ISOLATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DESCRIBE_PARAMETER: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; #ifdef SQL_DRIVER_AWARE_POOLING_SUPPORTED case SQL_DRIVER_AWARE_POOLING_SUPPORTED: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_DRIVER_AWARE_POOLING_NOT_CAPABLE, StringLengthPtr); break; #endif /* Handled by driver manager */ case SQL_DRIVER_HDBC: break; case SQL_DRIVER_HENV: break; case SQL_DRIVER_HLIB: break; case SQL_DRIVER_HSTMT: break; case SQL_DRIVER_NAME: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), MADB_DRIVER_NAME, SQL_NTS, &Dbc->Error); break; case SQL_DRIVER_ODBC_VER: { char *OdbcVersion = "03.51"; /* DM requests this info before Dbc->Charset initialized. Thus checking if it is, and use utf8 by default The other way would be to use utf8 when Dbc initialized */ SLen= (SQLSMALLINT)MADB_SetString(isWChar ? (Dbc->Charset.cs_info ? &Dbc->Charset : &utf8 ): NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), OdbcVersion, SQL_NTS, &Dbc->Error); } break; case SQL_DRIVER_VER: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), MARIADB_ODBC_VERSION, SQL_NTS, &Dbc->Error); break; /*******************************/ case SQL_DROP_ASSERTION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_CHARACTER_SET: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_COLLATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_DOMAIN: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_SCHEMA: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_TABLE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_DT_CASCADE | SQL_DT_DROP_TABLE | SQL_DT_RESTRICT, StringLengthPtr); break; case SQL_DROP_TRANSLATION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_DROP_VIEW: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_DV_CASCADE | SQL_DV_DROP_VIEW | SQL_DV_RESTRICT, StringLengthPtr); break; case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA1_ABSOLUTE | SQL_CA1_BULK_ADD | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_NEXT | SQL_CA1_POSITIONED_DELETE | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POS_DELETE | SQL_CA1_POS_POSITION | SQL_CA1_POS_REFRESH | SQL_CA1_POS_UPDATE | SQL_CA1_RELATIVE, StringLengthPtr); break; case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA2_CRC_EXACT | SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_SENSITIVITY_ADDITIONS | SQL_CA2_SENSITIVITY_DELETIONS | SQL_CA2_SENSITIVITY_UPDATES | SQL_CA2_SIMULATE_TRY_UNIQUE, StringLengthPtr); break; case SQL_EXPRESSIONS_IN_ORDERBY: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_FILE_USAGE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_FILE_NOT_SUPPORTED, StringLengthPtr); break; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA1_ABSOLUTE | SQL_CA1_BULK_ADD | SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_NEXT | SQL_CA1_POSITIONED_DELETE | SQL_CA1_POSITIONED_UPDATE | SQL_CA1_POS_DELETE | SQL_CA1_POS_POSITION | SQL_CA1_POS_REFRESH | SQL_CA1_POS_UPDATE | SQL_CA1_RELATIVE, StringLengthPtr); break; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA2_CRC_EXACT | SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_INSERT | SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_UPDATE, StringLengthPtr); break; case SQL_GETDATA_EXTENSIONS: { MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BLOCK | SQL_GD_BOUND, StringLengthPtr); break; } case SQL_GROUP_BY: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_GB_NO_RELATION, StringLengthPtr); break; case SQL_IDENTIFIER_CASE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_IC_MIXED, StringLengthPtr); break; case SQL_IDENTIFIER_QUOTE_CHAR: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), MADB_SqlMode(Dbc, MADB_ANSI_QUOTES) ? "\"" : "`", SQL_NTS, &Dbc->Error); break; case SQL_INDEX_KEYWORDS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_IK_ALL, StringLengthPtr); break; case SQL_INFO_SCHEMA_VIEWS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_ISV_CHARACTER_SETS | SQL_ISV_COLLATIONS | SQL_ISV_COLUMNS | SQL_ISV_COLUMN_PRIVILEGES | SQL_ISV_KEY_COLUMN_USAGE | SQL_ISV_REFERENTIAL_CONSTRAINTS | SQL_ISV_TABLES | SQL_ISV_TABLE_PRIVILEGES | SQL_ISV_TABLE_CONSTRAINTS | SQL_ISV_VIEWS, StringLengthPtr); break; case SQL_INSERT_STATEMENT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED | SQL_IS_SELECT_INTO, StringLengthPtr); break; case SQL_INTEGRITY: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_KEYSET_CURSOR_ATTRIBUTES1: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_KEYSET_CURSOR_ATTRIBUTES2: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_KEYWORDS: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL," "CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND," "DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW,DIV," "DUAL,EACH,ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8," "FORCE,FULLTEXT,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE," "HOUR_SECOND,IF,IGNORE,INFILE,INOUT,INT1,INT2,INT3,INT4,INT8," "ITERATE,KEY,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME," "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY," "MEDIUMBLOB,MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND," "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIONALLY," "OUT,OUTFILE,PURGE,RANGE,READS,READ_ONLY,READ_WRITE,REGEXP,RELEASE," "RENAME,REPEAT,REPLACE,REQUIRE,RETURN,RLIKE,SCHEMAS," "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SPATIAL,SPECIFIC," "SQLEXCEPTION,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT," "SSL,STARTING,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT," "TRIGGER,UNDO,UNLOCK,UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP," "VARBINARY,VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL,GENERAL," "IGNORE_SERVER_IDS,MASTER_HEARTBEAT_PERIOD,MAXVALUE,RESIGNAL,SIGNAL," "SLOW", SQL_NTS, &Dbc->Error); break; case SQL_LIKE_ESCAPE_CLAUSE: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_BINARY_LITERAL_LEN: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_CATALOG_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, NAME_CHAR_LEN * SYSTEM_MB_MAX_CHAR_LENGTH, StringLengthPtr); break; case SQL_MAX_CHAR_LITERAL_LEN: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_COLUMN_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, NAME_CHAR_LEN * SYSTEM_MB_MAX_CHAR_LENGTH - 1, StringLengthPtr); break; case SQL_MAX_COLUMNS_IN_GROUP_BY: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_COLUMNS_IN_INDEX: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 32, StringLengthPtr); break; case SQL_MAX_COLUMNS_IN_ORDER_BY: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_COLUMNS_IN_SELECT: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_COLUMNS_IN_TABLE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_CONCURRENT_ACTIVITIES: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_CURSOR_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, MADB_MAX_CURSOR_NAME, StringLengthPtr); break; case SQL_MAX_DRIVER_CONNECTIONS: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_IDENTIFIER_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, NAME_CHAR_LEN * SYSTEM_MB_MAX_CHAR_LENGTH, StringLengthPtr); break; case SQL_MAX_INDEX_SIZE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 3072, StringLengthPtr); break; case SQL_MAX_PROCEDURE_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, NAME_CHAR_LEN * SYSTEM_MB_MAX_CHAR_LENGTH, StringLengthPtr); break; case SQL_MAX_ROW_SIZE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_ROW_SIZE_INCLUDES_LONG: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_MAX_SCHEMA_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 0, StringLengthPtr); break; case SQL_MAX_STATEMENT_LEN: { size_t max_packet_size; mariadb_get_infov(Dbc->mariadb, MARIADB_MAX_ALLOWED_PACKET, (void*)&max_packet_size); MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, (SQLUINTEGER)max_packet_size, StringLengthPtr); } break; case SQL_MAX_TABLE_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, NAME_CHAR_LEN * SYSTEM_MB_MAX_CHAR_LENGTH, StringLengthPtr); break; case SQL_MAX_TABLES_IN_SELECT: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, 63, StringLengthPtr); break; case SQL_MAX_USER_NAME_LEN: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, USERNAME_LENGTH, StringLengthPtr); break; case SQL_MULT_RESULT_SETS: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_MULTIPLE_ACTIVE_TXN: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_NEED_LONG_DATA_LEN: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_NON_NULLABLE_COLUMNS: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_NNC_NON_NULL, StringLengthPtr); break; case SQL_NULL_COLLATION: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_NC_LOW, StringLengthPtr); break; case SQL_NUMERIC_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_FN_NUM_ABS | SQL_FN_NUM_ACOS | SQL_FN_NUM_ASIN | SQL_FN_NUM_ATAN | SQL_FN_NUM_ATAN2 | SQL_FN_NUM_CEILING | SQL_FN_NUM_COS | SQL_FN_NUM_COT | SQL_FN_NUM_EXP | SQL_FN_NUM_FLOOR | SQL_FN_NUM_LOG | SQL_FN_NUM_MOD | SQL_FN_NUM_SIGN | SQL_FN_NUM_SIN | SQL_FN_NUM_SQRT | SQL_FN_NUM_TAN | SQL_FN_NUM_PI | SQL_FN_NUM_RAND | SQL_FN_NUM_DEGREES | SQL_FN_NUM_LOG10 | SQL_FN_NUM_POWER | SQL_FN_NUM_RADIANS | SQL_FN_NUM_ROUND | SQL_FN_NUM_TRUNCATE, StringLengthPtr); break; case SQL_ODBC_API_CONFORMANCE: MADB_SET_NUM_VAL(SQLSMALLINT, InfoValuePtr, SQL_OAC_LEVEL1, StringLengthPtr); break; case SQL_ODBC_INTERFACE_CONFORMANCE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_OIC_CORE, StringLengthPtr); break; case SQL_ODBC_SQL_CONFORMANCE: MADB_SET_NUM_VAL(SQLSMALLINT, InfoValuePtr, SQL_OSC_CORE, StringLengthPtr); break; case SQL_ODBC_VER: break; case SQL_OUTER_JOINS: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_OJ_CAPABILITIES: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_NESTED | SQL_OJ_INNER, StringLengthPtr); break; case SQL_ORDER_BY_COLUMNS_IN_SELECT: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_PARAM_ARRAY_ROW_COUNTS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_PARC_NO_BATCH, StringLengthPtr); break; case SQL_PARAM_ARRAY_SELECTS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_PAS_NO_BATCH, StringLengthPtr); break; case SQL_PROCEDURE_TERM: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "stored procedure", SQL_NTS, &Dbc->Error); break; case SQL_PROCEDURES: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "Y", SQL_NTS, &Dbc->Error); break; case SQL_QUOTED_IDENTIFIER_CASE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_IC_SENSITIVE, StringLengthPtr); break; case SQL_ROW_UPDATES: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; case SQL_SCHEMA_TERM: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "", SQL_NTS, &Dbc->Error); break; case SQL_SCHEMA_USAGE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_SCROLL_OPTIONS: { SQLUINTEGER Options= SQL_SO_FORWARD_ONLY; if (!MA_ODBC_CURSOR_FORWARD_ONLY(Dbc)) Options|= SQL_SO_STATIC; if (MA_ODBC_CURSOR_DYNAMIC(Dbc)) Options|= SQL_SO_DYNAMIC; MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, Options, StringLengthPtr); } break; case SQL_SEARCH_PATTERN_ESCAPE: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "\\", SQL_NTS, &Dbc->Error); break; case SQL_SERVER_NAME: { const char *Host= ""; if (Dbc->mariadb) { mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_HOST, (void*)&Host); } SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), Host, SQL_NTS, &Dbc->Error); break; } case SQL_SPECIAL_CHARACTERS: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "\"\\/", SQL_NTS, &Dbc->Error); break; case SQL_SQL_CONFORMANCE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SC_SQL92_INTERMEDIATE, StringLengthPtr); break; case SQL_SQL92_DATETIME_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SDF_CURRENT_DATE | SQL_SDF_CURRENT_TIME | SQL_SDF_CURRENT_TIMESTAMP, StringLengthPtr); break; case SQL_SQL92_FOREIGN_KEY_DELETE_RULE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_SQL92_FOREIGN_KEY_UPDATE_RULE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_SQL92_GRANT: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SG_DELETE_TABLE | SQL_SG_INSERT_COLUMN | SQL_SG_INSERT_TABLE | SQL_SG_REFERENCES_COLUMN | SQL_SG_REFERENCES_TABLE | SQL_SG_SELECT_TABLE | SQL_SG_UPDATE_COLUMN | SQL_SG_UPDATE_TABLE | SQL_SG_WITH_GRANT_OPTION, StringLengthPtr); break; case SQL_SQL92_NUMERIC_VALUE_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SNVF_BIT_LENGTH | SQL_SNVF_CHARACTER_LENGTH | SQL_SNVF_CHAR_LENGTH | SQL_SNVF_EXTRACT | SQL_SNVF_OCTET_LENGTH | SQL_SNVF_POSITION, StringLengthPtr); break; case SQL_SQL92_PREDICATES: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SP_BETWEEN | SQL_SP_COMPARISON | SQL_SP_EXISTS | SQL_SP_IN | SQL_SP_ISNOTNULL | SQL_SP_ISNULL | SQL_SP_LIKE | SQL_SP_QUANTIFIED_COMPARISON, StringLengthPtr); break; case SQL_SQL92_RELATIONAL_JOIN_OPERATORS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SRJO_CROSS_JOIN | SQL_SRJO_INNER_JOIN | SQL_SRJO_LEFT_OUTER_JOIN | SQL_SRJO_RIGHT_OUTER_JOIN | SQL_SRJO_NATURAL_JOIN, StringLengthPtr); break; case SQL_SQL92_REVOKE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SR_DELETE_TABLE | SQL_SR_INSERT_COLUMN | SQL_SR_INSERT_TABLE | SQL_SR_REFERENCES_COLUMN | SQL_SR_REFERENCES_TABLE | SQL_SR_SELECT_TABLE | SQL_SR_UPDATE_COLUMN | SQL_SR_UPDATE_TABLE, StringLengthPtr); break; case SQL_SQL92_ROW_VALUE_CONSTRUCTOR: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SRVC_DEFAULT | SQL_SRVC_NULL | SQL_SRVC_ROW_SUBQUERY | SQL_SRVC_VALUE_EXPRESSION, StringLengthPtr); break; case SQL_SQL92_STRING_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SSF_CONVERT | SQL_SSF_LOWER | SQL_SSF_SUBSTRING | SQL_SSF_TRANSLATE | SQL_SSF_TRIM_BOTH | SQL_SSF_TRIM_LEADING | SQL_SSF_TRIM_TRAILING | SQL_SSF_UPPER, StringLengthPtr); break; case SQL_SQL92_VALUE_EXPRESSIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SVE_CASE | SQL_SVE_CAST | SQL_SVE_COALESCE | SQL_SVE_NULLIF, StringLengthPtr); break; case SQL_STANDARD_CLI_CONFORMANCE: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SCC_ISO92_CLI, StringLengthPtr); break; case SQL_STATIC_CURSOR_ATTRIBUTES1: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA1_ABSOLUTE | /* SQL_CA1_BOOKMARK | */ SQL_CA1_NEXT | SQL_CA1_RELATIVE, StringLengthPtr); break; case SQL_STATIC_CURSOR_ATTRIBUTES2: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_CA2_MAX_ROWS_SELECT, StringLengthPtr); break; case SQL_STRING_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_FN_STR_ASCII | SQL_FN_STR_BIT_LENGTH | SQL_FN_STR_CHAR | SQL_FN_STR_CHAR_LENGTH | SQL_FN_STR_CONCAT | SQL_FN_STR_INSERT | SQL_FN_STR_LCASE | SQL_FN_STR_LEFT | SQL_FN_STR_LENGTH | SQL_FN_STR_LOCATE | SQL_FN_STR_LOCATE_2 | SQL_FN_STR_LTRIM | SQL_FN_STR_OCTET_LENGTH | SQL_FN_STR_POSITION | SQL_FN_STR_REPEAT | SQL_FN_STR_REPLACE | SQL_FN_STR_RIGHT | SQL_FN_STR_RTRIM | SQL_FN_STR_SOUNDEX | SQL_FN_STR_SPACE | SQL_FN_STR_SUBSTRING | SQL_FN_STR_UCASE, StringLengthPtr); break; case SQL_SUBQUERIES: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_SQ_COMPARISON | SQL_SQ_CORRELATED_SUBQUERIES | SQL_SQ_EXISTS | SQL_SQ_IN | SQL_SQ_QUANTIFIED, StringLengthPtr); break; case SQL_SYSTEM_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_FN_SYS_DBNAME | SQL_FN_SYS_IFNULL | SQL_FN_SYS_USERNAME, StringLengthPtr); break; case SQL_TABLE_TERM: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "table", SQL_NTS, &Dbc->Error); break; case SQL_TIMEDATE_ADD_INTERVALS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_TIMEDATE_DIFF_INTERVALS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, 0, StringLengthPtr); break; case SQL_TIMEDATE_FUNCTIONS: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_FN_TD_CURDATE | SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME | SQL_FN_TD_CURRENT_TIMESTAMP | SQL_FN_TD_CURTIME | SQL_FN_TD_DAYNAME | SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK | SQL_FN_TD_DAYOFYEAR | SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR | SQL_FN_TD_MINUTE | SQL_FN_TD_MONTH | SQL_FN_TD_MONTHNAME | SQL_FN_TD_NOW | SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND | SQL_FN_TD_TIMESTAMPADD | SQL_FN_TD_TIMESTAMPDIFF | SQL_FN_TD_WEEK | SQL_FN_TD_YEAR, StringLengthPtr); break; case SQL_TXN_CAPABLE: MADB_SET_NUM_VAL(SQLUSMALLINT, InfoValuePtr, SQL_TC_DDL_COMMIT, StringLengthPtr); break; case SQL_TXN_ISOLATION_OPTION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_TXN_READ_COMMITTED | SQL_TXN_READ_UNCOMMITTED | SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE, StringLengthPtr); break; case SQL_UNION: MADB_SET_NUM_VAL(SQLUINTEGER, InfoValuePtr, SQL_U_UNION | SQL_U_UNION_ALL, StringLengthPtr); break; case SQL_USER_NAME: { const char *User= ""; if (Dbc->mariadb) { mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_USER, (void *)&User); } SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), User, SQL_NTS, &Dbc->Error); break; } case SQL_XOPEN_CLI_YEAR: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "1992", SQL_NTS, &Dbc->Error); break; case SQL_DATA_SOURCE_READ_ONLY: SLen= (SQLSMALLINT)MADB_SetString(isWChar ? &Dbc->Charset : NULL, (void *)InfoValuePtr, BUFFER_CHAR_LEN(BufferLength, isWChar), "N", SQL_NTS, &Dbc->Error); break; /* 2.0 types */ case SQL_POS_OPERATIONS: MADB_SET_NUM_VAL(SQLINTEGER, InfoValuePtr, SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD, StringLengthPtr); break; case SQL_STATIC_SENSITIVITY: MADB_SET_NUM_VAL(SQLINTEGER, InfoValuePtr, SQL_SS_DELETIONS | SQL_SS_UPDATES, StringLengthPtr); break; case SQL_LOCK_TYPES: MADB_SET_NUM_VAL(SQLINTEGER, InfoValuePtr, SQL_LCK_NO_CHANGE, StringLengthPtr); break; case SQL_SCROLL_CONCURRENCY: MADB_SET_NUM_VAL(SQLINTEGER, InfoValuePtr, SQL_SCCO_READ_ONLY | SQL_SCCO_OPT_VALUES, StringLengthPtr); break; default: MADB_SetError(&Dbc->Error, MADB_ERR_HY096, NULL, 0); return Dbc->Error.ReturnValue; } if (isWChar && SLen) { SLen*= sizeof(SQLWCHAR); } if (IsStringInfoType(InfoType) && StringLengthPtr) { *StringLengthPtr= SLen; } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_DriverSideMemalocate */ char * MADB_DriverSideAllocate(size_t size) { return (char *)MADB_CALLOC(size); } /* }}} */ /* {{{ MADB_DriverSideFree */ void MADB_DriverSideFree(void *ptr) { MADB_FREE(ptr); } /* }}} */ /* {{{ MADB_DriverConnect */ SQLRETURN MADB_DriverConnect(MADB_Dbc *Dbc, SQLHWND WindowHandle, SQLCHAR *InConnectionString, SQLULEN StringLength1, SQLCHAR *OutConnectionString, SQLULEN BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion) { MADB_Dsn *Dsn; MADB_Drv *Drv= NULL; SQLRETURN ret= SQL_SUCCESS; MADB_Prompt DSNPrompt= { NULL, NULL }; SQLULEN Length; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); Dsn= MADB_DSN_Init(); if (!MADB_ReadConnString(Dsn, (char *)InConnectionString, StringLength1, ';')) { MADB_SetError(&Dbc->Error, MADB_ERR_HY000, "Error while parsing DSN", 0); goto error; } /* if DSN prompt is off, adjusting DriverCompletion */ if (Dsn->ConnectPrompt) DriverCompletion= SQL_DRIVER_NOPROMPT; switch (DriverCompletion) { case SQL_DRIVER_COMPLETE_REQUIRED: case SQL_DRIVER_COMPLETE: case SQL_DRIVER_NOPROMPT: if (SQL_SUCCEEDED(MADB_DbcConnectDB(Dbc, Dsn))) { goto end; } else if (DriverCompletion == SQL_DRIVER_NOPROMPT) { /* For SQL_DRIVER_COMPLETE(_REQUIRED) this is not the end - will show prompt for user */ goto error; } /* If we got here, it means that we had unsuccessful connect attempt with SQL_DRIVER_COMPLETE(_REQUIRED) completion Have to clean that error */ MADB_CLEAR_ERROR(&Dbc->Error); break; case SQL_DRIVER_PROMPT: break; default: MADB_SetError(&Dbc->Error, MADB_ERR_HY110, NULL, 0); goto error; break; } /* Without window handle we can't show a dialog */ if (DriverCompletion != SQL_DRIVER_NOPROMPT && !WindowHandle) { MADB_SetError(&Dbc->Error, MADB_ERR_IM008, NULL, 0); goto error; } if (DriverCompletion == SQL_DRIVER_COMPLETE_REQUIRED) Dsn->isPrompt= MAODBC_PROMPT_REQUIRED; else Dsn->isPrompt= MAODBC_PROMPT; /* We need to obtain the driver name to load maodbcs.dll, if it's not stored inside DSN, error IM007 (dialog prohibited) will be returned */ if (!Dsn->Driver) { MADB_SetError(&Dbc->Error, MADB_ERR_IM007, NULL, 0); goto error; } if (!(Drv= MADB_DriverGet(Dsn->Driver))) { MADB_SetError(&Dbc->Error, MADB_ERR_IM003, NULL, 0); goto error; } if (!Drv->SetupLibrary) { MADB_SetError(&Dbc->Error, MADB_ERR_HY000, "Couldn't determine setup library", 0); goto error; } switch (DSNPrompt_Lookup(&DSNPrompt, Drv->SetupLibrary)) { case 0: break; case MADB_PROMPT_NOT_SUPPORTED: MADB_SetError(&Dbc->Error, MADB_ERR_HY000, "Prompting is not supported on this platform", 0); goto error; case MADB_PROMPT_COULDNT_LOAD: MADB_SetError(&Dbc->Error, MADB_ERR_HY000, "Couldn't load the setup library", 0); goto error; } Dsn->allocator= MADB_DriverSideAllocate; Dsn->free= MADB_DriverSideFree; if (DSNPrompt.Call((HWND)WindowHandle, Dsn) == FALSE) { /* If user cancels prompt, SQLDriverConnect should return SQL_NO_DATA */ Dbc->Error.ReturnValue= SQL_NO_DATA; goto error; } DSNPrompt_Free(&DSNPrompt); ret= MADB_DbcConnectDB(Dbc, Dsn); if (!SQL_SUCCEEDED(ret)) { goto error; } end: Dbc->Dsn= Dsn; /* Dialog returns bitmap - syncing corresponding properties */ MADB_DsnUpdateOptionsFields(Dsn); if (Dsn->isPrompt) { char *PreservePwd; /* DM should do that on its own, but we still better also remove pwd from the string being saved in the file DSN */ if (Dsn->SaveFile != NULL) { PreservePwd= Dsn->Password; Dsn->Password= NULL; } /* If prompt/complete(_required), and dialog was succusefully showed - we generate string from the result DSN */ Length= MADB_DsnToString(Dsn, (char *)OutConnectionString, BufferLength); if (Dsn->SaveFile != NULL) { Dsn->Password= PreservePwd; } } else { if (StringLength1 == SQL_NTS) { StringLength1= (SQLSMALLINT)strlen((const char*)InConnectionString); } if (OutConnectionString && BufferLength) { /* Otherwise we are supposed to simply copy incoming connection string */ strncpy_s((char *)OutConnectionString, BufferLength, (const char*)InConnectionString, StringLength1); } Length= StringLength1; } if (StringLength2Ptr) *StringLength2Ptr= (SQLSMALLINT)Length; if (OutConnectionString && BufferLength && Length > BufferLength) { MADB_SetError(&Dbc->Error, MADB_ERR_01004, NULL, 0); return Dbc->Error.ReturnValue; } return ret; error: DSNPrompt_Free(&DSNPrompt); MADB_DSN_Free(Dsn); MADB_DriverFree(Drv); return Dbc->Error.ReturnValue; } /* }}} */ SQLRETURN MADB_DbcTrackSession(MADB_Dbc* Dbc) { const char *key, *value; size_t keyLength, length; if (mysql_session_track_get_first(Dbc->mariadb, SESSION_TRACK_SCHEMA, &value, &length) == 0) { MADB_FREE(Dbc->CurrentSchema); Dbc->CurrentSchema= strndup(value, length); } if (mysql_session_track_get_first(Dbc->mariadb, SESSION_TRACK_SYSTEM_VARIABLES, &key, &keyLength) == 0) { do { mysql_session_track_get_next(Dbc->mariadb, SESSION_TRACK_SYSTEM_VARIABLES, &value, &length); if (strncmp(key, "autocommit", keyLength) == 0) { /* Seemingly it's ON or OFF, but checking also lowercase and '0' or '1' */ Dbc->AutoCommit= test(length > 1 && (value[1] == 'N' || value[1] == 'n') || length == 1 && *value == '1'); } else if (strncmp(key, MADB_GetTxIsolationVarName(Dbc), keyLength) == 0) { Dbc->TxnIsolation= TranslateTxIsolation(value, length); } } while (mysql_session_track_get_next(Dbc->mariadb, SESSION_TRACK_SYSTEM_VARIABLES, &key, &keyLength) == 0); } return SQL_SUCCESS; } SQLRETURN MADB_DbcDummyTrackSession(MADB_Dbc* Dbc) { return SQL_SUCCESS; } /* {{{ MADB_DbcGetTrackedTxIsolatin */ SQLRETURN MADB_DbcGetTrackedTxIsolatin(MADB_Dbc* Dbc, SQLINTEGER* txIsolation) { MADB_CLEAR_ERROR(&Dbc->Error); *txIsolation= Dbc->TxnIsolation; return Dbc->Error.ReturnValue; } /* {{{ MADB_DbcGetServerTxIsolation */ SQLRETURN MADB_DbcGetServerTxIsolation(MADB_Dbc* Dbc, SQLINTEGER* txIsolation) { MYSQL_RES* result; MYSQL_ROW row; const char* StmtString = MADB_GetTxIsolationQuery(Dbc); LOCK_MARIADB(Dbc); if (mysql_real_query(Dbc->mariadb, StmtString, 21)) { UNLOCK_MARIADB(Dbc); return MADB_SetNativeError(&Dbc->Error, SQL_HANDLE_DBC, Dbc->mariadb); } result = mysql_store_result(Dbc->mariadb); UNLOCK_MARIADB(Dbc); if ( result != NULL && (row = mysql_fetch_row(result))) { *txIsolation= Dbc->TxnIsolation= TranslateTxIsolation(row[0], strlen(row[0])); mysql_free_result(result); } else { return MADB_SetNativeError(&Dbc->Error, SQL_HANDLE_DBC, Dbc->mariadb); } return SQL_SUCCESS; } struct st_ma_connection_methods MADB_Dbc_Methods = { MADB_DbcSetAttr, MADB_DbcGetAttr, MADB_DbcConnectDB, MADB_DbcEndTran, MADB_DbcGetFunctions, MADB_DbcGetInfo, MADB_DriverConnect, MADB_DbcGetTrackedCurrentDB, MADB_DbcTrackSession, MADB_DbcGetTrackedTxIsolatin }; mariadb-connector-odbc-3.1.15/ma_connection.h000066400000000000000000000077431414431724000210750ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_connection_h_ #define _ma_connection_h_ #define MADB_CONN_OPT_NOT_SUPPORTED 0 #define MADB_CONN_OPT_BEFORE 1 #define MADB_CONN_OPT_AFTER 2 #define MADB_CONN_OPT_BOTH 3 /* sql_mode's identifiers */ enum enum_madb_sql_mode {MADB_NO_BACKSLASH_ESCAPES, MADB_ANSI_QUOTES }; struct st_ma_connection_methods; struct st_madb_isolation { long SqlIsolation; const char *StrIsolation; const char* TrackStr; /* String coming with session tracking */ }; struct st_ma_connection_methods { SQLRETURN (*SetAttr)(MADB_Dbc *Dbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength, my_bool isWChar); SQLRETURN (*GetAttr)(MADB_Dbc *Dbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr, my_bool isWChar); SQLRETURN (*ConnectDB)(MADB_Dbc *Connection, MADB_Dsn *Dsn); SQLRETURN (*EndTran)(MADB_Dbc *Dbc, SQLSMALLINT CompletionType); SQLRETURN (*GetFunctions)(MADB_Dbc *Dbc, SQLUSMALLINT FunctionId, SQLUSMALLINT *SupportedPtr); SQLRETURN (*GetInfo)(MADB_Dbc *Dnc, SQLUSMALLINT InfoType, SQLPOINTER InfoValuePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, my_bool isWChar); SQLRETURN (*DriverConnect)(MADB_Dbc *Dbc, SQLHWND WindowHandle, SQLCHAR *InConnectionString, SQLULEN StringLength1, SQLCHAR *OutConnectionString, SQLULEN BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion); SQLRETURN (*GetCurrentDB)(MADB_Dbc* Connection, SQLPOINTER CurrentDB, SQLINTEGER CurrentDBLength, SQLSMALLINT* StringLengthPtr, my_bool isWChar); SQLRETURN (*TrackSession)(MADB_Dbc* Connection); SQLRETURN (*GetTxIsolation)(MADB_Dbc* Connection, SQLINTEGER* txIsolation); }; my_bool CheckConnection(MADB_Dbc *Dbc); SQLRETURN MADB_DbcFree(MADB_Dbc *Connection); MADB_Dbc * MADB_DbcInit(MADB_Env *Env); BOOL MADB_SqlMode(MADB_Dbc *Connection, enum enum_madb_sql_mode SqlMode); /* Has platform versions */ char* MADB_GetDefaultPluginsDir(char* Buffer, size_t Size); #define MADB_SUPPORTED_CONVERSIONS SQL_CVT_BIGINT | SQL_CVT_BIT | SQL_CVT_CHAR | SQL_CVT_DATE |\ SQL_CVT_DECIMAL | SQL_CVT_DOUBLE | SQL_CVT_FLOAT |\ SQL_CVT_INTEGER | SQL_CVT_LONGVARCHAR | SQL_CVT_NUMERIC |\ SQL_CVT_REAL | SQL_CVT_SMALLINT | SQL_CVT_TIME | SQL_CVT_TIMESTAMP |\ SQL_CVT_TINYINT | SQL_CVT_VARCHAR | SQL_CVT_WCHAR | \ SQL_CVT_WLONGVARCHAR | SQL_CVT_WVARCHAR /**************** Helper macros ****************/ /* check if the connection is established */ #define MADB_Dbc_ACTIVE(a) \ ((a)->mariadb && mysql_get_socket((a)->mariadb) != MARIADB_INVALID_SOCKET) #define MADB_Dbc_DSN(a) \ (a) && (a)->Dsn #define MADB_CONNECTED(DbConnHandler) (DbConnHandler->mariadb && mysql_get_socket(DbConnHandler->mariadb) != MARIADB_INVALID_SOCKET) #endif /* _ma_connection_h */ mariadb-connector-odbc-3.1.15/ma_conv_charset.c000066400000000000000000000077251414431724000214070ustar00rootroot00000000000000/**************************************************************************** Copyright (C) 2012, 2020, MariaDB Corporation. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA Part of this code includes code from the PHP project which is freely available from http://www.php.net *****************************************************************************/ #ifndef _WIN32 #include #include #include #include #else #include #endif #include #if defined(SOLARIS) || defined(__sun) #define IF_SOLARIS(A,B) A #else #define IF_SOLARIS(A,B) B #endif #define HAVE_ICONV #ifdef HAVE_ICONV /* {{{ MADB_MapCharsetName Changing charset name into something iconv understands, if necessary. Another purpose it to avoid BOMs in result string, adding BE if necessary e.g.UTF16 does not work form iconv, while UTF-16 does. */ static void MADB_MapCharsetName(const char *cs_name, my_bool target_cs, char *buffer, size_t buff_len) { char digits[3], endianness[3]= "BE"; if (sscanf(cs_name, "UTF%2[0-9]%2[LBE]", digits, endianness)) { /* We should have at least digits. Endianness we write either default(BE), or what we found in the string */ snprintf(buffer, buff_len, "UTF-%s%s", digits, endianness); } else { /* Not our client - copy as is*/ strncpy(buffer, cs_name, buff_len - 1); buffer[buff_len - 1]= '\0'; } if (target_cs) { strncat(buffer, "//TRANSLIT", buff_len - strlen(buffer)); } } /* }}} */ #endif /* {{{ MADB_ConvertString Converts string from one charset to another, and writes converted string to given buffer @param[in] from @param[in/out] from_len @param[in] from_cs @param[out] to @param[in/out] to_len @param[in] to_cs @param[out] errorcode @return -1 in case of error, bytes used in the "to" buffer, otherwise */ size_t STDCALL MADB_ConvertString(const char *from __attribute__((unused)), size_t *from_len __attribute__((unused)), MARIADB_CHARSET_INFO *from_cs __attribute__((unused)), char *to __attribute__((unused)), size_t *to_len __attribute__((unused)), MARIADB_CHARSET_INFO *to_cs __attribute__((unused)), int *errorcode) { #ifndef HAVE_ICONV *errorcode= ENOTSUP; return -1; #else iconv_t conv= 0; size_t rc= -1; size_t save_len= *to_len; char to_encoding[128], from_encoding[128]; *errorcode= 0; /* check if conversion is supported */ if (!from_cs || !from_cs->encoding || !from_cs->encoding[0] || !to_cs || !to_cs->encoding || !to_cs->encoding[0]) { *errorcode= EINVAL; return rc; } MADB_MapCharsetName(to_cs->encoding, 1, to_encoding, sizeof(to_encoding)); MADB_MapCharsetName(from_cs->encoding, 0, from_encoding, sizeof(from_encoding)); if ((conv= iconv_open(to_encoding, from_encoding)) == (iconv_t)-1) { *errorcode= errno; goto error; } if ((rc= iconv(conv, IF_SOLARIS(,(char **))&from, from_len, &to, to_len)) == (size_t)-1) { *errorcode= errno; goto error; } rc= save_len - *to_len; error: if (conv != (iconv_t)-1) iconv_close(conv); return rc; #endif } /* }}} */ mariadb-connector-odbc-3.1.15/ma_conv_charset.h000066400000000000000000000031341414431724000214020ustar00rootroot00000000000000/**************************************************************************** Copyright (C) 2012, 2020, MariaDB Corporation. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA Part of this code includes code from the PHP project which is freely available from http://www.php.net *****************************************************************************/ #ifndef _MA_CONV_CHARSET_H_ #define _MA_CONV_CHARSET_H_ #include "mariadb_ctype.h" size_t MADB_ConvertString(const char *from __attribute__((unused)), size_t *from_len __attribute__((unused)), MARIADB_CHARSET_INFO *from_cs __attribute__((unused)), char *to __attribute__((unused)), size_t *to_len __attribute__((unused)), MARIADB_CHARSET_INFO *to_cs __attribute__((unused)), int *errorcode); #endif mariadb-connector-odbc-3.1.15/ma_debug.c000066400000000000000000000107571414431724000200160ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2015 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #include #ifdef MAODBC_DEBUG extern char LogFile[]; void ma_debug_print(my_bool ident, char *format, ...) { FILE *fp= fopen(LogFile, "a"); if (fp) { va_list va; va_start(va, format); if (ident) fprintf(fp, "\t"); vfprintf(fp, format, va); fprintf(fp, "\n"); va_end(va); fclose(fp); } } void ma_debug_printw(wchar_t *format, ...) { FILE *fp= fopen(LogFile, "a"); if (fp) { va_list va; va_start(va, format); fwprintf(fp, format, va); fwprintf(fp, L"\n"); va_end(va); fclose(fp); } } void ma_debug_printv(char *format, va_list args) { FILE *fp= fopen(LogFile, "a"); if (fp) { vfprintf(fp, format, args); fclose(fp); } } void ma_debug_print_error(MADB_Error *err) { /*TODO: Make it without #ifdefs */ #ifdef _WIN32 SYSTEMTIME st; GetSystemTime(&st); ma_debug_print(1, "%d-%02d-%02d %02d:%02d:%02d [%s](%u)%s", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, err->SqlState, err->NativeError, err->SqlErrorMsg); #else time_t t = time(NULL);\ struct tm st = *gmtime(&t);\ ma_debug_print(1, "%d-%02d-%02d %02d:%02d:%02d [%s](%u)%s", st.tm_year + 1900, st.tm_mon + 1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, err->SqlState, err->NativeError, err->SqlErrorMsg); #endif } void ma_print_value(SQLSMALLINT OdbcType, SQLPOINTER Value, SQLLEN octets) { if (Value == 0) { ma_debug_print(1, "NULL ptr"); } if (octets <= 0) { octets= 1; } switch (OdbcType) { case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: ma_debug_print(1, "%d", 0 + *((char*)Value)); break; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: ma_debug_print(1, "%d", 0 + *((short int*)Value)); break; case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: ma_debug_print(1, "%d", 0 + *((int*)Value)); break; case SQL_C_UBIGINT: case SQL_C_SBIGINT: ma_debug_print(1, "%ll", 0 + *((long long*)Value)); break; case SQL_C_DOUBLE: ma_debug_print(1, "%f", 0.0 + *((SQLDOUBLE*)Value)); break; case SQL_C_FLOAT: ma_debug_print(1, "%f", 0.0 + *((SQLFLOAT*)Value)); break; case SQL_C_NUMERIC: ma_debug_print(1, "%s", "[numeric struct]"); break; case SQL_C_TYPE_TIME: case SQL_C_TIME: ma_debug_print(1, "%02d:02d:02d", ((SQL_TIME_STRUCT*)Value)->hour, ((SQL_TIME_STRUCT*)Value)->minute, ((SQL_TIME_STRUCT*)Value)->second); break; case SQL_C_TYPE_DATE: case SQL_C_DATE: ma_debug_print(1, "%4d-02d-02d", ((SQL_DATE_STRUCT*)Value)->year, ((SQL_DATE_STRUCT*)Value)->month, ((SQL_DATE_STRUCT*)Value)->day); break; case SQL_C_TYPE_TIMESTAMP: case SQL_C_TIMESTAMP: ma_debug_print(1, "%4d-02d-02d %02d:02d:02d", ((SQL_TIMESTAMP_STRUCT*)Value)->year, ((SQL_TIMESTAMP_STRUCT*)Value)->month, ((SQL_TIMESTAMP_STRUCT*)Value)->day, ((SQL_TIMESTAMP_STRUCT*)Value)->hour, ((SQL_TIMESTAMP_STRUCT*)Value)->minute, ((SQL_TIMESTAMP_STRUCT*)Value)->second); break; case SQL_C_CHAR: ma_debug_print(1, "%*s%s", MIN(10, octets), (char*)Value, octets > 10 ? "..." : ""); break; default: ma_debug_print(1, "%*X%s", MIN(10, octets), (char*)Value, octets > 10 ? "..." : ""); break; } } /* #ifdef __APPLE__ void TravisTrace(char *format, va_list args) { BOOL Travis= FALSE; Travis= getenv("TRAVIS") != NULL; if (Travis != FALSE) { printf("#"); vprintf(format, args); } } #endif */ #endif mariadb-connector-odbc-3.1.15/ma_debug.h000066400000000000000000000072361414431724000200210ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2015 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_debug_h_ #define _ma_debug_h_ #ifndef MAODBC_DEBUG void ma_debug_print(my_bool ident, char *format, ...); #define MDBUG_C_IS_ON(C) (0) #define MDBUG_C_ENTER(C,A) {} #define MDBUG_C_RETURN(C,A,E) return (A) #define MDBUG_C_PRINT(C, format, args) {} #define MDBUG_C_VOID_RETURN(C) {} #define MDBUG_C_DUMP(C,A,B) {} #else #define MA_DEBUG_FLAG 4 #ifndef WIN32 #include #endif void ma_debug_print(my_bool ident, char *format, ...); void ma_debug_print_error(MADB_Error *err); /* Debug is on for connection */ #define MDBUG_C_IS_ON(C) ((C) && (((MADB_Dbc*)(C))->Options & MA_DEBUG_FLAG)) #ifdef WIN32 #define MDBUG_C_ENTER(C,A)\ if (MDBUG_C_IS_ON(C))\ {\ SYSTEMTIME st;\ GetSystemTime(&st);\ ma_debug_print(0, ">>> %d-%02d-%02d %02d:%02d:%02d --- %s (thread: %lu) ---", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, A, ((MADB_Dbc*)(C))->mariadb ? mysql_thread_id(((MADB_Dbc*)(C))->mariadb) : 0);\ } #else #define MDBUG_C_ENTER(C,A)\ if ((C) && (((MADB_Dbc*)(C))->Options & MA_DEBUG_FLAG))\ {\ time_t t = time(NULL);\ struct tm st= *gmtime(&t);\ ma_debug_print(0, ">>> %d-%02d-%02d %02d:%02d:%02d --- %s (thread: %d) ---", st.tm_year + 1900, st.tm_mon + 1, st.tm_mday, st.tm_hour, st.tm_min, st.tm_sec, A, ((MADB_Dbc*)(C))->mariadb ? mysql_thread_id(((MADB_Dbc*)(C))->mariadb) : 0);\ } #endif #define MDBUG_C_RETURN(C,A,E)\ if (MDBUG_C_IS_ON(C))\ {\ SQLRETURN _ret= (A);\ if (_ret && (E)->ReturnValue)\ ma_debug_print_error(E); \ ma_debug_print(0, "<<< --- end of function, returning %d ---", _ret); \ return _ret;\ }\ return (A); #define MDBUG_C_PRINT(C, format, ...)\ if (MDBUG_C_IS_ON(C))\ ma_debug_print(1, format, __VA_ARGS__); #define MDBUG_C_VOID_RETURN(C)\ if (MDBUG_C_IS_ON(C))\ ma_debug_print(0, "<<< --- end of function ---");\ return; #define MDBUG_C_DUMP(C,A,B)\ if (MDBUG_C_IS_ON(C))\ ma_debug_print(1, #A ":\t%" #B, A); #endif /* MAODBC_DEBUG */ /* These macros will be used to force debug output for functions without a DBC handle */ #ifndef MA_ODBC_DEBUG_ALL #define MDBUG_ENTER(A) {} #define MDBUG_RETURN(A) return (A) #define MDBUG_PRINT(format, args) {} #define MDBUG_VOID_RETURN() {} #define MDBUG_DUMP(B,C) {} #else #define MDBUG_ENTER(A)\ ma_debug_print(0, ">>> --- %s ---", A); #define MDBUG_RETURN(A)\ ma_debug_print(0, "<<< .. end of function ---");\ return (A); #define MDBUG_PRINT(format, ...)\ ma_debug_print(1, format, __VA_ARGS__); #define MDBUG_VOID_RETURN()\ ma_debug_print(0, "<<< --- end of function ---");\ return; #define MDBUG_DUMP(A,B)\ ma_debug_print(1, #A ":\t%" #B, A); #endif /* MA_ODBC_DEBUG_ALL */ #endif /* _ma_debug_h_ */ mariadb-connector-odbc-3.1.15/ma_desc.c000066400000000000000000001044371414431724000176450ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include extern Client_Charset utf8; /* To keep the source code smaller we use the following structure to check if a field identifier is valid for a given descriptor type */ struct st_ma_desc_fldid MADB_DESC_FLDID[]= { {SQL_DESC_ALLOC_TYPE, {MADB_DESC_READ, MADB_DESC_READ, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_ARRAY_SIZE, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_ARRAY_STATUS_PTR, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW}}, {SQL_DESC_BIND_OFFSET_PTR, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_BIND_TYPE, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_COUNT, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_ROWS_PROCESSED_PTR, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_RW, MADB_DESC_RW}}, {SQL_DESC_AUTO_UNIQUE_VALUE, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_BASE_COLUMN_NAME, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_BASE_TABLE_NAME, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_CASE_SENSITIVE, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_CATALOG_NAME, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_CONCISE_TYPE, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW}}, {SQL_DESC_DATA_PTR, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_DATETIME_INTERVAL_CODE, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_DATETIME_INTERVAL_PRECISION, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_DISPLAY_SIZE, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_FIXED_PREC_SCALE, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_RW, MADB_DESC_RW}}, {SQL_DESC_INDICATOR_PTR, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_LABEL, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_LENGTH, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_LITERAL_PREFIX, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_LITERAL_SUFFIX, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_LOCAL_TYPE_NAME, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ, MADB_DESC_READ}}, {SQL_DESC_NAME, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_NULLABLE, {MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ, MADB_DESC_READ}}, {SQL_DESC_NUM_PREC_RADIX, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_OCTET_LENGTH, {MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_OCTET_LENGTH_PTR,{MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_NONE, MADB_DESC_NONE}}, {SQL_DESC_PARAMETER_TYPE,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_RW, MADB_DESC_NONE}}, {SQL_DESC_PRECISION,{MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, #if (ODBCVER >= 0x0350) {SQL_DESC_ROWVER,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ, MADB_DESC_READ}}, #endif {SQL_DESC_SCALE,{MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_SCHEMA_NAME,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_SEARCHABLE,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_TABLE_NAME,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_TYPE,{MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_RW, MADB_DESC_READ}}, {SQL_DESC_TYPE_NAME ,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ, MADB_DESC_READ}}, {SQL_DESC_UNSIGNED ,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ, MADB_DESC_READ}}, {SQL_DESC_UPDATABLE ,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_READ}}, {SQL_DESC_UNNAMED ,{MADB_DESC_NONE, MADB_DESC_NONE, MADB_DESC_RW, MADB_DESC_READ}}, {0, {0, 0, 0, 0}} }; /* {{{ MADB_DescInit */ MADB_Desc *MADB_DescInit(MADB_Dbc *Dbc,enum enum_madb_desc_type DescType, my_bool isExternal) { MADB_Desc *Desc; if (!(Desc= (MADB_Desc *)MADB_CALLOC(sizeof(MADB_Desc)))) return NULL; Desc->DescType= DescType; MADB_PutErrorPrefix(Dbc, &Desc->Error); if (MADB_InitDynamicArray(&Desc->Records, sizeof(MADB_DescRecord), 0, MADB_DESC_INIT_REC_NUM)) { MADB_FREE(Desc); return NULL; } if (isExternal) { if (MADB_InitDynamicArray(&Desc->Stmts, sizeof(MADB_Stmt**), 0, MADB_DESC_INIT_STMT_NUM)) { MADB_DescFree(Desc, FALSE); return NULL; } else { Desc->Dbc= Dbc; /* MADB_DescInit call for explicit descriptor is in critical section */ Desc->ListItem.data= (void *)Desc; Dbc->Descrs= MADB_ListAdd(Dbc->Descrs, &Desc->ListItem); } } Desc->AppType= isExternal; Desc->Header.ArraySize= 1; return Desc; } /* }}} */ /* {{{ MADB_DescFree */ SQLRETURN MADB_DescFree(MADB_Desc *Desc, my_bool RecordsOnly) { MADB_DescRecord *Record; unsigned int i; if (!Desc) return SQL_ERROR; /* We need to free internal pointers first */ for (i=0; i < Desc->Records.elements; i++) { Record= ((MADB_DescRecord *)Desc->Records.buffer) + i; MADB_FREE(Record->InternalBuffer); MADB_FREE(Record->DefaultValue); if (Desc->DescType == MADB_DESC_IRD) { MADB_FREE(Record->CatalogName); MADB_FREE(Record->BaseCatalogName); MADB_FREE(Record->BaseColumnName); MADB_FREE(Record->BaseTableName); MADB_FREE(Record->ColumnName); MADB_FREE(Record->TableName); MADB_FREE(Record->TypeName); } else if(Desc->DescType == MADB_DESC_IPD) { MADB_FREE(Record->TypeName); } } MADB_DeleteDynamic(&Desc->Records); Desc->Header.Count= 0; if (Desc->AppType) { EnterCriticalSection(&Desc->Dbc->ListsCs); for (i=0; i < Desc->Stmts.elements; i++) { MADB_Stmt **XStmt= ((MADB_Stmt **)Desc->Stmts.buffer) + i; MADB_Stmt *Stmt= *XStmt; switch(Desc->DescType) { case MADB_DESC_ARD: Stmt->Ard=Stmt->IArd; break; case MADB_DESC_APD: Stmt->Apd= Stmt->IApd; break; } } MADB_DeleteDynamic(&Desc->Stmts); Desc->Dbc->Descrs= MADB_ListDelete(Desc->Dbc->Descrs, &Desc->ListItem); LeaveCriticalSection(&Desc->Dbc->ListsCs); } if (!RecordsOnly) MADB_FREE(Desc); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_SetIrdRecord */ my_bool MADB_SetIrdRecord(MADB_Stmt *Stmt, MADB_DescRecord *Record, MYSQL_FIELD *Field) { MY_CHARSET_INFO cs; MARIADB_CHARSET_INFO *FieldCs; if (Record == NULL) { return 1; } mariadb_get_infov(Stmt->Connection->mariadb, MARIADB_CONNECTION_MARIADB_CHARSET_INFO, (void*)&cs); MADB_RESET(Record->CatalogName, Field->db); MADB_RESET(Record->TableName, Field->table); MADB_RESET(Record->ColumnName, Field->name); MADB_RESET(Record->BaseTableName, Field->org_table); MADB_RESET(Record->BaseColumnName, Field->org_name); Record->AutoUniqueValue= (Field->flags & AUTO_INCREMENT_FLAG) ? SQL_TRUE : SQL_FALSE; Record->CaseSensitive= (Field->flags & BINARY_FLAG) ? SQL_TRUE : SQL_FALSE; Record->Nullable= ( (Field->flags & NOT_NULL_FLAG) && !Record->AutoUniqueValue && Field->type != MYSQL_TYPE_TIMESTAMP) ? SQL_NO_NULLS : SQL_NULLABLE; Record->Unsigned= (Field->flags & UNSIGNED_FLAG) ? SQL_TRUE : SQL_FALSE; /* We assume it might be updatable if tablename exists */ Record->Updateable= (Field->table && Field->table[0]) ? SQL_ATTR_READWRITE_UNKNOWN : SQL_ATTR_READONLY; /* RADIX: If the data type in the SQL_DESC_TYPE field is an approximate numeric data type, this SQLINTEGER field contains a value of 2 because the SQL_DESC_PRECISION field contains the number of bits. If the data type in the SQL_DESC_TYPE field is an exact numeric data type, this field contains a value of 10 because the SQL_DESC_PRECISION field contains the number of decimal digits. This field is set to 0 for all non-numeric data types. */ switch (Field->type) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: Record->NumPrecRadix= 10; Record->Scale= Field->decimals; Record->Precision= (SQLSMALLINT)Field->length - test(Record->Unsigned == SQL_FALSE) - test(Record->Scale > 0); if (Record->Precision == 0) { Record->Precision= Record->Scale; } break; case MYSQL_TYPE_FLOAT: Record->NumPrecRadix= 2; Record->Precision= (SQLSMALLINT)Field->length - 2; //Record->Scale= Field->decimals; break; case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_YEAR: Record->NumPrecRadix= 10; break; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: Record->Scale= Field->decimals; default: Record->NumPrecRadix= 0; break; } Record->ConciseType= MapMariadDbToOdbcType(Field); /* TYPE: For the datetime and interval data types, this field returns the verbose data type: SQL_DATETIME or SQL_INTERVAL. */ Record->Type= (Record->ConciseType == SQL_TYPE_DATE || Record->ConciseType == SQL_DATE || Record->ConciseType == SQL_TYPE_TIME || Record->ConciseType == SQL_TIME || Record->ConciseType == SQL_TYPE_TIMESTAMP || Record->ConciseType == SQL_TIMESTAMP) ? SQL_DATETIME : Record->ConciseType; switch(Record->ConciseType) { case SQL_TYPE_DATE: Record->DateTimeIntervalCode= SQL_CODE_DATE; break; case SQL_TYPE_TIME: Record->DateTimeIntervalCode= SQL_CODE_TIME; break; case SQL_TYPE_TIMESTAMP: Record->DateTimeIntervalCode= SQL_CODE_TIMESTAMP; break; } /* SEARCHABLE: Columns of type SQL_LONGVARCHAR and SQL_LONGVARBINARY usually return SQL_PRED_CHAR. */ Record->Searchable= (Record->ConciseType == SQL_LONGVARCHAR || Record->ConciseType == SQL_WLONGVARCHAR || Record->ConciseType == SQL_LONGVARBINARY) ? SQL_PRED_CHAR : SQL_SEARCHABLE; Record->DisplaySize= MADB_GetDisplaySize(Field, mariadb_get_charset_by_nr(Field->charsetnr)); Record->OctetLength= MADB_GetOctetLength(Field, cs.mbmaxlen); FieldCs= mariadb_get_charset_by_nr(Field->charsetnr); Record->Length= MADB_GetDataSize(Record->ConciseType, Field->length, Record->Unsigned == SQL_TRUE, Record->Precision, Record->Scale, FieldCs!= NULL ? FieldCs->char_maxlen : 1); MADB_RESET(Record->TypeName, MADB_GetTypeName(Field)); switch(Field->type) { case MYSQL_TYPE_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_VAR_STRING: if (Field->flags & BINARY_FLAG) { Record->LiteralPrefix= "0x"; Record->LiteralSuffix= ""; break; } /* else default */ case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_YEAR: Record->LiteralPrefix="'"; Record->LiteralSuffix= "'"; break; default: Record->LiteralPrefix= ""; Record->LiteralSuffix= ""; break; } return 0; } /* }}} */ /* {{{ MADB_DescSetIrdMetadata */ my_bool MADB_DescSetIrdMetadata(MADB_Stmt *Stmt, MYSQL_FIELD *Fields, unsigned int NumFields) { SQLSMALLINT i; /* Perhaps we should call routine that does SQL_CLOSE here */ Stmt->Ird->Header.Count= 0; for (i= 0; i < (SQLSMALLINT)NumFields; i++) { if (MADB_SetIrdRecord(Stmt, MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_WRITE), &Fields[i])) { return 1; } } return 0; } /* }}} */ /* {{{ MADB_FixOctetLength */ void MADB_FixOctetLength(MADB_DescRecord *Record) { switch (Record->ConciseType) { case SQL_BIT: case SQL_TINYINT: Record->OctetLength= 1; break; case SQL_SMALLINT: Record->OctetLength= 2; break; case SQL_INTEGER: case SQL_REAL: Record->OctetLength= 4; break; case SQL_BIGINT: case SQL_DOUBLE: Record->OctetLength= 8; break; case SQL_TYPE_DATE: Record->OctetLength= sizeof(SQL_DATE_STRUCT); break; case SQL_TYPE_TIME: Record->OctetLength= sizeof(SQL_TIME_STRUCT); break; case SQL_TYPE_TIMESTAMP: Record->OctetLength= sizeof(SQL_TIMESTAMP_STRUCT); break; default: Record->OctetLength= MIN(MADB_INT_MAX32, Record->OctetLength); } } /* }}} */ /* {{{ MADB_FixDisplayLength */ void MADB_FixDisplaySize(MADB_DescRecord *Record, const MY_CHARSET_INFO *charset) { switch (Record->ConciseType) { case SQL_BIT: Record->DisplaySize= 1; break; case SQL_TINYINT: Record->DisplaySize= 4 - test(Record->Unsigned == SQL_TRUE); break; case SQL_SMALLINT: Record->DisplaySize= 6 - test(Record->Unsigned == SQL_TRUE); break; case SQL_INTEGER: Record->DisplaySize= 11 - test(Record->Unsigned == SQL_TRUE); break; case SQL_REAL: Record->DisplaySize= 14; break; case SQL_BIGINT: Record->DisplaySize= 20; break; case SQL_FLOAT: case SQL_DOUBLE: Record->DisplaySize= 24; break; case SQL_DECIMAL: case SQL_NUMERIC: Record->DisplaySize= Record->Precision + 2; break; case SQL_TYPE_DATE: Record->DisplaySize= SQL_DATE_LEN; break; case SQL_TYPE_TIME: Record->DisplaySize= SQL_TIME_LEN + MADB_FRACTIONAL_PART(Record->Scale); break; case SQL_TYPE_TIMESTAMP: Record->DisplaySize= SQL_TIMESTAMP_LEN + MADB_FRACTIONAL_PART(Record->Scale); break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: Record->DisplaySize=Record->OctetLength*2; /* For display in hex */ break; case SQL_GUID: Record->DisplaySize= 36; break; default: if (charset == NULL || charset->mbmaxlen < 2/*i.e.0||1*/) { Record->DisplaySize=Record->OctetLength; } else { Record->DisplaySize=Record->OctetLength/charset->mbmaxlen; } } } /* }}} */ /* {{{ MADB_FixDataSize - aka Column size */ void MADB_FixDataSize(MADB_DescRecord *Record, const MY_CHARSET_INFO *charset) { Record->Length= MADB_GetDataSize(Record->ConciseType, Record->OctetLength, Record->Unsigned == TRUE, Record->Precision, Record->Scale, charset->mbmaxlen ); } /* }}} */ /* {{{ MADB_FixIrdRecord */ my_bool MADB_FixIrdRecord(MADB_Stmt *Stmt, MADB_DescRecord *Record) { MY_CHARSET_INFO cs; if (Record == NULL) { return 1; } MADB_FixOctetLength(Record); /* RADIX: If the data type in the SQL_DESC_TYPE field is an approximate numeric data type, this SQLINTEGER field contains a value of 2 because the SQL_DESC_PRECISION field contains the number of bits. If the data type in the SQL_DESC_TYPE field is an exact numeric data type, this field contains a value of 10 because the SQL_DESC_PRECISION field contains the number of decimal digits. This field is set to 0 for all non-numeric data types. */ switch (Record->ConciseType) { case SQL_DECIMAL: Record->NumPrecRadix= 10; Record->Precision= (SQLSMALLINT)Record->OctetLength - 2; /*Record->Scale= Fields[i].decimals;*/ break; case SQL_REAL: /* Float*/ Record->NumPrecRadix= 2; Record->Precision= (SQLSMALLINT)Record->OctetLength - 2; break; case SQL_DOUBLE: case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIGINT: Record->NumPrecRadix= 10; break; default: Record->NumPrecRadix= 0; break; } /* TYPE: For the datetime and interval data types, this field returns the verbose data type: SQL_DATETIME or SQL_INTERVAL. */ Record->Type= (Record->ConciseType == SQL_TYPE_DATE || Record->ConciseType == SQL_DATE || Record->ConciseType == SQL_TYPE_TIME || Record->ConciseType == SQL_TIME || Record->ConciseType == SQL_TYPE_TIMESTAMP || Record->ConciseType == SQL_TIMESTAMP) ? SQL_DATETIME : Record->ConciseType; switch(Record->ConciseType) { case SQL_TYPE_DATE: Record->DateTimeIntervalCode= SQL_CODE_DATE; break; case SQL_TYPE_TIME: Record->DateTimeIntervalCode= SQL_CODE_TIME; break; case SQL_TYPE_TIMESTAMP: Record->DateTimeIntervalCode= SQL_CODE_TIMESTAMP; break; } /* SEARCHABLE: Columns of type SQL_LONGVARCHAR and SQL_LONGVARBINARY usually return SQL_PRED_CHAR. */ Record->Searchable= (Record->ConciseType == SQL_LONGVARCHAR || Record->ConciseType == SQL_WLONGVARCHAR || Record->ConciseType == SQL_LONGVARBINARY) ? SQL_PRED_CHAR : SQL_SEARCHABLE; mariadb_get_infov(Stmt->Connection->mariadb, MARIADB_CONNECTION_MARIADB_CHARSET_INFO, (void*)&cs); MADB_FixDisplaySize(Record, &cs); MADB_FixDataSize(Record, &cs); switch(Record->ConciseType) { case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: Record->LiteralPrefix= "0x"; Record->LiteralSuffix= ""; break; /* else default */ case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: Record->LiteralPrefix="'"; Record->LiteralSuffix= "'"; break; default: Record->LiteralPrefix= ""; Record->LiteralSuffix= ""; break; } return 0; } /* }}} */ /* {{{ MADB_FixColumnDataTypes */ my_bool MADB_FixColumnDataTypes(MADB_Stmt *Stmt, MADB_ShortTypeInfo *ColTypesArr) { SQLSMALLINT i; MADB_DescRecord *Record= NULL; if (ColTypesArr == NULL) { return 1; } for (i=0; i < Stmt->Ird->Header.Count; ++i) { if (ColTypesArr[i].SqlType != 0) { Record= MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_READ); if (Record == NULL) { return 1; } Record->ConciseType= ColTypesArr[i].SqlType; Record->Nullable= ColTypesArr[i].Nullable; Record->Unsigned= ColTypesArr[i].Unsigned != 0 ? SQL_TRUE : SQL_FALSE; if (ColTypesArr[i].OctetLength > 0) { Record->OctetLength= ColTypesArr[i].OctetLength; } if (MADB_FixIrdRecord(Stmt, Record)) { return 1; } } } /* If the stmt is re-executed, we should be able to fix columns again */ Stmt->ColsTypeFixArr= ColTypesArr; return 0; } /* }}} */ void MADB_DescSetRecordDefaults(MADB_Desc *Desc, MADB_DescRecord *Record) { memset(Record, 0, sizeof(MADB_DescRecord)); switch (Desc->DescType) { case MADB_DESC_APD: Record->ConciseType= Record->Type= SQL_C_DEFAULT; break; case MADB_DESC_ARD: Record->ConciseType= Record->Type= SQL_C_DEFAULT; break; case MADB_DESC_IPD: Record->FixedPrecScale= SQL_FALSE; Record->LocalTypeName= ""; Record->Nullable= SQL_NULLABLE; Record->ParameterType= SQL_PARAM_INPUT; MADB_RESET(Record->TypeName, "VARCHAR"); Record->Unsigned= SQL_FALSE; Record->ColumnName= ""; break; case MADB_DESC_IRD: Record->Nullable= SQL_NULLABLE_UNKNOWN; Record->FixedPrecScale= SQL_FALSE; Record->CaseSensitive= SQL_TRUE; Record->ConciseType= SQL_VARCHAR; Record->AutoUniqueValue= SQL_FALSE; Record->Type= SQL_VARCHAR; MADB_RESET(Record->TypeName, "VARCHAR"); Record->Unsigned= SQL_FALSE; break; } } /* }}} */ /* {{{ MADB_DescGetInternalRecord */ MADB_DescRecord *MADB_DescGetInternalRecord(MADB_Desc *Desc, SQLSMALLINT RecordNumber, SQLSMALLINT Type) { MADB_DescRecord *DescRecord; if (RecordNumber > (SQLINTEGER)Desc->Records.elements && Type == MADB_DESC_READ) { MADB_SetError(&Desc->Error, MADB_ERR_07009, NULL, 0); return NULL; } while (RecordNumber >= (SQLINTEGER)Desc->Records.elements) { if (!(DescRecord= (MADB_DescRecord *)MADB_AllocDynamic(&Desc->Records))) { MADB_SetError(&Desc->Error, MADB_ERR_HY001, NULL, 0); return NULL; } MADB_DescSetRecordDefaults(Desc, DescRecord); } if (RecordNumber + 1 > Desc->Header.Count) Desc->Header.Count= (SQLSMALLINT)(RecordNumber + 1); DescRecord= ((MADB_DescRecord *)Desc->Records.buffer) + RecordNumber; return DescRecord; } /* }}} */ /* {{{ MADB_DescCheckFldId */ SQLRETURN MADB_DeskCheckFldId(MADB_Desc *Desc, SQLSMALLINT FieldIdentifier, SQLSMALLINT mode) { int i= 0; while (MADB_DESC_FLDID[i].FieldIdentifier && MADB_DESC_FLDID[i].FieldIdentifier != FieldIdentifier) ++i; /* End of list = invalid FieldIdentifier */ if (!MADB_DESC_FLDID[i].FieldIdentifier || !(MADB_DESC_FLDID[i].Access[Desc->DescType] & mode)) { MADB_SetError(&Desc->Error, MADB_ERR_HY091, NULL, 0); return SQL_ERROR; } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_DescGetField */ SQLRETURN MADB_DescGetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr, my_bool isWChar) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_DescRecord *DescRecord= NULL; SQLRETURN ret; size_t Length; /* Bookmark */ if (RecNumber < 1) { /* todo */ } ret= MADB_DeskCheckFldId(Desc, FieldIdentifier, MADB_DESC_READ); if (!SQL_SUCCEEDED(ret)) return ret; MADB_CLEAR_ERROR(&Desc->Error); if (RecNumber) if (!(DescRecord= MADB_DescGetInternalRecord(Desc, RecNumber - 1, MADB_DESC_READ))) return SQL_ERROR; switch (FieldIdentifier) { case SQL_DESC_ALLOC_TYPE: *((SQLINTEGER *)ValuePtr)= Desc->Header.AllocType; break; case SQL_DESC_ARRAY_SIZE: *((SQLULEN *)ValuePtr)= Desc->Header.ArraySize; break; case SQL_DESC_ARRAY_STATUS_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.ArrayStatusPtr; break; case SQL_DESC_BIND_OFFSET_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.BindOffsetPtr; break; case SQL_DESC_BIND_TYPE: *((SQLULEN *)ValuePtr)= Desc->Header.BindType; break; case SQL_DESC_COUNT: *(SQLSMALLINT *)ValuePtr= Desc->Header.Count; break; case SQL_DESC_ROWS_PROCESSED_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.RowsProcessedPtr; break; case SQL_DESC_AUTO_UNIQUE_VALUE: *((SQLINTEGER *)ValuePtr)= DescRecord->AutoUniqueValue; break; case SQL_DESC_BASE_COLUMN_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_BASE_TABLE_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->BaseTableName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_CASE_SENSITIVE: *((SQLINTEGER *)ValuePtr)= DescRecord->CaseSensitive; break; case SQL_DESC_CATALOG_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->BaseCatalogName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_CONCISE_TYPE: *((SQLSMALLINT *)ValuePtr)= DescRecord->ConciseType; break; case SQL_DESC_DATA_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->DataPtr; break; case SQL_DESC_DATETIME_INTERVAL_CODE: *((SQLSMALLINT *)ValuePtr)= DescRecord->DateTimeIntervalCode; break; case SQL_DESC_DATETIME_INTERVAL_PRECISION: *((SQLINTEGER *)ValuePtr)= DescRecord->DateTimeIntervalPrecision; break; case SQL_DESC_FIXED_PREC_SCALE: *((SQLSMALLINT *)ValuePtr)= DescRecord->FixedPrecScale; break; case SQL_DESC_INDICATOR_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->IndicatorPtr; break; case SQL_DESC_LENGTH: *((SQLINTEGER *)ValuePtr)= DescRecord->DescLength; break; case SQL_DESC_LITERAL_PREFIX: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->LiteralPrefix; break; case SQL_DESC_LITERAL_SUFFIX: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->LiteralSuffix; break; case SQL_DESC_LOCAL_TYPE_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->LocalTypeName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; DescRecord->Unnamed= SQL_NAMED; break; case SQL_DESC_NULLABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Nullable; break; case SQL_DESC_NUM_PREC_RADIX: *((SQLINTEGER *)ValuePtr)= DescRecord->NumPrecRadix; break; case SQL_DESC_OCTET_LENGTH: *((SQLLEN *)ValuePtr)= DescRecord->OctetLength; break; case SQL_DESC_OCTET_LENGTH_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->OctetLengthPtr; break; case SQL_DESC_PARAMETER_TYPE: *((SQLSMALLINT *)ValuePtr)= DescRecord->ParameterType; break; case SQL_DESC_PRECISION: *((SQLINTEGER *)ValuePtr)= DescRecord->Precision; break; #if (ODBCVER >= 0x0350) case SQL_DESC_ROWVER: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)(SQLULEN)DescRecord->RowVer; break; #endif case SQL_DESC_SCALE: *((SQLINTEGER *)ValuePtr)= DescRecord->Scale; break; case SQL_DESC_SCHEMA_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->SchemaName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_SEARCHABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Searchable; break; case SQL_DESC_TABLE_NAME: Length= MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->TableName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLINTEGER)Length; break; case SQL_DESC_TYPE: *((SQLINTEGER *)ValuePtr)= DescRecord->Type; break; case SQL_DESC_TYPE_NAME: *StringLengthPtr= (SQLINTEGER)MADB_SetString(isWChar ? &utf8 : 0, ValuePtr, BufferLength, DescRecord->TypeName, SQL_NTS, &Desc->Error); break; case SQL_DESC_UNSIGNED: *((SQLINTEGER *)ValuePtr)= DescRecord->Unsigned; break; case SQL_DESC_UPDATABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Updateable; break; } return ret; } /* {{{ MADB_DescSetField */ SQLRETURN MADB_DescSetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, my_bool isWChar) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_DescRecord *DescRecord= NULL; SQLRETURN ret; SQL_UNNAMED; ret= MADB_DeskCheckFldId(Desc, FieldIdentifier, MADB_DESC_WRITE); /* Application may set IPD's field SQL_DESC_UNNAMED to SQL_UNNAMED only */ if (FieldIdentifier == SQL_DESC_UNNAMED && (SQLSMALLINT)(SQLULEN)ValuePtr == SQL_NAMED) { ret= MADB_SetError(&Desc->Error, MADB_ERR_HY092, NULL, 0); } if (!SQL_SUCCEEDED(ret)) return ret; MADB_CLEAR_ERROR(&Desc->Error); switch (FieldIdentifier) { case SQL_DESC_ARRAY_SIZE: Desc->Header.ArraySize= (SQLULEN)ValuePtr; return SQL_SUCCESS; case SQL_DESC_ARRAY_STATUS_PTR: Desc->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; return SQL_SUCCESS; case SQL_DESC_BIND_OFFSET_PTR: Desc->Header.BindOffsetPtr= (SQLULEN *)ValuePtr; return SQL_SUCCESS; case SQL_DESC_BIND_TYPE: Desc->Header.BindType= (SQLINTEGER)(SQLLEN)ValuePtr; return SQL_SUCCESS; case SQL_DESC_COUNT: Desc->Header.Count= (SQLSMALLINT)(SQLLEN)ValuePtr; return SQL_SUCCESS; case SQL_DESC_ROWS_PROCESSED_PTR: Desc->Header.RowsProcessedPtr= (SQLULEN *)ValuePtr; return SQL_SUCCESS; } if (RecNumber > 0) { if (!(DescRecord= MADB_DescGetInternalRecord(Desc, RecNumber - 1, MADB_DESC_WRITE))) return SQL_ERROR; switch (FieldIdentifier) { case SQL_DESC_CONCISE_TYPE: DescRecord->ConciseType= (SQLSMALLINT)(SQLLEN)ValuePtr; DescRecord->Type= MADB_GetTypeFromConciseType(DescRecord->ConciseType); if (DescRecord->Type == SQL_INTERVAL) { DescRecord->DateTimeIntervalCode= DescRecord->ConciseType - 100; } break; case SQL_DESC_DATA_PTR: DescRecord->DataPtr= ValuePtr; break; case SQL_DESC_DATETIME_INTERVAL_CODE: DescRecord->DateTimeIntervalCode= (SQLSMALLINT)(SQLLEN)ValuePtr; break; case SQL_DESC_DATETIME_INTERVAL_PRECISION: DescRecord->DateTimeIntervalPrecision= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_DESC_FIXED_PREC_SCALE: DescRecord->FixedPrecScale= (SQLSMALLINT)(SQLLEN)ValuePtr; break; case SQL_DESC_INDICATOR_PTR: DescRecord->IndicatorPtr= (SQLLEN *)ValuePtr; break; case SQL_DESC_LENGTH: DescRecord->DescLength= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_DESC_NUM_PREC_RADIX: DescRecord->NumPrecRadix= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_DESC_OCTET_LENGTH: DescRecord->OctetLength= (SQLLEN)ValuePtr; break; case SQL_DESC_OCTET_LENGTH_PTR: DescRecord->OctetLengthPtr= (SQLLEN *)ValuePtr; break; case SQL_DESC_PARAMETER_TYPE: DescRecord->ParameterType= (SQLSMALLINT)(SQLLEN)ValuePtr; break; case SQL_DESC_PRECISION: DescRecord->Precision= (SQLSMALLINT)(SQLLEN)ValuePtr; break; case SQL_DESC_SCALE: if ((SQLSMALLINT)(SQLLEN)ValuePtr > MADB_MAX_SCALE) { DescRecord->Scale= MADB_MAX_SCALE; ret= MADB_SetError(&Desc->Error, MADB_ERR_01S02, NULL, 0); } else { DescRecord->Scale= (SQLSMALLINT)(SQLLEN)ValuePtr; } break; case SQL_DESC_TYPE: DescRecord->Type= (SQLSMALLINT)(SQLLEN)ValuePtr; DescRecord->ConciseType= DescRecord->Type; break; } /* bug41018 (ma_desc.c): We need to unbind in case parameter doesn't set a buffer or header field */ switch (FieldIdentifier) { case SQL_DESC_DATA_PTR: case SQL_DESC_OCTET_LENGTH_PTR: case SQL_DESC_INDICATOR_PTR: break; default: if (Desc->DescType== MADB_DESC_ARD && DescRecord && DescRecord->DataPtr) DescRecord->DataPtr= NULL; break; } /* inUse is only used to check if column/parameter was bound or not. Thus we do not set it for each field, but only for those, that make column/parameter "bound" */ if (DescRecord && (DescRecord->DataPtr != NULL || DescRecord->OctetLengthPtr != NULL || DescRecord->IndicatorPtr != NULL)) DescRecord->inUse= 1; } return ret; } /* }}} */ /* {{{ MADB_DescCopyDesc */ SQLRETURN MADB_DescCopyDesc(MADB_Desc *SrcDesc, MADB_Desc *DestDesc) { if (!SrcDesc) return SQL_INVALID_HANDLE; if (DestDesc->DescType == MADB_DESC_IRD) { MADB_SetError(&DestDesc->Error, MADB_ERR_HY016, NULL, 0); return SQL_ERROR; } if (SrcDesc->DescType == MADB_DESC_IRD && !SrcDesc->Header.Count) { MADB_SetError(&DestDesc->Error, MADB_ERR_HY007, NULL, 0); return SQL_ERROR; } /* make sure there aren't old records */ MADB_DeleteDynamic(&DestDesc->Records); if (MADB_InitDynamicArray(&DestDesc->Records, sizeof(MADB_DescRecord), SrcDesc->Records.max_element, SrcDesc->Records.alloc_increment)) { MADB_SetError(&DestDesc->Error, MADB_ERR_HY001, NULL, 0); return SQL_ERROR; } memcpy(&DestDesc->Header, &SrcDesc->Header, sizeof(MADB_Header)); /* We don't copy AppType from Src to Dest. If we copy internal descriptor to the explicit/external, it stays explicit/external */ DestDesc->DescType= SrcDesc->DescType; memcpy(&DestDesc->Error, &SrcDesc->Error, sizeof(MADB_Error)); /* Since we never allocate pointers we can just copy content */ memcpy(DestDesc->Records.buffer, SrcDesc->Records.buffer, SrcDesc->Records.size_of_element * SrcDesc->Records.max_element); DestDesc->Records.elements= SrcDesc->Records.elements; /* internal buffer needs to be clearead or we will get it freed twice with all nice subsequences */ { unsigned int i; for (i= 0; i < DestDesc->Records.elements; ++i) { MADB_DescRecord *Rec= MADB_DescGetInternalRecord(DestDesc, i, MADB_DESC_READ); if (Rec != NULL) { Rec->InternalBuffer= NULL; } } } return SQL_SUCCESS; } /* }}} */ SQLRETURN MADB_DescGetRec(MADB_Desc *Desc, SQLSMALLINT RecNumber, SQLCHAR *Name, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr, BOOL isWChar) { MADB_DescRecord *Record; SQLLEN Length; MADB_CLEAR_ERROR(&Desc->Error); if (!(Record= MADB_DescGetInternalRecord(Desc, RecNumber, MADB_DESC_READ))) { MADB_SetError(&Desc->Error, MADB_ERR_07009, NULL, 0); return Desc->Error.ReturnValue; } /* SQL_DESC_NAME */ Length= MADB_SetString(isWChar ? &utf8 : 0, Name, BufferLength, Record->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; Record->Unnamed= SQL_NAMED; /* SQL_DESC_TYPE */ *(SQLSMALLINT *)TypePtr= (SQLSMALLINT)Record->Type; /* SQL_DESC_DATETIME_INTERVAL_CODE */ *(SQLSMALLINT *)SubTypePtr= Record->DateTimeIntervalCode; /* SQL_DESC_OCTET_LENGTH */ *(SQLLEN *)LengthPtr= (SQLLEN)Record->OctetLength; /* SQL_DESC_PRECISION */ *(SQLSMALLINT *)PrecisionPtr= (SQLSMALLINT)Record->Precision; /* SQL_DESC_SCALE */ *(SQLSMALLINT *)ScalePtr= (SQLSMALLINT)Record->Scale; /* SQL_DESC_NULLABLE */ *(SQLSMALLINT *)NullablePtr= (SQLSMALLINT)Record->Nullable; return SQL_SUCCESS; } mariadb-connector-odbc-3.1.15/ma_desc.h000066400000000000000000000055101414431724000176420ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2015 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_desc_h_ #define _ma_desc_h_ #define MADB_DESC_NONE 0 #define MADB_DESC_READ 1 #define MADB_DESC_WRITE 2 #define MADB_DESC_RW 3 #define MADB_DESC_INIT_REC_NUM 32 #define MADB_DESC_INIT_STMT_NUM 16 enum enum_madb_desc_type {MADB_DESC_APD= 0, MADB_DESC_ARD, MADB_DESC_IPD, MADB_DESC_IRD, MADB_DESC_UNKNOWN=254}; MADB_DescRecord *MADB_DescGetInternalRecord(MADB_Desc *Desc, SQLSMALLINT RecordNumber, SQLSMALLINT Type); MADB_Desc *MADB_DescInit(MADB_Dbc *Dbc, enum enum_madb_desc_type DescType, my_bool isExternal); SQLRETURN MADB_DescFree(MADB_Desc *Desc, my_bool RecordsOnly); SQLRETURN MADB_DescGetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr, my_bool isWChar); SQLRETURN MADB_DescSetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, my_bool isWChar); my_bool MADB_DescSetIrdMetadata(MADB_Stmt *Stmt, MYSQL_FIELD *Fields, unsigned int NumFields); SQLRETURN MADB_DescCopyDesc(MADB_Desc *SrcDesc, MADB_Desc *DestDesc); SQLRETURN MADB_DescGetRec(MADB_Desc *Desc, SQLSMALLINT RecNumber, SQLCHAR *Name, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr, BOOL isWChar); my_bool MADB_FixColumnDataTypes(MADB_Stmt *Stmt, MADB_ShortTypeInfo *ColTypesArr); #endif /* _ma_desc_h_ */ mariadb-connector-odbc-3.1.15/ma_dll.c000066400000000000000000000026411414431724000174740ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include BOOL __stdcall DllMain ( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { switch (fdwReason) { case DLL_PROCESS_ATTACH: mysql_library_init(0, NULL, NULL); break; case DLL_PROCESS_DETACH: mysql_library_end(); break; case DLL_THREAD_ATTACH: mysql_thread_init(); break; case DLL_THREAD_DETACH: mysql_thread_end(); break; } return TRUE; } mariadb-connector-odbc-3.1.15/ma_driver.c000066400000000000000000000033771414431724000202230ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013 SkySQL AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include /* {{{ MADB_DriverInit */ MADB_Drv *MADB_DriverInit(void) { return (MADB_Drv* )MADB_CALLOC(sizeof(MADB_Drv)); } void MADB_DriverFree(MADB_Drv *Drv) { if (Drv) { MADB_FREE(Drv->DriverName); MADB_FREE(Drv->OdbcLibrary); MADB_FREE(Drv->SetupLibrary); MADB_FREE(Drv); } } /* {{{ MADB_DriverGet */ MADB_Drv * MADB_DriverGet(char *DriverName) { MADB_Drv *Drv= NULL; char Value[2048]; if (!DriverName || !SQLGetPrivateProfileString(DriverName, "Driver", "", Value, 2048, "ODBCINST.INI")) return NULL; Drv= MADB_DriverInit(); Drv->DriverName= _strdup(DriverName); Drv->OdbcLibrary= _strdup(Value); if (SQLGetPrivateProfileString(DriverName, "Setup", "", Value, 2048, "ODBCINST.INI")) Drv->SetupLibrary= _strdup(Value); return Drv; } mariadb-connector-odbc-3.1.15/ma_driver.h000066400000000000000000000032731414431724000202230ustar00rootroot00000000000000/*********************************************************************** Copyright (c) 2013 Monty Program Ab; Copyright (c) 2013 Georg Richter Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ************************************************************************/ #ifndef _ma_driver_h_ #define _ma_driver_h_ typedef struct { char *DriverName; char *OdbcLibrary; char *SetupLibrary; } MADB_Drv; MADB_Drv * MADB_DriverGet(char *DriverName); void MADB_DriverFree(MADB_Drv *Drv); MADB_Drv *MADB_DriverInit(void); #endif /* _ma_driver_h_ */ mariadb-connector-odbc-3.1.15/ma_dsn.c000066400000000000000000000546141414431724000175140ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #define _GNU_SOURCE #include #define DSNKEY_OPTIONS_INDEX 3 #define DSNKEY_OPTION_INDEX 4 #define DSNKEY_NAMEDPIPE_INDEX 5 #define DSNKEY_TCPIP_INDEX 6 #define DSNKEY_SERVER_INDEX 7 #define DSNKEY_UID_INDEX 8 #define DSNKEY_PWD_INDEX 9 #define DSNKEY_DATABASE_INDEX 10 #define DSNKEY_FP_INDEX 25 #define DSNKEY_FPLIST_INDEX 26 /* If adding new connstring option, one should add them to the end of the array section before aliases, because some arrays defining program logic use indexes of an element in this array. In particular, in the setup lib there is array mapping dsn field(i.e. this array element) to parent window and resource id of the contorl in the dialog. Thus if you still think it should go somewhere in the middle, mind also change that array and at least DsnKeysSwitch below in this file, and index defines in this file, accordingly */ MADB_DsnKey DsnKeys[]= { {"DSN", offsetof(MADB_Dsn, DSNName), DSN_TYPE_STRING, 0, 0}, /* 0 */ {"DESCRIPTION", offsetof(MADB_Dsn, Description), DSN_TYPE_STRING, 0, 0}, {"DRIVER", offsetof(MADB_Dsn, Driver), DSN_TYPE_STRING, 0, 0}, /* OPTIONS should go above all DSN_TYPE_OPTION. They are not saved in DSN separately, and then DSN is read, corresponding properties are filled from OPTIONS. Also putting its alias here - it should not appear on Windows(unless somebody edits registry manually), but on *nix we can expect everything. Array index used in some places to decide if the key is OPTIONS */ {"OPTIONS", offsetof(MADB_Dsn, Options), DSN_TYPE_INT, 0, 0}, /* DSNKEY_OPTIONS_INDEX */ {"OPTION", DSNKEY_OPTIONS_INDEX, DSN_TYPE_INT, 0, 1}, /* DSNKEY_OPTION_INDEX */ {"NamedPipe", offsetof(MADB_Dsn, IsNamedPipe), DSN_TYPE_OPTION, MADB_OPT_FLAG_NAMED_PIPE, 0}, /* MADB_DSNKEY_NAMEDPIPE_INDEX */ {"TCPIP", offsetof(MADB_Dsn, IsTcpIp), DSN_TYPE_BOOL, 0, 0}, /* DSNKEY_TCPIP_INDEX */ {"SERVER", offsetof(MADB_Dsn, ServerName), DSN_TYPE_STRING, 0, 0}, /* DSNKEY_SERVER_INDEX */ {"UID", offsetof(MADB_Dsn, UserName), DSN_TYPE_STRING, 0, 0}, /* DSNKEY_UID_INDEX */ {"PWD", offsetof(MADB_Dsn, Password), DSN_TYPE_STRING, 0, 0}, /* DSNKEY_PWD_INDEX */ {"DATABASE", offsetof(MADB_Dsn, Catalog), DSN_TYPE_COMBO, 0, 0}, /* 10 DSNKEY_DATABASE_INDEX */ {"PORT", offsetof(MADB_Dsn, Port), DSN_TYPE_INT, 0, 0}, {"INITSTMT", offsetof(MADB_Dsn, InitCommand), DSN_TYPE_STRING, 0, 0}, {"CONN_TIMEOUT", offsetof(MADB_Dsn, ConnectionTimeout), DSN_TYPE_INT, 0, 0}, {"AUTO_RECONNECT", offsetof(MADB_Dsn, Reconnect), DSN_TYPE_OPTION, MADB_OPT_FLAG_AUTO_RECONNECT,0}, {"NO_PROMPT", offsetof(MADB_Dsn, ConnectPrompt), DSN_TYPE_OPTION, MADB_OPT_FLAG_NO_PROMPT,0}, {"CHARSET", offsetof(MADB_Dsn, CharacterSet), DSN_TYPE_COMBO, 0, 0}, {"TRACE", offsetof(MADB_Dsn, TraceFile), DSN_TYPE_STRING, 0, 0}, {"PLUGIN_DIR", offsetof(MADB_Dsn, ConnCPluginsDir), DSN_TYPE_STRING, 0, 0}, /* SSL */ {"SSLKEY", offsetof(MADB_Dsn, SslKey), DSN_TYPE_STRING, 0, 0}, {"SSLCERT", offsetof(MADB_Dsn, SslCert), DSN_TYPE_STRING, 0, 0}, /* 20 */ {"SSLCA", offsetof(MADB_Dsn, SslCa), DSN_TYPE_STRING, 0, 0}, {"SSLCAPATH", offsetof(MADB_Dsn, SslCaPath), DSN_TYPE_STRING, 0, 0}, {"SSLCIPHER", offsetof(MADB_Dsn, SslCipher), DSN_TYPE_STRING, 0, 0}, {"SSLVERIFY", offsetof(MADB_Dsn, SslVerify), DSN_TYPE_BOOL, 0, 0}, {"TLSPEERFP", offsetof(MADB_Dsn, TlsPeerFp), DSN_TYPE_STRING, 0, 0}, {"TLSPEERFPLIST", offsetof(MADB_Dsn, TlsPeerFpList), DSN_TYPE_STRING, 0, 0}, {"SSLCRL", offsetof(MADB_Dsn, SslCrl), DSN_TYPE_STRING, 0, 0}, {"SSLCRLPATH", offsetof(MADB_Dsn, SslCrlPath), DSN_TYPE_STRING, 0, 0}, {"SOCKET", offsetof(MADB_Dsn, Socket), DSN_TYPE_STRING, 0, 0}, {"SAVEFILE", offsetof(MADB_Dsn, SaveFile), DSN_TYPE_STRING, 0, 0}, /* 30 */ {"USE_MYCNF", offsetof(MADB_Dsn, ReadMycnf), DSN_TYPE_OPTION, MADB_OPT_FLAG_USE_CNF, 0}, {"TLSVERSION", offsetof(MADB_Dsn, TlsVersion), DSN_TYPE_CBOXGROUP, 0, 0}, {"FORCETLS", offsetof(MADB_Dsn, ForceTls), DSN_TYPE_BOOL, 0, 0}, {"SERVERKEY", offsetof(MADB_Dsn, ServerKey), DSN_TYPE_STRING, 0, 0}, {"TLSKEYPWD", offsetof(MADB_Dsn, TlsKeyPwd), DSN_TYPE_STRING, 0, 0}, {"INTERACTIVE", offsetof(MADB_Dsn, InteractiveClient), DSN_TYPE_BOOL, 0, 0}, {"FORWARDONLY", offsetof(MADB_Dsn, ForceForwardOnly), DSN_TYPE_OPTION, MADB_OPT_FLAG_FORWARD_CURSOR, 0}, {"SCHEMANOERROR", offsetof(MADB_Dsn, NeglectSchemaParam),DSN_TYPE_BOOL, 0, 0}, {"READ_TIMEOUT", offsetof(MADB_Dsn, ReadTimeout), DSN_TYPE_INT, 0, 0}, {"WRITE_TIMEOUT", offsetof(MADB_Dsn, WriteTimeout), DSN_TYPE_INT, 0, 0}, /* Aliases. Here offset is index of aliased key */ {"SERVERNAME", DSNKEY_SERVER_INDEX, DSN_TYPE_STRING, 0, 1}, {"USER", DSNKEY_UID_INDEX, DSN_TYPE_STRING, 0, 1}, {"PASSWORD", DSNKEY_PWD_INDEX, DSN_TYPE_STRING, 0, 1}, {"DB", DSNKEY_DATABASE_INDEX, DSN_TYPE_COMBO, 0, 1}, {"SSLFP", DSNKEY_FP_INDEX, DSN_TYPE_STRING, 0, 1}, {"SSLFPLIST", DSNKEY_FPLIST_INDEX, DSN_TYPE_STRING, 0, 1}, /* Terminating Null */ {NULL, 0, DSN_TYPE_BOOL,0,0} }; #define IS_OPTIONS_BITMAP(key_index) (key_index == DSNKEY_OPTIONS_INDEX) typedef struct { unsigned int Key; unsigned int Dependent; BOOL Same; /* Should dependent be switched same way, or in reverse */ } MADB_DsnKeyDep; /* Define pairs of keys that are switches, i.e. setting one should reset the other. Transitive dependencies have to be defined as direct dependencies here as well */ const MADB_DsnKeyDep DsnKeysSwitch[]= { {DSNKEY_NAMEDPIPE_INDEX, DSNKEY_TCPIP_INDEX, 0}, {DSNKEY_TCPIP_INDEX, DSNKEY_NAMEDPIPE_INDEX, 0} }; const char TlsVersionName[][8]= {"TLSv1.1", "TLSv1.2", "TLSv1.3"}; const char TlsVersionBits[]= {MADB_TLSV11, MADB_TLSV12, MADB_TLSV13}; /* {{{ MADB_DSN_SetDefaults() */ void MADB_DSN_SetDefaults(MADB_Dsn *Dsn) { Dsn->IsTcpIp= 1; } /* }}} */ /* {{{ MADB_Dsn_Init() */ MADB_Dsn *MADB_DSN_Init() { MADB_Dsn *Dsn; if ((Dsn= (MADB_Dsn *)MADB_CALLOC(sizeof(MADB_Dsn)))) { Dsn->FreeMe= TRUE; Dsn->Keys= (MADB_DsnKey *)&DsnKeys; } return Dsn; } /* }}} */ /* {{{ MADB_Dsn_Free */ void MADB_DSN_Free(MADB_Dsn *Dsn) { if (!Dsn) return; MADB_FREE(Dsn->DSNName); MADB_FREE(Dsn->Driver); MADB_FREE(Dsn->Description); MADB_FREE(Dsn->ServerName); MADB_FREE(Dsn->UserName); MADB_FREE(Dsn->Password); MADB_FREE(Dsn->Catalog); MADB_FREE(Dsn->CharacterSet); MADB_FREE(Dsn->InitCommand); MADB_FREE(Dsn->TraceFile); MADB_FREE(Dsn->Socket); MADB_FREE(Dsn->ConnCPluginsDir); MADB_FREE(Dsn->SslKey); MADB_FREE(Dsn->SslCert); MADB_FREE(Dsn->SslCa); MADB_FREE(Dsn->SslCaPath); MADB_FREE(Dsn->SslCipher); MADB_FREE(Dsn->SslCrl); MADB_FREE(Dsn->SslCrlPath); MADB_FREE(Dsn->TlsPeerFp); MADB_FREE(Dsn->TlsPeerFpList); MADB_FREE(Dsn->SaveFile); MADB_FREE(Dsn->ServerKey); MADB_FREE(Dsn->TlsKeyPwd); if (Dsn->FreeMe) MADB_FREE(Dsn); } /* }}} */ void MADB_SetOptionValue(MADB_Dsn *Dsn, MADB_DsnKey *DsnKey, my_bool value) { *GET_FIELD_PTR(Dsn, DsnKey, my_bool)= value; if (value) { Dsn->Options |= DsnKey->FlagValue; } else { Dsn->Options &= ~DsnKey->FlagValue; } } my_bool MADB_DsnStoreValue(MADB_Dsn *Dsn, unsigned int DsnKeyIdx, char *Value, my_bool OverWrite); /* {{{ MADB_DsnSwitchDependents */ /* If TCPIP selected, we have to reset NAMEDPIPE */ BOOL MADB_DsnSwitchDependents(MADB_Dsn *Dsn, unsigned int Changed) { int i; for (i= 0; i < sizeof(DsnKeysSwitch)/sizeof(MADB_DsnKeyDep); ++i) { if (DsnKeysSwitch[i].Key == Changed) { my_bool KeySet; switch (DsnKeys[Changed].Type) { case DSN_TYPE_STRING: case DSN_TYPE_COMBO: { char *str= *GET_FIELD_PTR(Dsn, &DsnKeys[Changed], char*); KeySet= str && *str; } break; case DSN_TYPE_OPTION: case DSN_TYPE_BOOL: case DSN_TYPE_CBOXGROUP: { KeySet= *GET_FIELD_PTR(Dsn, &DsnKeys[Changed], my_bool); } break; case DSN_TYPE_INT: { KeySet= *GET_FIELD_PTR(Dsn, &DsnKeys[Changed], int) != 0; } } /* No problem to deal with aliases here as well, but let's keep things simple */ if (DsnKeys[DsnKeysSwitch[i].Dependent].IsAlias != 0) { return FALSE; } switch(DsnKeys[DsnKeysSwitch[i].Dependent].Type) { case DSN_TYPE_BOOL: *GET_FIELD_PTR(Dsn, &DsnKeys[DsnKeysSwitch[i].Dependent], my_bool)= DsnKeysSwitch[i].Same == KeySet ? 1 : 0; break; case DSN_TYPE_OPTION: MADB_SetOptionValue(Dsn, &DsnKeys[DsnKeysSwitch[i].Dependent], DsnKeysSwitch[i].Same == KeySet ? 1 : 0); break; default: return FALSE; /* Only boolean fields are supported as dependent atm */ } } } return TRUE; } /* }}} */ /* {{{ MADB_DsnStoreValue */ my_bool MADB_DsnStoreValue(MADB_Dsn *Dsn, unsigned int DsnKeyIdx, char *Value, my_bool OverWrite) { MADB_DsnKey *DsnKey= &DsnKeys[DsnKeyIdx]; if (!Dsn || DsnKey->IsAlias) return FALSE; switch(DsnKey->Type) { case DSN_TYPE_STRING: case DSN_TYPE_COMBO: { char **p= GET_FIELD_PTR(Dsn, DsnKey, char*); if (*p && OverWrite == FALSE) break; /* For the case of making copy of currently stored values */ MADB_RESET(*p, Value); } break; case DSN_TYPE_BOOL: /* If value is not set or we may overwrite it */ if (!(*GET_FIELD_PTR(Dsn, DsnKey, my_bool) && OverWrite == FALSE)) { *GET_FIELD_PTR(Dsn, DsnKey, my_bool)= atoi(Value); } break; case DSN_TYPE_CBOXGROUP: /* If value is not set or we may overwrite it */ if (! (*GET_FIELD_PTR(Dsn, DsnKey, char) && OverWrite == FALSE)) { char IntValue= atoi(Value); /* Atm we have only one DSN_TYPE_CBOXGROUP!!!, and following works only for it. If sometime another such field is added, we will need another data structure array, that will bind DSN field with string values and bits for this field. So far we use hardcoded arrays for the singe such field we have atm */ if (IntValue == '\0') { unsigned int i; IntValue= 0; for (i= 0; i < sizeof(TlsVersionBits); ++i) { #ifdef _AIX if (strstr(Value, TlsVersionName[i]) != NULL) #else if (strcasestr(Value, TlsVersionName[i]) != NULL) #endif { IntValue|= TlsVersionBits[i]; } } } *GET_FIELD_PTR(Dsn, DsnKey, char)= IntValue; } break; case DSN_TYPE_INT: if (*GET_FIELD_PTR(Dsn, DsnKey, int) && OverWrite == FALSE) break; *GET_FIELD_PTR(Dsn, DsnKey, int)= strtoul(Value, NULL, 10); break; case DSN_TYPE_OPTION: if (*GET_FIELD_PTR(Dsn, DsnKey, my_bool) && OverWrite == FALSE) break; MADB_SetOptionValue(Dsn, DsnKey, strtoul(Value, NULL, 10) != 0 ? 1 : 0); break; } return MADB_DsnSwitchDependents(Dsn, DsnKeyIdx); } /* }}} */ /* {{{ MADB_DsnUpdateOptionsFields */ void MADB_DsnUpdateOptionsFields(MADB_Dsn *Dsn) { int i= 0; while (DsnKeys[i].DsnKey != NULL) { if (DsnKeys[i].IsAlias == 0) { if (DsnKeys[i].Type == DSN_TYPE_OPTION) { *GET_FIELD_PTR(Dsn, &DsnKeys[i], my_bool)= (my_bool)(DSN_OPTION(Dsn, DsnKeys[i].FlagValue) ? 1 : 0); MADB_DsnSwitchDependents(Dsn, i); } } ++i; } } /* }}} */ /* {{{ MADB_ReadDSN */ my_bool MADB_ReadDSN(MADB_Dsn *Dsn, const char *KeyValue, my_bool OverWrite) { char *Value; /* if no key/value pair was specified, we will try to read Dsn->DSNName */ if (!KeyValue) { Value= Dsn->DSNName; } else { if ((Value= strchr(KeyValue, '=')) != NULL) { ++Value; MADB_RESET(Dsn->DSNName, Value); } } if (Value) { int i= 1; char KeyVal[1024]; while (DsnKeys[i].DsnKey) { unsigned int KeyIdx= DsnKeys[i].IsAlias ? DsnKeys[i].DsnOffset : i; if (SQLGetPrivateProfileString(Dsn->DSNName, DsnKeys[i].DsnKey, "", KeyVal, 1024, "ODBC.INI") > 0) { if (!MADB_DsnStoreValue(Dsn, KeyIdx, KeyVal, OverWrite)) return FALSE; } else if (DsnKeys[i].Type == DSN_TYPE_OPTION) { *GET_FIELD_PTR(Dsn, &DsnKeys[KeyIdx], my_bool)= (my_bool)(DSN_OPTION(Dsn, DsnKeys[KeyIdx].FlagValue) ? 1 : 0); } ++i; } return TRUE; } return FALSE; } /* }}} */ my_bool MADB_DSN_Exists(const char *DsnName) { my_bool ret; char buffer[1024]; char *p= ""; if (!DsnName) return FALSE; ret= (SQLGetPrivateProfileString(DsnName, NULL, p, buffer, 1024, "ODBC.INI") > 0); return ret; } /* {{{ MADB_SaveDSN */ my_bool MADB_SaveDSN(MADB_Dsn *Dsn) { int i= 1; char Value[32]; my_bool ret; DWORD ErrNum; if (!SQLValidDSN(Dsn->DSNName)) { strcpy_s(Dsn->ErrorMsg, SQL_MAX_MESSAGE_LENGTH, "Invalid Data Source Name"); return FALSE; } if (!SQLRemoveDSNFromIni(Dsn->DSNName)) { SQLInstallerError(1,&ErrNum, Dsn->ErrorMsg, SQL_MAX_MESSAGE_LENGTH, NULL); return FALSE; } if (!SQLWriteDSNToIni(Dsn->DSNName, Dsn->Driver)) { SQLInstallerError(1,&ErrNum, Dsn->ErrorMsg, SQL_MAX_MESSAGE_LENGTH, NULL); return FALSE; } while(DsnKeys[i].DsnKey) { /* Skipping aliases - options are saved by primary name only */ if (!DsnKeys[i].IsAlias) { ret= TRUE; /* We do not save DSN_TYPE_OPTION - they are saved as OPTIONS bits */ switch (DsnKeys[i].Type) { case DSN_TYPE_BOOL: ret= SQLWritePrivateProfileString(Dsn->DSNName, DsnKeys[i].DsnKey, *GET_FIELD_PTR(Dsn, &DsnKeys[i], my_bool) ? "1" : "0", "ODBC.INI"); break; case DSN_TYPE_INT: { _snprintf(Value ,32, "%d", *(int *)((char *)Dsn + DsnKeys[i].DsnOffset)); ret= SQLWritePrivateProfileString(Dsn->DSNName, DsnKeys[i].DsnKey, Value, "ODBC.INI"); } break; case DSN_TYPE_CBOXGROUP: { _snprintf(Value, 32, "%hu", (short)*GET_FIELD_PTR(Dsn, &DsnKeys[i], char)); ret= SQLWritePrivateProfileString(Dsn->DSNName, DsnKeys[i].DsnKey, Value, "ODBC.INI"); } break; case DSN_TYPE_STRING: case DSN_TYPE_COMBO: { char *Val= *GET_FIELD_PTR(Dsn, &DsnKeys[i], char*); if (Val && Val[0]) ret= SQLWritePrivateProfileString(Dsn->DSNName, DsnKeys[i].DsnKey, Val, "ODBC.INI"); } default: /* To avoid warning with some compilers */ break; } /* switch */ if (!ret) { SQLInstallerError(1,&ErrNum, Dsn->ErrorMsg, SQL_MAX_MESSAGE_LENGTH, NULL); return FALSE; } } i++; } /* Save Options */ _snprintf(Value ,32, "%d", Dsn->Options); if (!(ret= SQLWritePrivateProfileString(Dsn->DSNName, "OPTIONS", Value, "ODBC.INI"))) { SQLInstallerError(1,&ErrNum, Dsn->ErrorMsg, SQL_MAX_MESSAGE_LENGTH, NULL); return FALSE; } return TRUE; } /* }}} */ size_t ConnStringLength(const char * String, char Delimiter) { size_t result= strlen(String); const char *p= String + result + 1; if (Delimiter != '\0') { return result; } /* else - we have string with null terminated key=value pairs with additional NULL after last pair */ while (*p) { p+= strlen(p) + 1; } return p - String /* Length without ending NULL */; } /* {{{ MADB_ParseConnString */ my_bool MADB_ParseConnString(MADB_Dsn *Dsn, const char *String, size_t Length, char Delimiter) { char *Buffer, *Key, *Value, *ValueBuf; my_bool ret= TRUE; if (!String) return FALSE; if (Length == SQL_NTS) { Length= ConnStringLength(String, Delimiter); } Buffer= MADB_ALLOC(Length + 1); Buffer= memcpy(Buffer, String, Length + 1); Key= Buffer; ValueBuf= MADB_ALLOC(Length - 4); /*DSN= - DSN or DRIVER must be in */ while (Key && Key < ((char *)Buffer + Length)) { int i= 0; /* The case of ;; - "empty key/value pair. Probably that shouldn't be allowed. But parser uset to digest this, so leaving this as a feature so far TODO: check and maybe remove for the next version */ if (Delimiter != '\0' && *Key == Delimiter) { ++Key; continue; } if (!(Value= strchr(Key, '='))) { ret= FALSE; break; } *Value= 0; ++Value; Key= trim(Key); while (DsnKeys[i].DsnKey) { if (_stricmp(DsnKeys[i].DsnKey, Key) == 0) { char *p= NULL; if (DsnKeys[i].IsAlias) { i= DsnKeys[i].DsnOffset; /* For aliases DsnOffset is index of aliased "main" key */ } Value= ltrim(Value); if (Value[0] == '{') { char *valueBufPtr= ValueBuf; char *prev= ++Value; *valueBufPtr= '\0'; while ((p = strchr(prev, '}')) != NULL ) { memcpy(valueBufPtr, prev, p - prev); valueBufPtr+= p - prev; if (*(p + 1) == '}') { *(valueBufPtr++)= '}'; *valueBufPtr= '\0'; prev= p + 2; } else { *valueBufPtr= '\0'; ++p; break; } } Value= ValueBuf; } else if ((p= strchr(Value, Delimiter))) { *p= 0; } /* TODO: 3.2 we should not trim enclosed in braces, I think */ Value= trim(Value); /* Overwriting here - if an option repeated more than once in the string, its last entrance will determine the value */ if (!MADB_DsnStoreValue(Dsn, i, Value, TRUE)) { ret= FALSE; goto end; } if (IS_OPTIONS_BITMAP(i)) { MADB_DsnUpdateOptionsFields(Dsn); } if (p) { Key= p + 1; } else { Key= NULL; } break; } ++i; } /* Unknown keyword */ if (DsnKeys[i].DsnKey == NULL) { //TODO: shouldn't some error/warning be thrown? Key= strchr(Value, Delimiter); if (Key != NULL) { ++Key; } } } end: MADB_FREE(Buffer); MADB_FREE(ValueBuf); return ret; } /* }}} */ /* {{{ MADB_ReadConnString */ /* Like ParseConnString, but expands DSN if needed, preserving connection string values precedence. Or in other words - it is combination of ReadDsn and ParseConnString */ BOOL MADB_ReadConnString(MADB_Dsn *Dsn, const char *String, size_t Length, char Delimiter) { /* Basically at this point we need DSN name only */ if (!MADB_ParseConnString(Dsn, String, Length, Delimiter)) { return FALSE; } /* "If the connection string contains the DRIVER keyword, the driver cannot retrieve information about the data source from the system information." https://msdn.microsoft.com/en-us/library/ms715433%28v=vs.85%29.aspx */ if (Dsn->DSNName && MADB_IS_EMPTY(Dsn->Driver)) { MADB_ReadDSN(Dsn, NULL, FALSE); /* This redundancy is needed to be able to reset options set in the DSN, e.g. if DSN has Reconnect option selected, and connection string has AUTO_RECONNECT=0. Connection string should have precedence */ MADB_ParseConnString(Dsn, String, Length, Delimiter); } return TRUE; } /* }}} */ /* {{{ MADB_DsnToString */ SQLULEN MADB_DsnToString(MADB_Dsn *Dsn, char *OutString, SQLULEN OutLength) { int i= 0; SQLULEN TotalLength= 0; char *p= OutString; char *Value= NULL; char TmpStr[1024]= { '\0' }; char IntVal[12]; int CpyLength; if (OutLength && OutString) OutString[0]= '\0'; while (DsnKeys[i].DsnKey) { Value= NULL; if (!DsnKeys[i].IsAlias) { switch (DsnKeys[i].Type) { case DSN_TYPE_STRING: case DSN_TYPE_COMBO: Value= *GET_FIELD_PTR(Dsn, &DsnKeys[i], char*); if (MADB_IS_EMPTY(Value)) { ++i; continue; } break; case DSN_TYPE_INT: if (*GET_FIELD_PTR(Dsn, &DsnKeys[i], int)) { _snprintf(IntVal, sizeof(IntVal), "%d", *GET_FIELD_PTR(Dsn, &DsnKeys[i], int)); Value= IntVal; } break; case DSN_TYPE_BOOL: if (*GET_FIELD_PTR(Dsn, &DsnKeys[i], my_bool)) { Value= "1"; } break; case DSN_TYPE_CBOXGROUP: if (*GET_FIELD_PTR(Dsn, &DsnKeys[i], char)) { _snprintf(IntVal, sizeof(IntVal), "%hu", (short)*GET_FIELD_PTR(Dsn, &DsnKeys[i], char)); Value= IntVal; } break; default: /* To avoid warning with some compilers */ break; } } if (Value) { my_bool isSpecial= (strchr(Value, ' ') || strchr(Value, ';') || strchr(Value, '@')); CpyLength= _snprintf(TmpStr + TotalLength, 1024 - TotalLength, "%s%s=%s%s%s", (TotalLength) ? ";" : "", DsnKeys[i].DsnKey, isSpecial ? "{" : "", Value, isSpecial ? "}" : ""); TotalLength+= CpyLength; } ++i; } if (OutLength && OutString) { strncpy_s(OutString, OutLength, TmpStr, TotalLength); } return TotalLength; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_dsn.h000066400000000000000000000154441414431724000175170ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_dsn_h_ #define _ma_dsn_h_ #include /* MySQL ODBC compatibility options */ #define MADB_OPT_FLAG_FIELD_LENGTH 1 #define MADB_OPT_FLAG_FOUND_ROWS 2 #define MADB_OPT_FLAG_DEBUG 4 #define MADB_OPT_FLAG_BIG_PACKETS 8 #define MADB_OPT_FLAG_NO_PROMPT 16 #define MADB_OPT_FLAG_DYNAMIC_CURSOR 32 #define MADB_OPT_FLAG_NO_SCHEMA 64 #define MADB_OPT_FLAG_NO_DEFAULT_CURSOR 128 #define MADB_OPT_FLAG_NO_LOCALE 256 #define MADB_OPT_FLAG_PAD_SPACE 512 #define MADB_OPT_FLAG_FULL_COLUMN_NAMES 1024 /*10*/ #define MADB_OPT_FLAG_COMPRESSED_PROTO 2048 #define MADB_OPT_FLAG_IGNORE_SPACE 4096 #define MADB_OPT_FLAG_NAMED_PIPE 8192 #define MADB_OPT_FLAG_NO_BIGINT 16384 #define MADB_OPT_FLAG_NO_CATALOG 32768 #define MADB_OPT_FLAG_USE_CNF 65536 #define MADB_OPT_FLAG_SAFE 131072 #define MADB_OPT_FLAG_NO_TRANSACTIONS 262144 #define MADB_OPT_FLAG_LOG_QUERY 524288 #define MADB_OPT_FLAG_NO_CACHE 1048576 /*20*/ #define MADB_OPT_FLAG_FORWARD_CURSOR 2097152 #define MADB_OPT_FLAG_AUTO_RECONNECT 4194304 #define MADB_OPT_FLAG_AUTO_IS_NULL 8388608 #define MADB_OPT_FLAG_ZERO_DATE_TO_MIN 16777216 #define MADB_OPT_FLAG_MIN_DATE_TO_ZERO 33554432 #define MADB_OPT_FLAG_MULTI_STATEMENTS 67108864 #define MADB_OPT_FLAG_COLUMN_SIZE_S32 134217728 #define MADN_OPT_FLAG_NO_BINARY_RESULT 268435456 #define MADN_OPT_FLAG_BIGINT_BIND_STR 536870912 #define MADN_OPT_FLAG_NO_INFORMATION_SCHEMA 1073741824 /*30*/ enum enum_dsn_item_type { DSN_TYPE_STRING, DSN_TYPE_INT, DSN_TYPE_BOOL, DSN_TYPE_COMBO, /* Mainly the same as string, but the field in the dialog is combobox */ DSN_TYPE_OPTION, /* Connection string option has correspondent OPTIONS bit */ DSN_TYPE_CBOXGROUP /* Group of checkboxes each of them represent a bit in the field's value Bitmap size is 1 byte */ }; typedef struct { unsigned int Page; unsigned long Item; unsigned long value; } MADB_OptionsMap; typedef struct { char *DsnKey; unsigned int DsnOffset; enum enum_dsn_item_type Type; unsigned long FlagValue; my_bool IsAlias; } MADB_DsnKey; /* Definitions to tell setup library via isPrompt field what should it do */ #define MAODBC_CONFIG 0 #define MAODBC_PROMPT 1 #define MAODBC_PROMPT_REQUIRED 2 /* TLS version bits */ #define MADB_TLSV11 1 #define MADB_TLSV12 2 #define MADB_TLSV13 4 extern const char TlsVersionName[3][8]; extern const char TlsVersionBits[3]; typedef struct st_madb_dsn { /* TODO: Does it really matter to keep this array in the DSN structure? */ char ErrorMsg[SQL_MAX_MESSAGE_LENGTH]; /*** General ***/ char *DSNName; char *Driver; char *Description; /*** Connection parameters ***/ char *ServerName; char *UserName; char *Password; char *Catalog; char *CharacterSet; char *InitCommand; char *TraceFile; char *Socket; char *ConnCPluginsDir; /* SSL Settings */ char *SslKey; char *SslCert; char *SslCa; char *SslCaPath; char *SslCipher; char *SslCrl; char *SslCrlPath; char *TlsPeerFp; char *TlsPeerFpList; char *TlsKeyPwd; char* ServerKey; char* SaveFile; /* --- Internal --- */ MADB_DsnKey* Keys; /* Callbacke required for prompt to keep all memory de/allocation operations on same side of libraries */ char* (*allocator)(size_t); void (*free)(void*); int isPrompt; /* Internal - end */ unsigned int Port; /* Options */ unsigned int Options; unsigned int ConnectionTimeout; unsigned int ReadTimeout; unsigned int WriteTimeout; my_bool Reconnect; my_bool MultiStatements; /* TRUE means "no prompt" */ my_bool ConnectPrompt; my_bool IsNamedPipe; my_bool IsTcpIp; my_bool SslVerify; char TlsVersion; my_bool ForceTls; my_bool ReadMycnf; my_bool InteractiveClient; my_bool ForceForwardOnly; my_bool NeglectSchemaParam; /* Internal */ my_bool FreeMe; } MADB_Dsn; /* this structure is used to store and retrieve DSN Information */ extern MADB_DsnKey DsnKeys[]; #define GET_FIELD_PTR(DSN, DSNKEY, TYPE) ((TYPE *)((char*)(DSN) + (DSNKEY)->DsnOffset)) /*** Function prototypes ***/ MADB_Dsn * MADB_DSN_Init (void); void MADB_DSN_SetDefaults(MADB_Dsn *Dsn); void MADB_DSN_Free (MADB_Dsn *Dsn); my_bool MADB_ReadDSN (MADB_Dsn *Dsn, const char *KeyValue, my_bool OverWrite); my_bool MADB_SaveDSN (MADB_Dsn *Dsn); my_bool MADB_DSN_Exists (const char *DsnName); my_bool MADB_ParseConnString(MADB_Dsn *Dsn, const char *String, size_t Length, char Delimiter); BOOL MADB_ReadConnString (MADB_Dsn *Dsn, const char *String, size_t Length, char Delimiter); SQLULEN MADB_DsnToString (MADB_Dsn *Dsn, char *OutString, SQLULEN OutLength); void MADB_DsnUpdateOptionsFields (MADB_Dsn *Dsn); BOOL MADB_DSN_PossibleConnect (MADB_Dsn *Dsn); /*** Helper macros ***/ #define DSN_OPTION(a,b)\ ((a)->Options & b) #define MA_ODBC_CURSOR_DYNAMIC(a)\ DSN_OPTION((a), MADB_OPT_FLAG_DYNAMIC_CURSOR) #define MA_ODBC_CURSOR_FORWARD_ONLY(a)\ DSN_OPTION((a), MADB_OPT_FLAG_FORWARD_CURSOR) #define MADB_DSN_SET_STR(dsn, item, value, len)\ if((value) && (len) != 0)\ {\ if ((len) == SQL_NTS)\ (len)=(SQLSMALLINT)strlen((value));\ MADB_FREE((dsn)->item);\ (dsn)->item= (char *)calloc(len + 1, sizeof(char));\ memcpy((dsn)->item, (value),(len));\ } #endif /* _ma_dsn_h_ */ mariadb-connector-odbc-3.1.15/ma_environment.c000066400000000000000000000123151414431724000212640ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include extern Client_Charset utf8; extern MARIADB_CHARSET_INFO* DmUnicodeCs; extern MARIADB_CHARSET_INFO dummyUtf32le; Client_Charset SourceAnsiCs= {0, 0}; /* Basically it should be initialized with 0 anyway */ char* DefaultPluginLocation= NULL; #ifndef _MAX_PATH # define _MAX_PATH 260 #endif static char PluginLocationBuf[_MAX_PATH]; MARIADB_CHARSET_INFO * mysql_find_charset_name(const char *name); #ifdef _WIN32 # pragma comment(lib, "ws2_32.lib") #endif static my_bool little_endian() { int x= 1; char *c= (char*)&x; return *c; } /* {{{ MADB_EnvFree */ SQLRETURN MADB_EnvFree(MADB_Env *Env) { if (!Env) return SQL_ERROR; DeleteCriticalSection(&Env->cs); free(Env); #ifdef _WIN32 WSACleanup(); #endif return SQL_SUCCESS; } /* }}} */ const char* GetDefaultLogDir(); int GetSourceAnsiCs(Client_Charset *cc); /* {{{ MADB_EnvInit */ static void DetectAppType(MADB_Env* Env) { Env->AppType= ATypeGeneral; #ifdef _WIN32 if (GetModuleHandle("msaccess.exe") != NULL) { Env->AppType = ATypeMSAccess; } #endif } /* }}} */ /* {{{ MADB_EnvInit */ MADB_Env *MADB_EnvInit() { MADB_Env *Env= NULL; #ifdef _WIN32 /* Since we can't determine if WSAStartup has been called, we need to call it again */ WORD VersionRequested; int err; WSADATA WsaData; /* if possible use latest supported version (2.2) */ const unsigned int MajorVersion=2, MinorVersion=2; VersionRequested= MAKEWORD(MajorVersion, MinorVersion); /* Load WinSock library */ if ((err= WSAStartup(VersionRequested, &WsaData))) { /* todo: optional debug output */ return Env; } /* make sure 2.2 or higher is supported */ if ((LOBYTE(WsaData.wVersion) * 10 + HIBYTE(WsaData.wVersion)) < 22) { /* todo: optional debug output */ goto cleanup; } #endif mysql_library_init(0, NULL, NULL); if (!(Env= (MADB_Env *)MADB_CALLOC(sizeof(MADB_Env)))) { /* todo: optional debug output */ goto cleanup; } MADB_PutErrorPrefix(NULL, &Env->Error); InitializeCriticalSection(&Env->cs); Env->OdbcVersion= SQL_OV_ODBC3; /* This is probably is better todo with thread_once */ if (DmUnicodeCs == NULL) { if (sizeof(SQLWCHAR) == 2) { DmUnicodeCs= mariadb_get_charset_by_name(little_endian() ? "utf16le" : "utf16"); } else { DmUnicodeCs= little_endian() ? &dummyUtf32le : mariadb_get_charset_by_name("utf32"); } } utf8.cs_info= mariadb_get_charset_by_name("utf8mb4"); GetDefaultLogDir(); GetSourceAnsiCs(&SourceAnsiCs); /* If we have something in the buffer - then we've already tried to get default location w/out much success */ if (DefaultPluginLocation == NULL && strlen(PluginLocationBuf) == 0) { DefaultPluginLocation= MADB_GetDefaultPluginsDir(PluginLocationBuf, sizeof(PluginLocationBuf)); } DetectAppType(Env); cleanup: #ifdef _WIN32 if (!Env) WSACleanup(); #endif return Env; } /* }}} */ /* {{{ MADB_EnvSetAttr */ SQLRETURN MADB_EnvSetAttr(MADB_Env* Env, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { MADB_CLEAR_ERROR(&Env->Error); switch (Attribute) { case SQL_ATTR_ODBC_VERSION: if (Env->Dbcs) { MADB_SetError(&Env->Error, MADB_ERR_HYC00, NULL, 0); return Env->Error.ReturnValue; } Env->OdbcVersion= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_ATTR_OUTPUT_NTS: if ((SQLINTEGER)(SQLLEN)ValuePtr != SQL_TRUE) MADB_SetError(&Env->Error, MADB_ERR_S1C00, NULL, 0); break; default: MADB_SetError(&Env->Error, MADB_ERR_HYC00, NULL, 0); break; } // LeaveCriticalSection(&Env->cs); return Env->Error.ReturnValue; } /* }}} */ /* {{{ MADB_EnvGetAttr */ SQLRETURN MADB_EnvGetAttr(MADB_Env *Env, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_CLEAR_ERROR(&Env->Error); switch (Attribute) { case SQL_ATTR_CONNECTION_POOLING: *(SQLUINTEGER *)ValuePtr = SQL_CP_OFF; break; case SQL_ATTR_ODBC_VERSION: *(SQLINTEGER *)ValuePtr= Env->OdbcVersion; break; case SQL_ATTR_OUTPUT_NTS: *(SQLINTEGER *)ValuePtr= SQL_TRUE; break; default: MADB_SetError(&Env->Error, MADB_ERR_HYC00, NULL, 0); break; } return Env->Error.ReturnValue; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_environment.h000066400000000000000000000026541414431724000212760ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013 SkySQL AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_environment_h_ #define _ma_environment_h_ MADB_Env *MADB_EnvInit(); SQLRETURN MADB_EnvFree(MADB_Env *Env); SQLRETURN MADB_EnvSetAttr(MADB_Env* Env, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength); SQLRETURN MADB_EnvGetAttr(MADB_Env *Env, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr); extern Client_Charset SourceAnsiCs; #endif /* _ma_environment_h_ */ mariadb-connector-odbc-3.1.15/ma_error.c000066400000000000000000000440741414431724000200600ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include extern Client_Charset utf8; /* {{{ MADB_ErrorList[] */ MADB_ERROR MADB_ErrorList[] = { { "00000", "", "", SQL_ERROR}, { "01000", "", "General warning", SQL_ERROR}, { "01001", "01S03", "Cursor operation conflict", SQL_ERROR}, { "01002", "", "Disconnect error", SQL_ERROR}, { "01003", "", "NULL value eliminated in set function", SQL_ERROR}, { "01004", "", "String data, right-truncated", SQL_ERROR}, { "01006", "", "Privilege not revoked", SQL_ERROR}, { "01007", "", "Privilege not granted", SQL_ERROR}, { "01S00", "", "Invalid connection string attribute", SQL_ERROR}, { "01S01", "", "Error in row", SQL_ERROR}, { "01S02", "", "Option value changed", SQL_ERROR}, { "01S06", "", "Attempt to fetch before the result set returned the first rowset", SQL_ERROR}, { "01S07", "", "Fractional truncation", SQL_ERROR}, { "01S08", "", "Error saving File DSN", SQL_ERROR}, { "01S09", "", "Invalid keyword", SQL_ERROR}, { "07001", "", "Wrong number of parameters", SQL_ERROR}, { "07002", "", "COUNT field incorrect", SQL_ERROR}, { "07005", "2400", "Prepared statement not a cursor-specification", SQL_ERROR}, { "07006", "", "Restricted data type attribute violation", SQL_ERROR}, { "07009", "S1002", "Invalid descriptor index", SQL_ERROR}, { "07S01", "", "Invalid use of default parameter", SQL_ERROR}, { "08001", "", "Client unable to establish connection", SQL_ERROR}, { "08002", "", "Connection name in use", SQL_ERROR}, { "08003", "", "Connection not open", SQL_ERROR}, { "08004", "", "Server rejected the connection", SQL_ERROR}, { "08007", "", "Connection failure during transaction", SQL_ERROR}, { "08S01", "", "Communication link failure", SQL_ERROR}, { "21S01", "", "Insert value list does not match column list", SQL_ERROR}, { "21S02", "", "Degree of derived table does not match column list", SQL_ERROR}, { "22001", "", "String data, right-truncated", SQL_ERROR}, { "22002", "", "Indicator variable required but not supplied", SQL_ERROR}, { "22003", "", "Numeric value out of range", SQL_ERROR}, { "22007", "22008", "Invalid datetime format", SQL_ERROR}, { "22008", "", "Datetime field overflow", SQL_ERROR}, { "22012", "", "Division by zero", SQL_ERROR}, { "22015", "", "Interval field overflow", SQL_ERROR}, { "22018", "22005", "Invalid character value for cast specification", SQL_ERROR}, { "22019", "", "Invalid escape character", SQL_ERROR}, { "22025", "", "Invalid escape sequence", SQL_ERROR}, { "22026", "", "String data, length mismatch", SQL_ERROR}, { "23000", "", "Integrity constraint violation", SQL_ERROR}, { "24000", "", "Invalid cursor state", SQL_ERROR}, { "25000", "", "Invalid transaction state", SQL_ERROR}, { "25S01", "", "Transaction state", SQL_ERROR}, { "25S02", "", "Transaction is still active", SQL_ERROR}, { "25S03", "", "Transaction is rolled back", SQL_ERROR}, { "28000", "", "Invalid authorization specification", SQL_ERROR}, { "34000", "", "Invalid cursor name", SQL_ERROR}, { "3C000", "", "Duplicate cursor name", SQL_ERROR}, { "3D000", "", "Invalid catalog name", SQL_ERROR}, { "3F000", "", "Invalid schema name", SQL_ERROR}, { "40001", "", "Serialization failure", SQL_ERROR}, { "40002", "", "Integrity constraint violation", SQL_ERROR}, { "40003", "", "Statement completion unknown", SQL_ERROR}, { "42000", "37000", "Syntax error or access violation", SQL_ERROR}, { "42S01", "S0001", "Base table or view already exists", SQL_ERROR}, { "42S02", "S0002", "Base table or view not found", SQL_ERROR}, { "42S11", "S0011", "Index already exists", SQL_ERROR}, { "42S12", "S0012", "Index not found", SQL_ERROR}, { "42S21", "S0021", "Column already exists", SQL_ERROR}, { "42S22", "S0022", "Column not found", SQL_ERROR}, { "44000", "", "WITH CHECK OPTION violation", SQL_ERROR}, { "HY000", "S1000", "General error", SQL_ERROR}, { "HY001", "S1001", "Memory allocation error", SQL_ERROR}, { "HY003", "S1003", "Invalid application buffer type", SQL_ERROR}, { "HY004", "S1004", "Invalid SQL data type", SQL_ERROR}, { "HY007", "S1010", "Associated statement is not prepared", SQL_ERROR}, { "HY008", "S1008", "Operation canceled", SQL_ERROR}, { "HY009", "S1009", "Invalid use of null pointer", SQL_ERROR}, { "HY010", "S1010", "Function sequence error", SQL_ERROR}, { "HY011", "S1011", "Attribute cannot be set now", SQL_ERROR}, { "HY012", "S1012", "Invalid transaction operation code", SQL_ERROR}, { "HY013", "", "Memory management error", SQL_ERROR}, { "HY014", "", "Limit on the number of handles exceeded", SQL_ERROR}, { "HY015", "", "No cursor name available", SQL_ERROR}, { "HY016", "", "Cannot modify an implementation row descriptor", SQL_ERROR}, { "HY017", "", "Invalid use of an automatically allocated descriptor handle", SQL_ERROR}, { "HY018", "70100", "Server declined cancel request", SQL_ERROR}, { "HY019", "22003", "Non-character and non-binary data sent in pieces", SQL_ERROR}, { "HY020", "", "Attempt to concatenate a null value", SQL_ERROR}, { "HY021", "", "Inconsistent descriptor information", SQL_ERROR}, { "HY024", "S1009", "Invalid attribute value", SQL_ERROR}, { "HY090", "S1009", "Invalid string or buffer length", SQL_ERROR}, { "HY091", "S1091", "Invalid descriptor field identifier", SQL_ERROR}, { "HY092", "S1092", "Invalid attribute/option identifier", SQL_ERROR}, { "HY095", "", "Function type out of range", SQL_ERROR}, { "HY096", "S1096", "Invalid information type", SQL_ERROR}, { "HY097", "S1097", "Column type out of range", SQL_ERROR}, { "HY098", "S1098", "Scope type out of range", SQL_ERROR}, { "HY099", "S1099", "Nullable type out of range", SQL_ERROR}, { "HY100", "S1100", "Uniqueness option type out of range", SQL_ERROR}, { "HY101", "S1101", "Accuracy option type out of range", SQL_ERROR}, { "HY103", "S1103", "Invalid retrieval code", SQL_ERROR}, { "HY104", "S1104", "Invalid precision or scale value", SQL_ERROR}, { "HY105", "S1105", "Invalid parameter type", SQL_ERROR}, { "HY106", "S1106", "Fetch type out of range", SQL_ERROR}, { "HY107", "S1107", "Row value out of range", SQL_ERROR}, { "HY109", "S1109", "Invalid cursor position", SQL_ERROR}, { "HY110", "S1110", "Invalid driver completion", SQL_ERROR}, { "HY111", "S1111", "Invalid bookmark value", SQL_ERROR}, { "HYC00", "S1C00", "Optional feature not implemented", SQL_ERROR}, { "HYT00", "S1T00", "Timeout expired", SQL_ERROR}, { "HYT01", "", "Connection timeout expired", SQL_ERROR}, { "IM001", "", "Driver does not support this function", SQL_ERROR}, { "IM002", "", "Data source name not found and no default driver specified", SQL_ERROR}, { "IM003", "", "Specified driver could not be loaded", SQL_ERROR}, { "IM004", "", "Driver's SQLAllocHandle on SQL_HANDLE_ENV failed", SQL_ERROR}, { "IM005", "", "Driver's SQLAllocHandle on SQL_HANDLE_DBC failed", SQL_ERROR}, { "IM006", "", "Driver's SQLSetConnectAttr failed", SQL_ERROR}, { "IM007", "", "No data source or driver specified; dialog prohibited", SQL_ERROR}, { "IM008", "", "Dialog failed", SQL_ERROR}, { "IM009", "", "Unable to load translation DL", SQL_ERROR}, { "IM010", "", "Data source name too long", SQL_ERROR}, { "IM011", "", "Driver name too long", SQL_ERROR}, { "IM012", "", "DRIVER keyword syntax error", SQL_ERROR}, { "IM013", "", "Trace file error", SQL_ERROR}, { "IM014", "", "Invalid name of File DSN", SQL_ERROR}, { "IM015", "", "Corrupt file data source", SQL_ERROR}, { "S1000", "", "General error", SQL_ERROR}, { "S1107", "", "Row value out of range", SQL_ERROR}, { "S1C00", "", "Optional feature not implemented", SQL_ERROR}, { "", "", "", -1} }; /* }}} */ char* MADB_PutErrorPrefix(MADB_Dbc *dbc, MADB_Error *error) { /* If prefix is already there - we do not write again. One shoud reset error->PrefixLen in order to force */ if (error->PrefixLen == 0) { error->PrefixLen= strlen(MARIADB_ODBC_ERR_PREFIX); strcpy_s(error->SqlErrorMsg, SQL_MAX_MESSAGE_LENGTH + 1, MARIADB_ODBC_ERR_PREFIX); if (dbc != NULL && dbc->mariadb != NULL) { error->PrefixLen += _snprintf(error->SqlErrorMsg + error->PrefixLen, SQL_MAX_MESSAGE_LENGTH + 1 - error->PrefixLen, "[%s]", mysql_get_server_info(dbc->mariadb)); } } return error->SqlErrorMsg + error->PrefixLen; } SQLRETURN MADB_SetNativeError(MADB_Error *Error, SQLSMALLINT HandleType, void *Ptr) { char *Sqlstate= NULL, *Errormsg= NULL; int NativeError= 0; switch (HandleType) { case SQL_HANDLE_DBC: Sqlstate= (char *)mysql_sqlstate((MYSQL *)Ptr); Errormsg= (char *)mysql_error((MYSQL *)Ptr); NativeError= mysql_errno((MYSQL *)Ptr); break; case SQL_HANDLE_STMT: Sqlstate= (char *)mysql_stmt_sqlstate((MYSQL_STMT *)Ptr); Errormsg= (char *)mysql_stmt_error((MYSQL_STMT *)Ptr); NativeError= mysql_stmt_errno((MYSQL_STMT *)Ptr); break; } /* work-around of probalby a bug in mariadb_stmt_execute_direct, that returns 1160 in case of lost connection */ if ((NativeError == 2013 || NativeError == 2006 || NativeError == 1160) && (strcmp(Sqlstate, "HY000") == 0 || strcmp(Sqlstate, "00000") == 0)) { Sqlstate= "08S01"; } Error->ReturnValue= SQL_ERROR; if (Errormsg) { strcpy_s(Error->SqlErrorMsg + Error->PrefixLen, SQL_MAX_MESSAGE_LENGTH + 1 - Error->PrefixLen, Errormsg); } if (Sqlstate) strcpy_s(Error->SqlState, SQLSTATE_LENGTH + 1, Sqlstate); Error->NativeError= NativeError; if (Error->SqlState[0] == '0') Error->ReturnValue= (Error->SqlState[1] == '0') ? SQL_SUCCESS : (Error->SqlState[1] == '1') ? SQL_SUCCESS_WITH_INFO : SQL_ERROR; return Error->ReturnValue; } /* {{{ MADB_SetError */ SQLRETURN MADB_SetError(MADB_Error *Error, unsigned int SqlErrorCode, const char *NativeErrorMsg, unsigned int NativeError) { unsigned int ErrorCode= SqlErrorCode; Error->ErrorNum= 0; if ((NativeError == 2013 || NativeError == 2006 || NativeError == 1160) && SqlErrorCode == MADB_ERR_HY000) ErrorCode= MADB_ERR_08S01; Error->ErrRecord= &MADB_ErrorList[ErrorCode]; Error->ReturnValue= SQL_ERROR; if (NativeErrorMsg) { strcpy_s(Error->SqlErrorMsg + Error->PrefixLen, SQL_MAX_MESSAGE_LENGTH + 1 - Error->PrefixLen, NativeErrorMsg); } else { strcpy_s(Error->SqlErrorMsg + Error->PrefixLen, SQL_MAX_MESSAGE_LENGTH + 1 - Error->PrefixLen, MADB_ErrorList[ErrorCode].SqlErrorMsg); } strcpy_s(Error->SqlState, SQLSTATE_LENGTH + 1, MADB_ErrorList[ErrorCode].SqlState); Error->NativeError= NativeError; /* Check the return code */ if (Error->SqlState[0] == '0') Error->ReturnValue= (Error->SqlState[1] == '0') ? SQL_SUCCESS : (Error->SqlState[1] == '1') ? SQL_SUCCESS_WITH_INFO : SQL_ERROR; return Error->ReturnValue; } /* }}} */ /* {{{ MADB_CopyError */ void MADB_CopyError(MADB_Error *ErrorTo, MADB_Error *ErrorFrom) { ErrorTo->NativeError= ErrorFrom->NativeError; ErrorTo->ReturnValue= ErrorFrom->ReturnValue; ErrorTo->PrefixLen= ErrorFrom->PrefixLen; strcpy_s(ErrorTo->SqlState, SQLSTATE_LENGTH + 1, ErrorFrom->SqlState); strcpy_s(ErrorTo->SqlErrorMsg, SQL_MAX_MESSAGE_LENGTH + 1, ErrorFrom->SqlErrorMsg); } /* }}} */ /* {{{ MADB_GetDiagRec */ SQLRETURN MADB_GetDiagRec(MADB_Error *Err, SQLSMALLINT RecNumber, void *SQLState, SQLINTEGER *NativeErrorPtr, void *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr, my_bool isWChar, SQLINTEGER OdbcVersion) { MADB_Error InternalError; char *SqlStateVersion= Err->SqlState; SQLSMALLINT Length= 0; InternalError.PrefixLen= 0; MADB_CLEAR_ERROR(&InternalError); if (RecNumber > 1) return SQL_NO_DATA; /* check if we have to map the SQLState to ODBC version 2 state */ if (OdbcVersion == SQL_OV_ODBC2) { int i= 0; while (MADB_ErrorList[i].SqlState[0]) { if (strcmp(Err->SqlState, MADB_ErrorList[i].SqlState) == 0) { if (MADB_ErrorList[i].SqlStateV2[0]) SqlStateVersion= MADB_ErrorList[i].SqlStateV2; break; } ++i; } } if (NativeErrorPtr) *NativeErrorPtr= Err->NativeError; if (SQLState) MADB_SetString(isWChar ? &utf8 : 0, (void *)SQLState, SQL_SQLSTATE_SIZE + 1, SqlStateVersion, SQL_SQLSTATE_SIZE, &InternalError); if (MessageText) Length= (SQLSMALLINT)MADB_SetString(isWChar ? &utf8 : 0, (void*)MessageText, BufferLength, Err->SqlErrorMsg, strlen(Err->SqlErrorMsg), &InternalError); if (TextLengthPtr) *TextLengthPtr= (SQLSMALLINT)strlen(Err->SqlErrorMsg); if (!MessageText || !BufferLength) return SQL_SUCCESS; return InternalError.ReturnValue; } /* }}} */ /* {{{ MADB_GetDiagField */ SQLRETURN MADB_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, my_bool isWChar) { MADB_Error *Err= NULL; MADB_Stmt *Stmt= NULL; MADB_Desc *Desc= NULL; MADB_Dbc *Dbc= NULL; MADB_Env *Env= NULL; MADB_Error Error; SQLLEN Length; if (StringLengthPtr) *StringLengthPtr= 0; Error.PrefixLen= 0; MADB_CLEAR_ERROR(&Error); if (RecNumber > 1) return SQL_NO_DATA; switch(HandleType) { case SQL_HANDLE_DBC: Dbc= (MADB_Dbc *)Handle; Err= &Dbc->Error; break; case SQL_HANDLE_STMT: Stmt= (MADB_Stmt *)Handle; Err= &Stmt->Error; break; case SQL_HANDLE_ENV: Env= (MADB_Env *)Handle; Err= &Env->Error; break; case SQL_HANDLE_DESC: Desc= (MADB_Desc *)Handle; Err= &Desc->Error; break; default: return SQL_INVALID_HANDLE; } switch(DiagIdentifier) { case SQL_DIAG_CURSOR_ROW_COUNT: if (!Stmt) return SQL_ERROR; *(SQLLEN *)DiagInfoPtr= (Stmt->result) ?(SQLLEN)mysql_stmt_num_rows(Stmt->stmt) : 0; break; case SQL_DIAG_DYNAMIC_FUNCTION: if (!Stmt) return SQL_ERROR; /* Todo */ break; case SQL_DIAG_DYNAMIC_FUNCTION_CODE: if (!Stmt) return SQL_ERROR; *(SQLINTEGER *)DiagInfoPtr= 0; break; case SQL_DIAG_NUMBER: *(SQLINTEGER *)DiagInfoPtr= 1; break; case SQL_DIAG_RETURNCODE: *(SQLRETURN *)DiagInfoPtr= Err->ReturnValue; break; case SQL_DIAG_ROW_COUNT: if (HandleType != SQL_HANDLE_STMT || !Stmt) return SQL_ERROR; *(SQLLEN *)DiagInfoPtr= (Stmt->stmt) ? (SQLLEN)mysql_stmt_affected_rows(Stmt->stmt) : 0; break; case SQL_DIAG_CLASS_ORIGIN: Length= MADB_SetString(isWChar ? &utf8 : 0, DiagInfoPtr, isWChar ? BufferLength / sizeof(SQLWCHAR) : BufferLength, strncmp(Err->SqlState, "IM", 2)== 0 ? "ODBC 3.0" : "ISO 9075", SQL_NTS, &Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; break; case SQL_DIAG_COLUMN_NUMBER: *(SQLINTEGER *)DiagInfoPtr= SQL_COLUMN_NUMBER_UNKNOWN; break; case SQL_DIAG_CONNECTION_NAME: /* MariaDB ODBC Driver always returns an empty string */ if (StringLengthPtr) *StringLengthPtr= 0; DiagInfoPtr= (isWChar) ? (SQLPOINTER)L"" : (SQLPOINTER)""; break; case SQL_DIAG_MESSAGE_TEXT: Length= MADB_SetString(isWChar ? &utf8 : 0, DiagInfoPtr, isWChar ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Err->SqlErrorMsg, strlen(Err->SqlErrorMsg), &Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; break; case SQL_DIAG_NATIVE: *(SQLINTEGER *)DiagInfoPtr= Err->NativeError; break; case SQL_DIAG_ROW_NUMBER: if (HandleType != SQL_HANDLE_STMT || RecNumber < 1) return SQL_ERROR; *(SQLLEN*)DiagInfoPtr= SQL_ROW_NUMBER_UNKNOWN; break; case SQL_DIAG_SERVER_NAME: { char *ServerName= ""; if (Stmt && Stmt->stmt) { mariadb_get_infov(Stmt->stmt->mysql, MARIADB_CONNECTION_HOST, (void*)&ServerName); } else if (Dbc && Dbc->mariadb) { mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_HOST, (void*)&ServerName); } Length= MADB_SetString(isWChar ? &utf8 : 0, DiagInfoPtr, isWChar ? BufferLength / sizeof(SQLWCHAR) : BufferLength, ServerName ? ServerName : "", ServerName ? strlen(ServerName) : 0, &Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; } break; case SQL_DIAG_SQLSTATE: Length= MADB_SetString(isWChar ? &utf8 : 0, DiagInfoPtr, isWChar ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Err->SqlState, strlen(Err->SqlState), &Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; break; case SQL_DIAG_SUBCLASS_ORIGIN: Length= MADB_SetString(isWChar ? &utf8 : 0, DiagInfoPtr, isWChar ? BufferLength / sizeof(SQLWCHAR) : BufferLength, "ODBC 3.0", 8, &Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; break; default: return SQL_ERROR; } if (isWChar && StringLengthPtr) *StringLengthPtr*= sizeof(SQLWCHAR); return Error.ReturnValue; } mariadb-connector-odbc-3.1.15/ma_error.h000066400000000000000000000125711414431724000200620ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2015 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_error_h_ #define _ma_error_h_ extern MADB_ERROR MADB_ErrorList[]; enum enum_madb_error { MADB_ERR_00000=0, MADB_ERR_01000, MADB_ERR_01001, MADB_ERR_01002, MADB_ERR_01003, MADB_ERR_01004, MADB_ERR_01006, MADB_ERR_01007, MADB_ERR_01S00, MADB_ERR_01S01, MADB_ERR_01S02, MADB_ERR_01S06, MADB_ERR_01S07, MADB_ERR_01S08, MADB_ERR_01S09, MADB_ERR_07001, MADB_ERR_07002, MADB_ERR_07005, MADB_ERR_07006, MADB_ERR_07009, MADB_ERR_07S01, MADB_ERR_08001, MADB_ERR_08002, MADB_ERR_08003, MADB_ERR_08004, MADB_ERR_08007, MADB_ERR_08S01, MADB_ERR_21S01, MADB_ERR_21S02, MADB_ERR_22001, MADB_ERR_22002, MADB_ERR_22003, MADB_ERR_22007, MADB_ERR_22008, MADB_ERR_22012, MADB_ERR_22015, MADB_ERR_22018, MADB_ERR_22019, MADB_ERR_22025, MADB_ERR_22026, MADB_ERR_23000, MADB_ERR_24000, MADB_ERR_25000, MADB_ERR_25S01, MADB_ERR_25S02, MADB_ERR_25S03, MADB_ERR_28000, MADB_ERR_34000, MADB_ERR_3C000, MADB_ERR_3D000, MADB_ERR_3F000, MADB_ERR_40001, MADB_ERR_40002, MADB_ERR_40003, MADB_ERR_42000, MADB_ERR_42S01, MADB_ERR_42S02, MADB_ERR_42S11, MADB_ERR_42S12, MADB_ERR_42S21, MADB_ERR_42S22, MADB_ERR_44000, MADB_ERR_HY000, MADB_ERR_HY001, MADB_ERR_HY003, MADB_ERR_HY004, MADB_ERR_HY007, MADB_ERR_HY008, MADB_ERR_HY009, MADB_ERR_HY010, MADB_ERR_HY011, MADB_ERR_HY012, MADB_ERR_HY013, MADB_ERR_HY014, MADB_ERR_HY015, MADB_ERR_HY016, MADB_ERR_HY017, MADB_ERR_HY018, MADB_ERR_HY019, MADB_ERR_HY020, MADB_ERR_HY021, MADB_ERR_HY024, MADB_ERR_HY090, MADB_ERR_HY091, MADB_ERR_HY092, MADB_ERR_HY095, MADB_ERR_HY096, MADB_ERR_HY097, MADB_ERR_HY098, MADB_ERR_HY099, MADB_ERR_HY100, MADB_ERR_HY101, MADB_ERR_HY103, MADB_ERR_HY104, MADB_ERR_HY105, MADB_ERR_HY106, MADB_ERR_HY107, MADB_ERR_HY109, MADB_ERR_HY110, MADB_ERR_HY111, MADB_ERR_HYC00, MADB_ERR_HYT00, MADB_ERR_HYT01, MADB_ERR_IM001, MADB_ERR_IM002, MADB_ERR_IM003, MADB_ERR_IM004, MADB_ERR_IM005, MADB_ERR_IM006, MADB_ERR_IM007, MADB_ERR_IM008, MADB_ERR_IM009, MADB_ERR_IM010, MADB_ERR_IM011, MADB_ERR_IM012, MADB_ERR_IM013, MADB_ERR_IM014, MADB_ERR_IM015, MADB_ERR_S1000, MADB_ERR_S1107, MADB_ERR_S1C00, }; char* MADB_PutErrorPrefix(MADB_Dbc *dbc, MADB_Error *error); SQLRETURN MADB_SetError(MADB_Error *Error, unsigned int SqlErrorCode, const char *SqlErrorMsg, unsigned int NativeError); SQLRETURN MADB_SetNativeError(MADB_Error *Error, SQLSMALLINT HandleType, void *Ptr); void MADB_CopyError(MADB_Error *ErrorTo, MADB_Error *ErrorFrom); SQLRETURN MADB_GetDiagRec(MADB_Error *Err, SQLSMALLINT RecNumber, void *SQLState, SQLINTEGER *NativeErrorPtr, void *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr, my_bool isWChar, SQLINTEGER OdbcVersion); SQLRETURN MADB_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, my_bool isWChar); #define MADB_CLEAR_ERROR(a) do { \ strcpy_s((a)->SqlState, SQL_SQLSTATE_SIZE+1, MADB_ErrorList[MADB_ERR_00000].SqlState); \ (a)->SqlErrorMsg[(a)->PrefixLen]= 0; \ (a)->NativeError= 0;\ (a)->ReturnValue= SQL_SUCCESS;\ (a)->ErrorNum= 0; \ } while (0) #define MADB_CLEAR_HANDLE_ERROR(handle_type, handle) \ switch (handle_type) { \ case SQL_HANDLE_ENV: \ MADB_CLEAR_ERROR(&((MADB_Env *)Handle)->Error); \ break; \ case SQL_HANDLE_DBC: \ MADB_CLEAR_ERROR(&((MADB_Dbc *)Handle)->Error); \ break; \ case SQL_HANDLE_STMT:\ MADB_CLEAR_ERROR(&((MADB_Stmt *)Handle)->Error); \ } #define MADB_CHECK_HANDLE_CLEAR_ERROR(handle_type, handle) \ if (handle == 0) return SQL_INVALID_HANDLE;\ MADB_CLEAR_HANDLE_ERROR(handle_type, handle) \ #define MADB_NOT_IMPLEMENTED(HANDLE)\ MADB_SetError(&(HANDLE)->Error, MADB_ERR_IM001, NULL, 0);\ return SQL_ERROR; #define MADB_CHECK_ATTRIBUTE(Handle, Attr, ValidAttrs)\ {\ SQLULEN x=1, ok=0, my_attr=(SQLULEN)(Attr);\ while (x <= ValidAttrs[0] && !ok)\ if ((ok= ValidAttrs[x++] == my_attr))\ if (!ok)\ {\ MADB_SetError(&(Handle)->Error,MADB_ERR_HY024, NULL, 0);\ return (Handle)->Error.ReturnValue;\ }\ } #endif /* _ma_error_h_ */ mariadb-connector-odbc-3.1.15/ma_helper.c000066400000000000000000001047311414431724000202030ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #include #define MADB_FIELD_IS_BINARY(_field) ((_field)->charsetnr == BINARY_CHARSETNR) void CloseMultiStatements(MADB_Stmt *Stmt) { unsigned int i; for (i=0; i < STMT_COUNT(Stmt->Query); ++i) { MDBUG_C_PRINT(Stmt->Connection, "-->closing %0x", Stmt->MultiStmts[i]); if (Stmt->MultiStmts[i] != NULL) { mysql_stmt_close(Stmt->MultiStmts[i]); } } MADB_FREE(Stmt->MultiStmts); Stmt->stmt= NULL; } MYSQL_STMT* MADB_NewStmtHandle(MADB_Stmt *Stmt) { static const my_bool UpdateMaxLength= 1; MYSQL_STMT* stmt= mysql_stmt_init(Stmt->Connection->mariadb); if (stmt != NULL) { mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &UpdateMaxLength); } else { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } return stmt; } /* Required, but not sufficient condition */ BOOL QueryIsPossiblyMultistmt(MADB_QUERY *Query) { return Query->QueryType != MADB_QUERY_CREATE_PROC && Query->QueryType != MADB_QUERY_CREATE_FUNC && Query->QueryType != MADB_QUERY_CREATE_DEFINER && Query->QueryType != MADB_NOT_ATOMIC_BLOCK; } /* Trims spaces and/or ';' at the end of query */ int SqlRtrim(char *StmtStr, int Length) { if (Length > 0) { char *end= StmtStr + Length - 1; while (end > StmtStr && (isspace(0x000000ff & *end) || *end == ';')) { *end= '\0'; --end; --Length; } } return Length; } /* Function assumes that the query is multistatement. And, e.g. STMT_COUNT(Stmt->Query) > 1 */ unsigned int GetMultiStatements(MADB_Stmt *Stmt, BOOL ExecDirect) { int i= 0; unsigned int MaxParams= 0; char *p= Stmt->Query.RefinedText; Stmt->MultiStmtNr= 0; Stmt->MultiStmts= (MYSQL_STMT **)MADB_CALLOC(sizeof(MYSQL_STMT) * STMT_COUNT(Stmt->Query)); while (p < Stmt->Query.RefinedText + Stmt->Query.RefinedLength) { Stmt->MultiStmts[i]= i == 0 ? Stmt->stmt : MADB_NewStmtHandle(Stmt); MDBUG_C_PRINT(Stmt->Connection, "-->inited&preparing %0x(%d,%s)", Stmt->MultiStmts[i], i, p); if (mysql_stmt_prepare(Stmt->MultiStmts[i], p, (unsigned long)strlen(p))) { MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->MultiStmts[i]); CloseMultiStatements(Stmt); /* Last paranoid attempt make sure that we did not have a parsing error. More to preserve "backward-compatibility" - we did this before, but before trying to prepare "multi-statement". */ if (i == 0 && Stmt->Error.NativeError !=1295 /*ER_UNSUPPORTED_PS*/) { Stmt->stmt= MADB_NewStmtHandle(Stmt); if (mysql_stmt_prepare(Stmt->stmt, STMT_STRING(Stmt), (unsigned long)strlen(STMT_STRING(Stmt)))) { MADB_STMT_CLOSE_STMT(Stmt); } else { MADB_DeleteSubqueries(&Stmt->Query); return 0; } } return 1; } if (mysql_stmt_param_count(Stmt->MultiStmts[i]) > MaxParams) { MaxParams= mysql_stmt_param_count(Stmt->MultiStmts[i]); } p+= strlen(p) + 1; ++i; } if (MaxParams) { Stmt->params= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * MaxParams); } return 0; } my_bool MADB_CheckPtrLength(SQLINTEGER MaxLength, char *Ptr, SQLINTEGER NameLen) { if(!Ptr) return TRUE; if ((NameLen == SQL_NTS && strlen(Ptr) >(size_t) MaxLength) || NameLen > MaxLength) return FALSE; return TRUE; } int MADB_GetWCharType(int Type) { switch (Type) { case SQL_CHAR: return SQL_WCHAR; case SQL_VARCHAR: return SQL_WVARCHAR; case SQL_LONGVARCHAR: return SQL_WLONGVARCHAR; default: return Type; } } int MADB_KeyTypeCount(MADB_Dbc *Connection, char *TableName, int KeyFlag) { int Count= 0; unsigned int i; char StmtStr[1024]; char *p= StmtStr; char Database[65]= {'\0'}; MADB_Stmt *Stmt= NULL; MYSQL_FIELD *Field; Connection->Methods->GetAttr(Connection, SQL_ATTR_CURRENT_CATALOG, Database, 65, NULL, FALSE); p+= _snprintf(p, 1024, "SELECT * FROM "); if (Database[0] != '\0') { p+= _snprintf(p, sizeof(StmtStr) - strlen(p), "`%s`.", Database); } p+= _snprintf(p, sizeof(StmtStr) - strlen(p), "%s LIMIT 0", TableName); if (MA_SQLAllocHandle(SQL_HANDLE_STMT, (SQLHANDLE)Connection, (SQLHANDLE*)&Stmt) == SQL_ERROR || Stmt->Methods->ExecDirect(Stmt, (char *)StmtStr, SQL_NTS) == SQL_ERROR || Stmt->Methods->Fetch(Stmt) == SQL_ERROR) { goto end; } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { Field= mysql_fetch_field_direct(Stmt->metadata, i); if (Field->flags & KeyFlag) { ++Count; } } end: if (Stmt) { Stmt->Methods->StmtFree(Stmt, SQL_DROP); } return Count; } /* {{{ MADB_CheckODBCType */ BOOL MADB_CheckODBCType(SQLSMALLINT Type) { switch(Type) { case SQL_C_CHAR: case SQL_C_WCHAR: case SQL_C_SSHORT: case SQL_C_SHORT: case SQL_C_USHORT: case SQL_C_SLONG: case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_FLOAT: case SQL_C_DOUBLE: case SQL_C_BIT: case SQL_C_STINYINT: case SQL_C_TINYINT: case SQL_C_UTINYINT: case SQL_C_SBIGINT: case SQL_C_UBIGINT: case SQL_C_BINARY: case SQL_C_TYPE_DATE: case SQL_C_TYPE_TIME: case SQL_C_TYPE_TIMESTAMP: case SQL_C_NUMERIC: #if (ODBCVER>=0x0350) case SQL_C_GUID: #endif case SQL_C_DEFAULT: return TRUE; default: return FALSE; } } /* {{{ MADB_GetTypeFromConciseType */ SQLSMALLINT MADB_GetTypeFromConciseType(SQLSMALLINT ConciseType) { switch (ConciseType) { /* todo: support for interval. currently we map only date/time types */ case SQL_C_DATE: case SQL_C_TIME: case SQL_C_TIMESTAMP: case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: return SQL_DATETIME; case SQL_C_INTERVAL_YEAR: case SQL_C_INTERVAL_YEAR_TO_MONTH: case SQL_C_INTERVAL_MONTH: case SQL_C_INTERVAL_DAY: case SQL_C_INTERVAL_DAY_TO_HOUR: case SQL_C_INTERVAL_DAY_TO_MINUTE: case SQL_C_INTERVAL_DAY_TO_SECOND: case SQL_C_INTERVAL_HOUR: case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: case SQL_C_INTERVAL_MINUTE: case SQL_C_INTERVAL_MINUTE_TO_SECOND: case SQL_C_INTERVAL_SECOND: return SQL_INTERVAL; default: return ConciseType; } } /* }}} */ /* {{{ MADB_GetTypeName */ char *MADB_GetTypeName(MYSQL_FIELD *Field) { switch(Field->type) { case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return "decimal"; case MYSQL_TYPE_NULL: return "null"; case MYSQL_TYPE_TINY: return (Field->flags & NUM_FLAG) ? "tinyint" : "char"; case MYSQL_TYPE_SHORT: return "smallint"; case MYSQL_TYPE_LONG: return "integer"; case MYSQL_TYPE_FLOAT: return "float"; case MYSQL_TYPE_DOUBLE: return "double"; case MYSQL_TYPE_TIMESTAMP: return "timestamp"; case MYSQL_TYPE_LONGLONG: return "bigint"; case MYSQL_TYPE_INT24: return "mediumint"; case MYSQL_TYPE_DATE: return "date"; case MYSQL_TYPE_TIME: return "time"; case MYSQL_TYPE_DATETIME: return "datetime"; case MYSQL_TYPE_YEAR: return "year"; case MYSQL_TYPE_NEWDATE: return "date"; case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: return MADB_FIELD_IS_BINARY(Field) ? "varbinary" : "varchar"; case MYSQL_TYPE_BIT: return "bit"; case MYSQL_TYPE_ENUM: return "enum"; case MYSQL_TYPE_SET: return "set"; case MYSQL_TYPE_TINY_BLOB: return MADB_FIELD_IS_BINARY(Field) ? "tinyblob" : "tinytext"; case MYSQL_TYPE_MEDIUM_BLOB: return MADB_FIELD_IS_BINARY(Field) ? "mediumblob" : "mediumtext"; case MYSQL_TYPE_LONG_BLOB: return MADB_FIELD_IS_BINARY(Field) ? "longblob" : "longtext"; case MYSQL_TYPE_BLOB: return MADB_FIELD_IS_BINARY(Field) ? "blob" : "text"; case MYSQL_TYPE_STRING: return MADB_FIELD_IS_BINARY(Field) ? "binary" : "char"; case MYSQL_TYPE_GEOMETRY: return "geometry"; default: return ""; } } /* }}} */ MYSQL_RES *MADB_GetDefaultColumnValues(MADB_Stmt *Stmt, MYSQL_FIELD *fields) { MADB_DynString DynStr; unsigned int i; MYSQL_RES *result= NULL; MADB_InitDynamicString(&DynStr, "SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='", 512, 512); if (MADB_DynstrAppend(&DynStr, fields[0].db) || MADB_DYNAPPENDCONST(&DynStr, "' AND TABLE_NAME='") || MADB_DynstrAppend(&DynStr, fields[0].org_table) || MADB_DYNAPPENDCONST(&DynStr, "' AND COLUMN_NAME IN (")) goto error; for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ); if (!Rec->inUse || MADB_ColumnIgnoredInAllRows(Stmt->Ard, Rec) == TRUE) { continue; } if (MADB_DynstrAppend(&DynStr, i > 0 ? ",'" : "'") || MADB_DynstrAppend(&DynStr, fields[i].org_name) || MADB_DynstrAppend(&DynStr, "'")) { goto error; } } if (MADB_DYNAPPENDCONST(&DynStr, ") AND COLUMN_DEFAULT IS NOT NULL")) goto error; LOCK_MARIADB(Stmt->Connection); if (mysql_query(Stmt->Connection->mariadb, DynStr.str)) goto error; result= mysql_store_result(Stmt->Connection->mariadb); error: UNLOCK_MARIADB(Stmt->Connection); MADB_DynstrFree(&DynStr); return result; } char *MADB_GetDefaultColumnValue(MYSQL_RES *res, const char *Column) { MYSQL_ROW row; if (res == NULL || !res->row_count) return NULL; mysql_data_seek(res, 0); while ((row= mysql_fetch_row(res))) { if (_stricmp(row[0], Column) == 0) return _strdup(row[1]); } return NULL; } SQLLEN MADB_GetDataSize(SQLSMALLINT SqlType, SQLLEN OctetLength, BOOL Unsigned, SQLSMALLINT Precision, SQLSMALLINT Scale, unsigned int CharMaxLen) { switch(SqlType) { case SQL_BIT: return 1; case SQL_TINYINT: return 3; case SQL_SMALLINT: return 5; case SQL_INTEGER: return 10; case SQL_BIGINT: return 20 - test(Unsigned != FALSE); case SQL_REAL: return 7; case SQL_DOUBLE: case SQL_FLOAT: return 15; case SQL_DECIMAL: case SQL_NUMERIC: return Precision; case SQL_TYPE_DATE: return SQL_DATE_LEN; case SQL_TYPE_TIME: return SQL_TIME_LEN + MADB_FRACTIONAL_PART(Scale); case SQL_TYPE_TIMESTAMP: return SQL_TIMESTAMP_LEN + MADB_FRACTIONAL_PART(Scale); case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: return OctetLength; case SQL_GUID: return 36;; default: { if (CharMaxLen < 2/*i.e.0||1*/) { return OctetLength; } else { return OctetLength/CharMaxLen; } } } } /* {{{ MADB_GetDisplaySize */ size_t MADB_GetDisplaySize(MYSQL_FIELD *Field, MARIADB_CHARSET_INFO *charset) { /* Todo: check these values with output from mysql --with-columntype-info */ switch (Field->type) { case MYSQL_TYPE_NULL: return 1; case MYSQL_TYPE_BIT: return (Field->length == 1) ? 1 : (Field->length + 7) / 8 * 2; case MYSQL_TYPE_TINY: return 4 - test(Field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: return 6 - test(Field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_INT24: return 9 - test(Field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_LONG: return 11 - test(Field->flags & UNSIGNED_FLAG); case MYSQL_TYPE_LONGLONG: return 20; case MYSQL_TYPE_DOUBLE: return 15; case MYSQL_TYPE_FLOAT: return 7; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: { /* The edge case like decimal(1,1)*/ size_t Precision= Field->length - test((Field->flags & UNSIGNED_FLAG) == 0) - test(Field->decimals != 0); return Field->length + test(Precision == Field->decimals); } case MYSQL_TYPE_DATE: return SQL_DATE_LEN; /* YYYY-MM-DD */ case MYSQL_TYPE_TIME: return SQL_TIME_LEN + MADB_FRACTIONAL_PART(Field->decimals); /* HH:MM:SS.ffffff */ case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: return SQL_TIMESTAMP_LEN + MADB_FRACTIONAL_PART(Field->decimals); case MYSQL_TYPE_BLOB: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_SET: case MYSQL_TYPE_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: { if (MADB_FIELD_IS_BINARY(Field)) { return Field->length*2; /* ODBC specs says we should give 2 characters per byte to display binaray data in hex form */ } else if (charset == NULL || charset->char_maxlen < 2/*i.e.0||1*/) { return Field->length; } else { return Field->length/charset->char_maxlen; } } default: return SQL_NO_TOTAL; } } /* }}} */ /* {{{ MADB_GetOctetLength */ size_t MADB_GetOctetLength(MYSQL_FIELD *Field, unsigned short MaxCharLen) { size_t Length= MIN(MADB_INT_MAX32, Field->/*max_*/length); switch (Field->type) { case MYSQL_TYPE_NULL: return 1; case MYSQL_TYPE_BIT: return (Field->length + 7) / 8; case MYSQL_TYPE_TINY: return 1; case MYSQL_TYPE_YEAR: case MYSQL_TYPE_SHORT: return 2; case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: return 4; case MYSQL_TYPE_LONGLONG: return 8; case MYSQL_TYPE_DOUBLE: return 8; case MYSQL_TYPE_FLOAT: return 4; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: { /* The edge case like decimal(1,1)*/ size_t Precision= Field->length - test((Field->flags & UNSIGNED_FLAG) == 0) - test(Field->decimals != 0); return Field->length + test(Precision == Field->decimals); } case MYSQL_TYPE_DATE: return sizeof(SQL_DATE_STRUCT); case MYSQL_TYPE_TIME: return sizeof(SQL_TIME_STRUCT); case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: return sizeof(SQL_TIMESTAMP_STRUCT); case MYSQL_TYPE_BLOB: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_TINY_BLOB: return Length; case MYSQL_TYPE_SET: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: return Length; /* Field->length is calculated using current charset */ default: return SQL_NO_TOTAL; } } /* }}} */ /* {{{ MADB_GetDefaultType */ int MADB_GetDefaultType(int SQLDataType) { switch(SQLDataType) { case SQL_BIGINT: return SQL_C_SBIGINT; case SQL_BINARY: return SQL_C_BINARY; case SQL_BIT: return SQL_C_BIT; case SQL_CHAR: return SQL_C_CHAR; case SQL_DATE: case SQL_TYPE_DATE: return SQL_C_DATE; case SQL_DECIMAL: return SQL_C_CHAR; case SQL_DOUBLE: return SQL_C_DOUBLE; case SQL_FLOAT: return SQL_C_FLOAT; case SQL_INTEGER: return SQL_C_LONG; case SQL_LONGVARBINARY: return SQL_C_BINARY; case SQL_LONGVARCHAR: return SQL_C_CHAR; case SQL_NUMERIC: return SQL_C_CHAR; case SQL_REAL: return SQL_C_FLOAT; case SQL_SMALLINT: return SQL_C_SHORT; case SQL_TIME: case SQL_TYPE_TIME: return SQL_C_TIME; case SQL_TIMESTAMP: case SQL_TYPE_TIMESTAMP: return SQL_C_TIMESTAMP; case SQL_TINYINT: return SQL_C_TINYINT; case SQL_VARBINARY: return SQL_C_BINARY; case SQL_VARCHAR: return SQL_C_CHAR; default: return SQL_C_CHAR; } } /* }}} */ /* {{{ MapMariadDbToOdbcType */ /* It's not quite right to mix here C and SQL types, even though constants are sort of equal */ SQLSMALLINT MapMariadDbToOdbcType(MYSQL_FIELD *field) { switch (field->type) { case MYSQL_TYPE_BIT: if (field->length > 1) return SQL_BINARY; return SQL_BIT; case MYSQL_TYPE_NULL: return SQL_VARCHAR; case MYSQL_TYPE_TINY: return field->flags & NUM_FLAG ? SQL_TINYINT : SQL_CHAR; case MYSQL_TYPE_YEAR: case MYSQL_TYPE_SHORT: return SQL_SMALLINT; case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: return SQL_INTEGER; case MYSQL_TYPE_FLOAT: return SQL_REAL; case MYSQL_TYPE_DOUBLE: return SQL_DOUBLE; case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: return SQL_TYPE_TIMESTAMP; case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_DATE: return SQL_TYPE_DATE; case MYSQL_TYPE_TIME: return SQL_TYPE_TIME; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: return MADB_FIELD_IS_BINARY(field) ? SQL_LONGVARBINARY : SQL_LONGVARCHAR; case MYSQL_TYPE_LONGLONG: return SQL_BIGINT; case MYSQL_TYPE_STRING: return MADB_FIELD_IS_BINARY(field) ? SQL_BINARY : SQL_CHAR; case MYSQL_TYPE_VAR_STRING: return MADB_FIELD_IS_BINARY(field) ? SQL_VARBINARY : SQL_VARCHAR; case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: return SQL_CHAR; case MYSQL_TYPE_GEOMETRY: return SQL_LONGVARBINARY; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: return SQL_DECIMAL; default: return SQL_UNKNOWN_TYPE; } } /* }}} */ /* {{{ MADB_GetTypeLength */ size_t MADB_GetTypeLength(SQLINTEGER SqlDataType, size_t Length) { switch(SqlDataType) { case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: return 1; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: return 2; case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: return sizeof(SQLINTEGER); case SQL_C_UBIGINT: case SQL_C_SBIGINT: return sizeof(long long); case SQL_C_DOUBLE: return sizeof(SQLDOUBLE); case SQL_C_FLOAT: return sizeof(SQLFLOAT); case SQL_C_NUMERIC: return sizeof(SQL_NUMERIC_STRUCT); case SQL_C_TYPE_TIME: case SQL_C_TIME: return sizeof(SQL_TIME_STRUCT); case SQL_C_TYPE_DATE: case SQL_C_DATE: return sizeof(SQL_DATE_STRUCT); case SQL_C_TYPE_TIMESTAMP: case SQL_C_TIMESTAMP: return sizeof(SQL_TIMESTAMP_STRUCT); default: return Length; } } /* }}} */ /* {{{ MADB_GetMaDBTypeAndLength */ int MADB_GetMaDBTypeAndLength(SQLINTEGER SqlDataType, my_bool *Unsigned, unsigned long *Length) { *Unsigned= 0; switch(SqlDataType) { case SQL_C_BIT: case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: *Length= 1; *Unsigned= (SqlDataType == SQL_C_UTINYINT); return MYSQL_TYPE_TINY; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: *Length= 2; *Unsigned= (SqlDataType == SQL_C_USHORT); return MYSQL_TYPE_SHORT; case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: *Length= sizeof(SQLINTEGER); *Unsigned= (SqlDataType == SQL_C_ULONG); return MYSQL_TYPE_LONG; case SQL_C_UBIGINT: case SQL_C_SBIGINT: *Length= sizeof(long long); *Unsigned= (SqlDataType == SQL_C_UBIGINT); return MYSQL_TYPE_LONGLONG; case SQL_C_DOUBLE: *Length= sizeof(SQLDOUBLE); return MYSQL_TYPE_DOUBLE; case SQL_C_FLOAT: *Length =sizeof(SQLFLOAT); return MYSQL_TYPE_FLOAT; case SQL_C_NUMERIC: /**Length= sizeof(SQL_NUMERIC_STRUCT);*/ return MYSQL_TYPE_DECIMAL; case SQL_C_TYPE_TIME: case SQL_C_TIME: *Length= sizeof(SQL_TIME_STRUCT); return MYSQL_TYPE_TIME; case SQL_C_TYPE_DATE: case SQL_C_DATE: *Length= sizeof(SQL_DATE_STRUCT); return MYSQL_TYPE_DATE; case SQL_C_TYPE_TIMESTAMP: case SQL_C_TIMESTAMP: *Length= sizeof(SQL_TIMESTAMP_STRUCT); return MYSQL_TYPE_TIMESTAMP; case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: *Length= sizeof(SQL_INTERVAL_STRUCT); return MYSQL_TYPE_TIME; case SQL_C_CHAR: return MYSQL_TYPE_STRING; default: return MYSQL_TYPE_BLOB; } } /* }}} */ void MADB_CopyOdbcTsToMadbTime(SQL_TIMESTAMP_STRUCT *Src, MYSQL_TIME *Dst) { Dst->year= Src->year; Dst->month= Src->month; Dst->day= Src->day; Dst->hour= Src->hour; Dst->minute= Src->minute; Dst->second= Src->second; Dst->second_part= Src->fraction / 1000; } void MADB_CopyMadbTimeToOdbcTs(MYSQL_TIME *Src, SQL_TIMESTAMP_STRUCT *Dst) { Dst->year= Src->year; Dst->month= Src->month; Dst->day= Src->day; Dst->hour= Src->hour; Dst->minute= Src->minute; Dst->second= Src->second; Dst->fraction= Src->second_part*1000; } SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER DataPtr, SQLLEN *Length, SQLLEN *Ind, SQLSMALLINT CType, SQLSMALLINT SqlType) { SQLLEN Dummy; Length= Length == NULL ? &Dummy : Length; switch(CType) { case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: { SQL_TIMESTAMP_STRUCT *ts= (SQL_TIMESTAMP_STRUCT *)DataPtr; if (ts != NULL) { /* If time converted to timestamp - fraction is set to 0, date is set to current date */ if (SqlType == SQL_TIME || SqlType == SQL_TYPE_TIME) { time_t sec_time; struct tm * cur_tm; sec_time= time(NULL); cur_tm= localtime(&sec_time); ts->year= 1900 + cur_tm->tm_year; ts->month= cur_tm->tm_mon + 1; ts->day= cur_tm->tm_mday; ts->fraction= 0; } else { ts->year= tm->year; ts->month= tm->month; ts->day= tm->day; ts->fraction= tm->second_part * 1000; } ts->hour= tm->hour; ts->minute= tm->minute; ts->second= tm->second; if (ts->year + ts->month + ts->day + ts->hour + ts->minute + ts->fraction + ts->second == 0) { if (Ind != NULL) { *Ind= SQL_NULL_DATA; } else { return MADB_SetError(&Stmt->Error, MADB_ERR_22002, NULL, 0); } break; } } *Length= sizeof(SQL_TIMESTAMP_STRUCT); } break; case SQL_C_TIME: case SQL_C_TYPE_TIME: { SQL_TIME_STRUCT *ts= (SQL_TIME_STRUCT *)DataPtr; if (ts != NULL) { /* tm(buffer from MYSQL_BIND) can be NULL. And that happens if ts(app's buffer) is null */ if (!VALID_TIME(tm)) { return MADB_SetError(&Stmt->Error, MADB_ERR_22007, NULL, 0); } ts->hour= tm->hour; ts->minute= tm->minute; ts->second= tm->second; *Length= sizeof(SQL_TIME_STRUCT); if (tm->second_part) { return MADB_SetError(&Stmt->Error, MADB_ERR_01S07, NULL, 0); } } } break; case SQL_C_DATE: case SQL_TYPE_DATE: { SQL_DATE_STRUCT *ts= (SQL_DATE_STRUCT *)DataPtr; if (ts != NULL) { ts->year= tm->year; ts->month= tm->month; ts->day= tm->day; if (ts->year + ts->month + ts->day == 0) { if (Ind != NULL) { *Ind= SQL_NULL_DATA; } else { return MADB_SetError(&Stmt->Error, MADB_ERR_22002, NULL, 0); } break; } } *Length= sizeof(SQL_DATE_STRUCT); } break; } return SQL_SUCCESS; } void *GetBindOffset(MADB_Desc *Desc, MADB_DescRecord *Record, void *Ptr, SQLULEN RowNumber, size_t PtrSize) { size_t BindOffset= 0; /* This is not quite clear - I'd imagine, that if BindOffset is set, then Ptr can be NULL. Makes perfect sense in case of row-based binding - setting pointers to offset in structure, and BindOffset to the begin of array. One of members would have 0 offset then. But specs are rather against that, and other drivers also don't support such interpretation */ if (Ptr == NULL) { return NULL; } if (Desc->Header.BindOffsetPtr != NULL) { BindOffset= (size_t)*Desc->Header.BindOffsetPtr; } /* row wise binding */ if (Desc->Header.BindType == SQL_BIND_BY_COLUMN || Desc->Header.BindType == SQL_PARAM_BIND_BY_COLUMN) { BindOffset+= PtrSize * RowNumber; } else { BindOffset+= Desc->Header.BindType * RowNumber; } return (char *)Ptr + BindOffset; } /* Checking if column ignored in all bound rows. Should hel*/ BOOL MADB_ColumnIgnoredInAllRows(MADB_Desc *Desc, MADB_DescRecord *Rec) { SQLULEN row; SQLLEN *IndicatorPtr; for (row= 0; row < Desc->Header.ArraySize; ++row) { IndicatorPtr= (SQLLEN *)GetBindOffset(Desc, Rec, Rec->IndicatorPtr, row, sizeof(SQLLEN)); if (IndicatorPtr == NULL || *IndicatorPtr != SQL_COLUMN_IGNORE) { return FALSE; } } return TRUE; } void MADB_NumericInit(SQL_NUMERIC_STRUCT *number, MADB_DescRecord *Ard) { if (!number) return; number->precision= (SQLCHAR)Ard->Precision; number->scale= (SQLCHAR)Ard->Scale; memset(number->val, 0, sizeof(number->val)); } /* {{{ MADB_CharToSQLNumeric */ int MADB_CharToSQLNumeric(char *buffer, MADB_Desc *Ard, MADB_DescRecord *ArdRecord, SQL_NUMERIC_STRUCT *dst_buffer, unsigned long RowNumber) { char *p; SQL_NUMERIC_STRUCT *number= dst_buffer != NULL ? dst_buffer : (SQL_NUMERIC_STRUCT *)GetBindOffset(Ard, ArdRecord, ArdRecord->DataPtr, RowNumber, ArdRecord->OctetLength); int ret= 0; if (!buffer || !number) { return ret; } p= trim(buffer); MADB_NumericInit(number, ArdRecord); /* Determining the sign of the number. From now on we dean with unsigned number */ if (!(number->sign = (*p == '-') ? 0 : 1)) { p++; } /* Empty string - nothing to do*/ if (!*p) { return ret; } if (number->precision == 0) { number->precision= MADB_DEFAULT_PRECISION; } /* Skipping leading zeroes */ while (*p == '0') { ++p; } if (*p) { int i; unsigned int bit, hval, tv, dig, sta, olen; int leading_zeros= 0; char *dot= strchr(p, '.'); char digits[100]; unsigned short digits_count= 0; /* integer part digits count*/ if (dot == NULL) { char* end= p; while (*end && isdigit(0x000000ff & *end)) ++end; digits_count= (unsigned short)(end - p); } else { digits_count= (unsigned short)(dot - p); } /* Overflow checks */ if (digits_count > MADB_DEFAULT_PRECISION + 1 ) /* 16 bytes of FF make up 39 digits number */ { return MADB_ERR_22003; } if (number->precision > 0 && digits_count > number->precision) { /* if scale is negative, and we have just enough zeroes at the end - we are fine, there is no overflow */ if (number->scale < 0 && (number->precision - number->scale) >= digits_count) { /* Checking that all digits past presision are '0'. Otherwise - overflow */ for (i = digits_count - number->precision; i > 0; --i) { if (*(p + digits_count - i) != '0') { return MADB_ERR_22003; } } } else { return MADB_ERR_22003; } } memcpy(digits, p, digits_count); if (dot && number->scale > 0) { short digits_total= 0, /* fractional part total digits */ digits_significant= 0; /* fractional part significant digits(not counting 0 at the end) */ p= dot + 1; while (*p) { /* ignore non numbers */ if (!isdigit(0x000000ff & *p)) break; ++digits_total; /* ignore trailing zeros */ if (*p != '0') { digits_significant= digits_total; } ++p; } /* Kinda tricky. let's say precision is 5.2. 1234.5 is fine, 1234.56 is overflow, 123.456 fractional overflow with rounding and warning */ if (digits_count + digits_significant > number->precision && digits_significant <= number->scale) { return MADB_ERR_22003; /* if digits are zero there is no overflow */ /*for (p= dot + 1; p <= dot + digits_significant; ++p) { if (*p != '0') }*/ } if (digits_significant > number->scale) { ret= MADB_ERR_01S07; memcpy(digits + digits_count, dot + 1, number->scale); } else { memcpy(digits + digits_count, dot + 1, digits_significant); for (i= digits_count + digits_significant; i < digits_count + number->scale; ++i) { digits[i]= '0'; } } digits_count+= number->scale; } /* Rounding */ if (number->scale < 0) { int64_t OldVal, Val; int64_t RoundNumber= (int64_t)pow(10.0, -number->scale); //if (digits_count <= number->precision) { digits[digits_count/*number->precision*/]= 0; } Val= _atoi64(digits); OldVal= Val; Val= (Val + RoundNumber / 2) / RoundNumber * RoundNumber; if (OldVal != Val) { return MADB_ERR_22003; } _snprintf(digits, sizeof(digits), "%lld", Val/RoundNumber); digits_count= (short)strlen(digits); if (digits_count > number->precision) return MADB_ERR_22003; } digits_count= MIN(digits_count, MADB_DEFAULT_PRECISION + 1); for (hval = 0, bit = 1L, sta = 0, olen = 0; sta < digits_count;) { for (dig = 0, i = sta; i < digits_count; i++) { tv = dig * 10 + digits[i] - '0'; dig = tv % 2; digits[i] = tv / 2 + '0'; if (i == sta && tv < 2) sta++; } if (dig > 0) hval |= bit; bit <<= 1; if (bit >= (1L << 8)) { if (olen >= SQL_MAX_NUMERIC_LEN) { //number->scale = sta - number->precision; ret= MADB_ERR_22003; break; } number->val[olen++] = hval; hval = 0; bit = 1L; } } if (hval != 0) { if (olen < SQL_MAX_NUMERIC_LEN) { number->val[olen++] = hval; } else { ret= MADB_ERR_22003; } } } return ret; } /* {{{ MADB_GetHexString */ size_t MADB_GetHexString(char *BinaryBuffer, size_t BinaryLength, char *HexBuffer, size_t HexLength) { const char HexDigits[]= "0123456789ABCDEF"; char *Start= HexBuffer; size_t CurrentLength= HexLength; if (!HexBuffer || !BinaryBuffer) return 0; while (BinaryLength-- && CurrentLength > 2) { *HexBuffer++=HexDigits[*BinaryBuffer >> 4]; *HexBuffer++=HexDigits[*BinaryBuffer & 0x0F]; BinaryBuffer++; CurrentLength-= 2; } *HexBuffer= 0; return (HexBuffer - Start); } SQLRETURN MADB_DaeStmt(MADB_Stmt *Stmt, SQLUSMALLINT Operation) { char *TableName= MADB_GetTableName(Stmt); char *CatalogName= MADB_GetCatalogName(Stmt); MADB_DynString DynStmt; MADB_CLEAR_ERROR(&Stmt->Error); memset(&DynStmt, 0, sizeof(MADB_DynString)); if (Stmt->DaeStmt) Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); Stmt->DaeStmt= NULL; if (!SQL_SUCCEEDED(MA_SQLAllocHandle(SQL_HANDLE_STMT, (SQLHANDLE)Stmt->Connection, (SQLHANDLE *)&Stmt->DaeStmt))) { MADB_CopyError(&Stmt->Error, &Stmt->Connection->Error); goto end; } switch(Operation) { case SQL_ADD: if (MADB_InitDynamicString(&DynStmt, "INSERT INTO ", 1024, 1024) || MADB_DynStrAppendQuoted(&DynStmt, CatalogName) || MADB_DynstrAppend(&DynStmt, ".") || MADB_DynStrAppendQuoted(&DynStmt, TableName)|| MADB_DynStrUpdateSet(Stmt, &DynStmt)) { MADB_DynstrFree(&DynStmt); return Stmt->Error.ReturnValue; } Stmt->DataExecutionType= MADB_DAE_ADD; break; case SQL_DELETE: if (MADB_InitDynamicString(&DynStmt, "DELETE FROM ", 1024, 1024) || MADB_DynStrAppendQuoted(&DynStmt, CatalogName) || MADB_DynstrAppend(&DynStmt, ".") || MADB_DynStrAppendQuoted(&DynStmt, TableName) || MADB_DynStrGetWhere(Stmt, &DynStmt, TableName, FALSE)) { MADB_DynstrFree(&DynStmt); return Stmt->Error.ReturnValue; } Stmt->DataExecutionType= MADB_DAE_DELETE; break; case SQL_UPDATE: if (MADB_InitDynamicString(&DynStmt, "UPDATE ", 1024, 1024) || MADB_DynStrAppendQuoted(&DynStmt, CatalogName) || MADB_DynstrAppend(&DynStmt, ".") || MADB_DynStrAppendQuoted(&DynStmt, TableName)|| MADB_DynStrUpdateSet(Stmt, &DynStmt)|| MADB_DynStrGetWhere(Stmt, &DynStmt, TableName, FALSE)) { MADB_DynstrFree(&DynStmt); return Stmt->Error.ReturnValue; } Stmt->DataExecutionType= MADB_DAE_UPDATE; break; } if (!SQL_SUCCEEDED(Stmt->DaeStmt->Methods->Prepare(Stmt->DaeStmt, DynStmt.str, SQL_NTS, FALSE))) { MADB_CopyError(&Stmt->Error, &Stmt->DaeStmt->Error); Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); } end: MADB_DynstrFree(&DynStmt); return Stmt->Error.ReturnValue; } int MADB_FindNextDaeParam(MADB_Desc *Desc, int InitialParam, SQLSMALLINT RowNumber) { int i; MADB_DescRecord *Record; for (i= InitialParam > -1 ? InitialParam + 1 : 0; i < Desc->Header.Count; i++) { if ((Record= MADB_DescGetInternalRecord(Desc, i, MADB_DESC_READ))) { if (Record->OctetLengthPtr) { /* Stmt->DaeRowNumber is 1 based */ SQLLEN *OctetLength = (SQLLEN *)GetBindOffset(Desc, Record, Record->OctetLengthPtr, RowNumber > 1 ? RowNumber - 1 : 0, sizeof(SQLLEN)); if (PARAM_IS_DAE(OctetLength)) { return i; } } } } return MADB_NOPARAM; } BOOL MADB_IsNumericType(SQLSMALLINT ConciseType) { switch (ConciseType) { case SQL_C_DOUBLE: case SQL_C_FLOAT: case SQL_DECIMAL: return TRUE; } return MADB_IsIntType(ConciseType); } BOOL MADB_IsIntType(SQLSMALLINT ConciseType) { switch (ConciseType) { case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: case SQL_C_UBIGINT: case SQL_C_SBIGINT: case SQL_BIGINT: return TRUE; } return FALSE; } /* Now it's more like installing result */ void MADB_InstallStmt(MADB_Stmt *Stmt, MYSQL_STMT *stmt) { Stmt->stmt= stmt; if (mysql_stmt_field_count(Stmt->stmt) == 0) { MADB_DescFree(Stmt->Ird, TRUE); Stmt->AffectedRows= mysql_stmt_affected_rows(Stmt->stmt); } else { Stmt->AffectedRows= 0; MADB_StmtResetResultStructures(Stmt); MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt)); } }mariadb-connector-odbc-3.1.15/ma_helper.h000066400000000000000000000125351414431724000202100ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_helper_h_ #define _ma_helper_h_ void CloseMultiStatements(MADB_Stmt *Stmt); MYSQL_STMT* MADB_NewStmtHandle(MADB_Stmt *Stmt); BOOL QueryIsPossiblyMultistmt(MADB_QUERY *Query); int SqlRtrim(char *StmtStr, int Length); unsigned int GetMultiStatements(MADB_Stmt *Stmt, BOOL ExecDirect); int MADB_KeyTypeCount(MADB_Dbc *Connection, char *TableName, int KeyFlag); MYSQL_RES *MADB_ReadDefaultValues(MADB_Dbc *Dbc, const char *Catalog, const char *TableName); int MADB_GetDefaultType(int SQLDataType); void MADB_CopyOdbcTsToMadbTime(SQL_TIMESTAMP_STRUCT *Src, MYSQL_TIME *Dst); void MADB_CopyMadbTimeToOdbcTs(MYSQL_TIME *Src, SQL_TIMESTAMP_STRUCT *Dst); SQLRETURN MADB_CopyMadbTimestamp(MADB_Stmt *Stmt, MYSQL_TIME *tm, SQLPOINTER DataPtr, SQLLEN *Length, SQLLEN *Ind, SQLSMALLINT CType, SQLSMALLINT SqlType); int MADB_GetWCharType(int Type); BOOL MADB_CheckODBCType(SQLSMALLINT Type); SQLSMALLINT MADB_GetTypeFromConciseType(SQLSMALLINT ConciseType); size_t MADB_GetTypeLength(SQLINTEGER SqlDataType, size_t Length); SQLLEN MADB_GetDataSize(SQLSMALLINT SqlType, SQLLEN OctetLength, BOOL Unsigned, SQLSMALLINT Precision, SQLSMALLINT Scale, unsigned int CharMaxLen); int MADB_GetMaDBTypeAndLength(SQLINTEGER SqlDataType, my_bool *Unsigned, unsigned long *Length); //char *MADB_GetDefaultColumnValue(MADB_Stmt *Stmt, char *Schema, char *TableName, char *Column); SQLSMALLINT MapMariadDbToOdbcType(MYSQL_FIELD *field); size_t MADB_GetHexString(char *BinaryBuffer, size_t BinaryLength, char *HexBuffer, size_t HexLength); size_t MADB_GetDisplaySize(MYSQL_FIELD *Field, MARIADB_CHARSET_INFO *charset); size_t MADB_GetOctetLength(MYSQL_FIELD *Field, unsigned short MaxCharLen); char * MADB_GetTypeName(MYSQL_FIELD *Field); char * ltrim(char *Str); char * trim(char *Str); my_bool MADB_CheckPtrLength(SQLINTEGER MaxLength, char *Ptr, SQLINTEGER NameLen); void * GetBindOffset(MADB_Desc *Ard, MADB_DescRecord *ArdRecord, void *Ptr, SQLULEN RowNumber, size_t PtrSize); BOOL MADB_ColumnIgnoredInAllRows(MADB_Desc *Desc, MADB_DescRecord *Rec); SQLRETURN MADB_DaeStmt(MADB_Stmt *Stmt, SQLUSMALLINT Operation); MYSQL_RES * MADB_GetDefaultColumnValues(MADB_Stmt *Stmt, MYSQL_FIELD *fields); char * MADB_GetDefaultColumnValue(MYSQL_RES *res, const char *Column); /* SQL_NUMERIC stuff */ int MADB_CharToSQLNumeric (char *buffer, MADB_Desc *Ard, MADB_DescRecord *ArdRecord, SQL_NUMERIC_STRUCT *dst_buffer, unsigned long RowNumber); void MADB_NumericInit (SQL_NUMERIC_STRUCT *number, MADB_DescRecord *Ard); int MADB_FindNextDaeParam (MADB_Desc *Desc, int InitialParam, SQLSMALLINT RowNumber); BOOL MADB_IsNumericType(SQLSMALLINT ConciseType); BOOL MADB_IsIntType (SQLSMALLINT ConciseType); /* For multistatement picks stmt handler pointed by stored index, and sets it as "current" stmt handler */ void MADB_InstallStmt (MADB_Stmt *Stmt, MYSQL_STMT *stmt); /* for dummy binding */ extern my_bool DummyError; /* Stringify macros */ #define XSTR(s) STR(s) #define STR(s) #s #define MADB_INT_MAX32 0x7FFFFFFFL #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif #ifndef test #define test(a) ((a) ? 1 : 0) #endif #define BUFFER_CHAR_LEN(blen,wchar) (wchar) ? (blen) / sizeof(SQLWCHAR) : (blen) #define MADB_FREE(a) do { \ free((a));\ (a)= NULL; \ } while(0) #define MADB_ALLOC(a) malloc((a)) #define MADB_CALLOC(a) calloc((a) > 0 ? (a) : 1, sizeof(char)) #define MADB_REALLOC(a,b) realloc((a),(b)) /* If required to free old memory pointed by current ptr, and set new value */ #define MADB_RESET(ptr, newptr) do {\ char *local_new_ptr= (newptr);\ if (local_new_ptr != ptr) {\ free((char*)(ptr));\ if (local_new_ptr != NULL)\ (ptr)= _strdup(local_new_ptr);\ else\ (ptr)= NULL;\ }\ } while(0) #define MADB_SET_NUM_VAL(TYPE, PTR, VALUE, LENGTHPTR)\ {\ if((PTR))\ *(TYPE *)(PTR)= (VALUE);\ if((LENGTHPTR))\ *(LENGTHPTR)= sizeof(TYPE);\ } #define MADB_SET_INTVAL(PTR, LEN, INTTYPE, VALUE) \ {\ if((PTR))\ *((INTTYPE *)PTR)= (VALUE); \ LEN=sizeof(INTTYPE);\ } #define MADB_IS_EMPTY(STR) ((STR)==NULL || *(STR)=='\0') #define MADB_FRACTIONAL_PART(_decimals) ((_decimals) > 0 ? (_decimals) + 1/*Decimal point*/ : 0) #endif mariadb-connector-odbc-3.1.15/ma_info.c000066400000000000000000000321671414431724000176620ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2017 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include MADB_TypeInfo TypeInfoV3[]= { {"BIT",SQL_BIT,1,"","","NULL",1,1,3,0,0,0,"BIT",0,0,0,0,10, SQL_BIT}, {"BOOL",SQL_BIT,1,"","","NULL",1,1,3,0,0,0,"BOOL",0,0,0,0,10, SQL_BIT}, {"TINYINT",SQL_TINYINT,3,"","","NULL",1,0,3,SQL_FALSE,0,1,"TINYINT",0,0,0,0,10, SQL_TINYINT}, {"TINYINT UNSIGNED",SQL_TINYINT,3,"","","NULL",1,0,3,SQL_TRUE,0,1,"TINYINT UNSIGNED",0,0,0,0,10, SQL_TINYINT}, {"BIGINT",SQL_BIGINT,19,"","","NULL",1,0,3,SQL_FALSE,0,1,"BIGINT",0,0,0,0,10, SQL_BIGINT}, {"BIGINT UNSIGNED",SQL_BIGINT,20,"","","NULL",1,0,3,1,0,1,"BIGINT UNSIGNED",0,0,0,0,10, SQL_BIGINT}, {"LONG VARBINARY",SQL_LONGVARBINARY,16777215,"''","''","NULL",1,1,3,0,0,0,"LONG VARBINARY",0,0,0,0,10, SQL_LONGVARBINARY}, {"MEDIUMBLOB",SQL_LONGVARBINARY,16777215,"''","''","NULL",1,1,3,0,0,0,"MEDIUMBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"LONGBLOB",SQL_LONGVARBINARY,2147483647,"''","''","NULL",1,1,3,0,0,0,"LONGBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"BLOB",SQL_LONGVARBINARY,65535,"''","''","NULL",1,1,3,0,0,0,"BLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"TINYBLOB",SQL_LONGVARBINARY,255,"''","''","NULL",1,1,3,0,0,0,"TINYBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"VARBINARY",SQL_VARBINARY,255,"''","''","'length'",1,1,3,0,0,0,"VARBINARY",0,0,0,0,10, SQL_VARBINARY}, {"BINARY",SQL_BINARY,255,"''","''","'length'",1,1,3,0,0,0,"BINARY",0,0,0,0,10, SQL_BINARY}, {"LONG VARCHAR",SQL_LONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"LONG VARCHAR",0,0,0,0,10, SQL_LONGVARCHAR}, {"MEDIUMTEXT",SQL_LONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"MEDIUMTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"LONGTEXT",SQL_LONGVARCHAR,2147483647,"''","''","NULL",1,0,3,0,0,0,"LONGTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"TEXT",SQL_LONGVARCHAR,65535,"''","''","NULL",1,0,3,0,0,0,"TEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"TINYTEXT",SQL_LONGVARCHAR,255,"''","''","NULL",1,0,3,0,0,0,"TINYTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"CHAR",SQL_CHAR,255,"''","''","'length'",1,0,3,0,0,0,"CHAR",0,0,0,0,10, SQL_CHAR}, {"NUMERIC",SQL_NUMERIC,65,"","","'precision,scale'",1,0,3,0,0,1,"NUMERIC", -308,308,0,0,10, SQL_NUMERIC}, /* Todo: ?? */ {"DECIMAL",SQL_DECIMAL,65,"","","'precision,scale'",1,0,3,0,0,1,"DECIMAL",-308,308,0,0,10, SQL_DECIMAL}, {"INTEGER",SQL_INTEGER,10,"","","NULL",1,0,3,SQL_FALSE,0,1,"INTEGER",0,0,0,0,10,SQL_INTEGER}, {"INTEGER UNSIGNED",SQL_INTEGER,10,"","","NULL",1,0,3,1,0,1,"INTEGER UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"INT",SQL_INTEGER,10,"","","NULL",1,0,3,SQL_FALSE,0,1,"INT",0,0,0,0,10, SQL_INTEGER}, {"INT UNSIGNED",SQL_INTEGER,10,"","","NULL",1,0,3,1,0,1,"INT UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"MEDIUMINT",SQL_INTEGER,7,"","","NULL",1,0,3,SQL_FALSE,0,1,"MEDIUMINT",0,0,0,0,10}, {"MEDIUMINT UNSIGNED",SQL_INTEGER,8,"","","NULL",1,0,3,1,0,1,"MEDIUMINT UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"SMALLINT",SQL_SMALLINT,5,"","","NULL",1,0,3,SQL_FALSE,0,1,"SMALLINT",0,0,0,0,10, SQL_SMALLINT}, {"SMALLINT UNSIGNED",SQL_SMALLINT,5,"","","NULL",1,0,3,1,0,1,"SMALLINT UNSIGNED",0,0,0,0,10, SQL_SMALLINT}, {"FLOAT",SQL_FLOAT,10,"","","'precision,scale'",1,0,3,0,0,1,"FLOAT",-38,38,0,0,10, SQL_FLOAT}, {"DOUBLE",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"DOUBLE",-308,308,0,0,10, SQL_DOUBLE}, {"DOUBLE PRECISION",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"DOUBLE PRECISION",-308,308,0,0,10, SQL_DOUBLE}, {"REAL",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"REAL",-308,308,0,0,10, SQL_DOUBLE}, {"VARCHAR",SQL_VARCHAR,255,"''","''","'length'",1,0,3,0,0,0,"VARCHAR",0,0,0,0,10, SQL_VARCHAR}, {"ENUM",SQL_VARCHAR,65535,"''","''","NULL",1,0,3,0,0,0,"ENUM",0,0,0,0,10, SQL_VARCHAR}, {"SET",SQL_VARCHAR,64,"''","''","NULL",1,0,3,0,0,0,"SET",0,0,0,0,10, SQL_VARCHAR}, {"DATE",SQL_TYPE_DATE,10,"''","''","NULL",1,0,3,0,0,0,"DATE",0,0,0,0,10, SQL_DATETIME}, {"TIME",SQL_TYPE_TIME,8,"''","''","NULL",1,0,3,0,0,0,"TIME",0,0,0,0,10, SQL_DATETIME}, {"DATETIME",SQL_TYPE_TIMESTAMP,16,"''","''","NULL",1,0,3,0,0,0,"DATETIME",0,0,0,0,10, SQL_DATETIME}, {"TIMESTAMP",SQL_TYPE_TIMESTAMP,16,"''","''","'scale'",1,0,3,0,0,0,"TIMESTAMP",0,0,0,0,10, SQL_DATETIME}, {"CHAR",SQL_WCHAR,255,"''","''","'length'",1,0,3,0,0,0,"CHAR",0,0,0,0,10, SQL_WCHAR}, {"VARCHAR",SQL_WVARCHAR,255,"''","''","'length'",1,0,3,0,0,0,"VARCHAR",0,0,0,0,10, SQL_WVARCHAR}, {"LONG VARCHAR",SQL_WLONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"LONG VARCHAR",0,0,0,0,10, SQL_LONGVARCHAR}, {NULL,0,0,NULL,NULL,NULL,0,0,0,0,0,0,NULL,0,0,0,0,0} }; MADB_TypeInfo TypeInfoV2[]= { {"BIT",SQL_BIT,1,"","","NULL",1,1,3,0,0,0,"BIT",0,0,0,0,10, SQL_BIT}, {"BOOL",SQL_BIT,1,"","","NULL",1,1,3,0,0,0,"BOOL",0,0,0,0,10, SQL_BIT}, {"TINYINT",SQL_TINYINT,3,"","","NULL",1,0,3,SQL_FALSE,0,1,"TINYINT",0,0,0,0,10, SQL_TINYINT}, {"TINYINT UNSIGNED",SQL_TINYINT,3,"","","NULL",1,0,3,SQL_FALSE,0,1,"TINYINT UNSIGNED",0,0,0,0,10, SQL_TINYINT}, {"BIGINT",SQL_BIGINT,19,"","","NULL",1,0,3,SQL_FALSE,0,1,"BIGINT",0,0,0,0,10, SQL_BIGINT}, {"BIGINT UNSIGNED",SQL_BIGINT,20,"","","NULL",1,0,3,SQL_TRUE,0,1,"BIGINT UNSIGNED",0,0,0,0,10, SQL_BIGINT}, {"LONG VARBINARY",SQL_LONGVARBINARY,16777215,"''","''","NULL",1,1,3,0,0,0,"LONG VARBINARY",0,0,0,0,10, SQL_LONGVARBINARY}, {"MEDIUMBLOB",SQL_LONGVARBINARY,16777215,"''","''","NULL",1,1,3,0,0,0,"MEDIUMBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"LONGBLOB",SQL_LONGVARBINARY,2147483647,"''","''","NULL",1,1,3,0,0,0,"LONGBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"BLOB",SQL_LONGVARBINARY,65535,"''","''","NULL",1,1,3,0,0,0,"BLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"TINYBLOB",SQL_LONGVARBINARY,255,"''","''","NULL",1,1,3,0,0,0,"TINYBLOB",0,0,0,0,10, SQL_LONGVARBINARY}, {"VARBINARY",SQL_VARBINARY,255,"''","''","'length'",1,1,3,0,0,0,"VARBINARY",0,0,0,0,10, SQL_VARBINARY}, {"BINARY",SQL_BINARY,255,"''","''","'length'",1,1,3,0,0,0,"BINARY",0,0,0,0,10, SQL_BINARY}, {"LONG VARCHAR",SQL_LONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"LONG VARCHAR",0,0,0,0,10, SQL_LONGVARCHAR}, {"MEDIUMTEXT",SQL_LONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"MEDIUMTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"LONGTEXT",SQL_LONGVARCHAR,2147483647,"''","''","NULL",1,0,3,0,0,0,"LONGTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"TEXT",SQL_LONGVARCHAR,65535,"''","''","NULL",1,0,3,0,0,0,"TEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"TINYTEXT",SQL_LONGVARCHAR,255,"''","''","NULL",1,0,3,0,0,0,"TINYTEXT",0,0,0,0,10, SQL_LONGVARCHAR}, {"CHAR",SQL_CHAR,255,"''","''","'length'",1,0,3,0,0,0,"CHAR",0,0,0,0,10, SQL_CHAR}, {"NUMERIC",SQL_NUMERIC,65,"","","'precision,scale'",1,0,3,0,0,1,"NUMERIC", -308,308,0,0,10, SQL_NUMERIC}, /* Todo: ?? */ {"DECIMAL",SQL_DECIMAL,65,"","","'precision,scale'",1,0,3,0,0,1,"DECIMAL",-308,308,0,0,10, SQL_DECIMAL}, {"INTEGER",SQL_INTEGER,10,"","","NULL",1,0,3,SQL_FALSE,0,1,"INTEGER",0,0,0,0,10,SQL_INTEGER}, {"INTEGER UNSIGNED",SQL_INTEGER,10,"","","NULL",1,0,3,1,0,1,"INTEGER UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"INT",SQL_INTEGER,10,"","","NULL",1,0,3,SQL_FALSE,0,1,"INT",0,0,0,0,10, SQL_INTEGER}, {"INT UNSIGNED",SQL_INTEGER,10,"","","NULL",1,0,3,1,0,1,"INT UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"MEDIUMINT",SQL_INTEGER,7,"","","NULL",1,0,3,SQL_FALSE,0,1,"MEDIUMINT",0,0,0,0,10}, {"MEDIUMINT UNSIGNED",SQL_INTEGER,8,"","","NULL",1,0,3,1,0,1,"MEDIUMINT UNSIGNED",0,0,0,0,10, SQL_INTEGER}, {"SMALLINT",SQL_SMALLINT,5,"","","NULL",1,0,3,SQL_FALSE,0,1,"SMALLINT",0,0,0,0,10, SQL_SMALLINT}, {"SMALLINT UNSIGNED",SQL_SMALLINT,5,"","","NULL",1,0,3,1,0,1,"SMALLINT UNSIGNED",0,0,0,0,10, SQL_SMALLINT}, {"FLOAT",SQL_FLOAT,10,"","","'precision,scale'",1,0,3,0,0,1,"FLOAT",-38,38,0,0,10, SQL_FLOAT}, {"DOUBLE",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"DOUBLE",-308,308,0,0,10, SQL_DOUBLE}, {"DOUBLE PRECISION",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"DOUBLE PRECISION",-308,308,0,0,10, SQL_DOUBLE}, {"REAL",SQL_DOUBLE,17,"","","'precision,scale'",1,0,3,0,0,1,"REAL",-308,308,0,0,10, SQL_DOUBLE}, {"VARCHAR",SQL_VARCHAR,255,"''","''","'length'",1,0,3,0,0,0,"VARCHAR",0,0,0,0,10, SQL_VARCHAR}, {"ENUM",SQL_VARCHAR,65535,"''","''","NULL",1,0,3,0,0,0,"ENUM",0,0,0,0,10, SQL_VARCHAR}, {"SET",SQL_VARCHAR,64,"''","''","NULL",1,0,3,0,0,0,"SET",0,0,0,0,10, SQL_VARCHAR}, {"DATE",SQL_DATE,10,"''","''","NULL",1,0,3,0,0,0,"DATE",0,0,0,0,10, SQL_DATETIME}, {"TIME",SQL_TIME,18,"''","''","NULL",1,0,3,0,0,0,"TIME",0,0,0,0,10, SQL_DATETIME}, {"DATETIME",SQL_TIMESTAMP,27,"''","''","NULL",1,0,3,0,0,0,"DATETIME",0,0,0,0,10, SQL_DATETIME}, {"TIMESTAMP",SQL_TIMESTAMP,27,"''","''","'scale'",1,0,3,0,0,0,"TIMESTAMP",0,0,0,0,10, SQL_DATETIME}, {"CHAR",SQL_WCHAR,255,"''","''","'length'",1,0,3,0,0,0,"CHAR",0,0,0,0,10, SQL_WCHAR}, {"VARCHAR",SQL_WVARCHAR,255,"''","''","'length'",1,0,3,0,0,0,"VARCHAR",0,0,0,0,10, SQL_WVARCHAR}, {"LONG VARCHAR",SQL_WLONGVARCHAR,16777215,"''","''","NULL",1,0,3,0,0,0,"LONG VARCHAR",0,0,0,0,10, SQL_WLONGVARCHAR}, {NULL,0,0,NULL,NULL,NULL,0,0,0,0,0,0,NULL,0,0,0,0,0} }; static MADB_ShortTypeInfo gtiDefType[19]= {{0, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {SQL_INTEGER, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, /*7*/ {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, /*11*/ {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {0, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, /*16*/ {SQL_SMALLINT, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0}, {SQL_INTEGER, 0, 0, 0}, {SQL_SMALLINT, 0, 0, 0} }; /* {{{ MADB_GetTypeInfo */ SQLRETURN MADB_GetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; my_bool isFirst= TRUE; char StmtStr[5120]; char *p= StmtStr; int i; MADB_TypeInfo *TypeInfo= TypeInfoV3; if (Stmt->Connection->Environment->OdbcVersion == SQL_OV_ODBC2) { TypeInfo= TypeInfoV2; /* We need to map time types */ switch(DataType) { case SQL_TYPE_TIMESTAMP: DataType=SQL_TIMESTAMP; break; case SQL_TYPE_DATE: DataType= SQL_DATE; break; case SQL_TYPE_TIME: DataType= SQL_TIME; break; default: break; } } StmtStr[0]= 0; for (i=0;TypeInfo[i].TypeName; i++) { if (DataType == SQL_ALL_TYPES || TypeInfo[i].DataType == DataType) { if(isFirst) { isFirst= FALSE; p+= _snprintf(p, 5120 - strlen(StmtStr), "SELECT '%s' AS TYPE_NAME, %d AS DATA_TYPE, %lu AS COLUMN_SIZE, '%s' AS LITERAL_PREFIX, " "'%s' AS LITERAL_SUFFIX, %s AS CREATE_PARAMS, %d AS NULLABLE, %d AS CASE_SENSITIVE, " "%d AS SEARCHABLE, %d AS UNSIGNED_ATTRIBUTE, %d AS FIXED_PREC_SCALE, %d AS AUTO_UNIQUE_VALUE, " "'%s' AS LOCAL_TYPE_NAME, %d AS MINIMUM_SCALE, %d AS MAXIMUM_SCALE, " "%d AS SQL_DATA_TYPE, " "%d AS SQL_DATETIME_SUB, %d AS NUM_PREC_RADIX, NULL AS INTERVAL_PRECISION ", TypeInfo[i].TypeName,TypeInfo[i].DataType,TypeInfo[i].ColumnSize,TypeInfo[i].LiteralPrefix, TypeInfo[i].LiteralSuffix,TypeInfo[i].CreateParams,TypeInfo[i].Nullable,TypeInfo[i].CaseSensitive, TypeInfo[i].Searchable,TypeInfo[i].Unsigned,TypeInfo[i].FixedPrecScale,TypeInfo[i].AutoUniqueValue, TypeInfo[i].LocalTypeName,TypeInfo[i].MinimumScale,TypeInfo[i].MaximumScale, TypeInfo[i].SqlDataType, TypeInfo[i].SqlDateTimeSub,TypeInfo[i].NumPrecRadix); } else p+= _snprintf(p, 5120 - strlen(StmtStr), "UNION SELECT '%s', %d, %lu , '%s', " "'%s', %s, %d, %d, " "%d, %d, %d, %d, " "'%s', %d, %d, " "%d, " "%d, %d, NULL ", TypeInfo[i].TypeName,TypeInfo[i].DataType,TypeInfo[i].ColumnSize,TypeInfo[i].LiteralPrefix, TypeInfo[i].LiteralSuffix,TypeInfo[i].CreateParams,TypeInfo[i].Nullable,TypeInfo[i].CaseSensitive, TypeInfo[i].Searchable,TypeInfo[i].Unsigned,TypeInfo[i].FixedPrecScale,TypeInfo[i].AutoUniqueValue, TypeInfo[i].LocalTypeName,TypeInfo[i].MinimumScale,TypeInfo[i].MaximumScale, TypeInfo[i].SqlDataType, TypeInfo[i].SqlDateTimeSub,TypeInfo[i].NumPrecRadix); } } ret= Stmt->Methods->ExecDirect(Stmt, StmtStr, SQL_NTS); if (SQL_SUCCEEDED(ret)) { MADB_FixColumnDataTypes(Stmt, gtiDefType); } return ret; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_info.h000066400000000000000000000033061414431724000176600ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_info_h_ #define _ma_info_h_ typedef struct { char *TypeName; SQLSMALLINT DataType; SQLINTEGER ColumnSize; char *LiteralPrefix; char *LiteralSuffix; char *CreateParams; SQLSMALLINT Nullable; SQLSMALLINT CaseSensitive; SQLSMALLINT Searchable; SQLSMALLINT Unsigned; SQLSMALLINT FixedPrecScale; SQLSMALLINT AutoUniqueValue; char *LocalTypeName; SQLSMALLINT MinimumScale; SQLSMALLINT MaximumScale; SQLSMALLINT SqlDataType1; SQLSMALLINT SqlDateTimeSub; SQLINTEGER NumPrecRadix; SQLSMALLINT IntervalPrecision; SQLSMALLINT SqlDataType; } MADB_TypeInfo; SQLRETURN MADB_GetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType); #endif /* _ma_info_h_ */ mariadb-connector-odbc-3.1.15/ma_legacy_helpers.c000066400000000000000000000223351414431724000217110ustar00rootroot00000000000000/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA */ /******** * * Code for lists, MADB_DynArray, and MADB_DynString copied (from C/C * ********/ /* *************** Code for handling dubble-linked lists in C ********* */ #include /* How much overhead does malloc have. The code often allocates something like 1024-MALLOC_OVERHEAD bytes */ #define MALLOC_OVERHEAD 8 /* Add a element to start of list */ MADB_List *MADB_ListAdd(MADB_List *root, MADB_List *element) { if (root) { if (root->prev) /* If add in mid of list */ root->prev->next= element; element->prev=root->prev; root->prev=element; } else element->prev=0; element->next=root; return(element); /* New root */ } MADB_List *MADB_ListDelete(MADB_List *root, MADB_List *element) { if (element->prev) element->prev->next=element->next; else root=element->next; if (element->next) element->next->prev=element->prev; return root; } void MADB_ListFree(MADB_List *root, unsigned int free_data) { MADB_List *next; while (root) { next=root->next; if (free_data) free(root->data); free(root); root=next; } } MADB_List *MADB_ListCons(void *data, MADB_List *list) { MADB_List *new_charset=(MADB_List*) malloc(sizeof(MADB_List)); if (!new_charset) return 0; new_charset->data=data; return MADB_ListAdd(list,new_charset); } MADB_List *MADB_ListReverse(MADB_List *root) { MADB_List *last; last=root; while (root) { last=root; root=root->next; last->next=last->prev; last->prev=root; } return last; } unsigned int MADB_ListLength(MADB_List *list) { unsigned int count; for (count=0 ; list ; list=list->next, count++) ; return count; } int MADB_ListWalk(MADB_List *list, MADB_ListWalkAction action, char * argument) { int error=0; while (list) { if ((error = (*action)(list->data,argument))) return error; list= MADB_LIST_REST(list); } return 0; } /************************** MADB_DynString ***************************/ /* Code for handling strings with can grow dynamicly. Copyright Monty Program KB. By monty. */ my_bool MADB_InitDynamicString(MADB_DynString *str, const char *init_str, size_t init_alloc, size_t alloc_increment) { unsigned int length; if (!alloc_increment) alloc_increment=128; length=1; if (init_str && (length= (unsigned int) strlen(init_str)+1) < init_alloc) init_alloc=((length+alloc_increment-1)/alloc_increment)*alloc_increment; if (!init_alloc) init_alloc=alloc_increment; if (!(str->str=(char*) malloc(init_alloc))) return(TRUE); str->length=length-1; if (init_str) memcpy(str->str,init_str,length); str->max_length=init_alloc; str->alloc_increment=alloc_increment; return(FALSE); } my_bool MADB_DynstrSet(MADB_DynString *str, const char *init_str) { unsigned int length; if (init_str && (length= (unsigned int) strlen(init_str)+1) > str->max_length) { str->max_length=((length+str->alloc_increment-1)/str->alloc_increment)* str->alloc_increment; if (!str->max_length) str->max_length=str->alloc_increment; if (!(str->str=(char*) realloc(str->str,str->max_length))) return(TRUE); } if (init_str) { str->length=length-1; memcpy(str->str,init_str,length); } else str->length=0; return(FALSE); } my_bool MADB_DynstrRealloc(MADB_DynString *str, size_t additional_size) { if (!additional_size) return(FALSE); if (str->length + additional_size > str->max_length) { str->max_length=((str->length + additional_size+str->alloc_increment-1)/ str->alloc_increment)*str->alloc_increment; if (!(str->str=(char*) realloc(str->str,str->max_length))) return(TRUE); } return(FALSE); } my_bool MADB_DynstrAppend(MADB_DynString *str, const char *append) { return MADB_DynstrAppendMem(str,append,strlen(append)); } my_bool MADB_DynstrAppendMem(MADB_DynString *str, const char *append, size_t length) { char *new_ptr; if (str->length+length >= str->max_length) { size_t new_length=(str->length+length+str->alloc_increment)/ str->alloc_increment; new_length*=str->alloc_increment; if (!(new_ptr=(char*) realloc(str->str,new_length))) return TRUE; str->str=new_ptr; str->max_length=new_length; } memcpy(str->str + str->length,append,length); str->length+=length; str->str[str->length]=0; /* Safety for C programs */ return FALSE; } void MADB_DynstrFree(MADB_DynString *str) { if (str->str) { free(str->str); str->str=0; } } char *MADB_DynstrMake(register char *dst, register const char *src, size_t length) { while (length--) if (! (*dst++ = *src++)) return dst-1; *dst=0; return dst; } /************* MADB_DynArray - Handling of arrays that can grow dynamicly. **************/ #undef SAFEMALLOC /* Problems with threads */ /* Initiate array and alloc space for init_alloc elements. Array is usable even if space allocation failed */ my_bool MADB_InitDynamicArray(MADB_DynArray *array, unsigned int element_size, unsigned int init_alloc, unsigned int alloc_increment) { if (!alloc_increment) { alloc_increment=MAX((8192-MALLOC_OVERHEAD)/element_size,16); if (init_alloc > 8 && alloc_increment > init_alloc * 2) alloc_increment=init_alloc*2; } if (!init_alloc) init_alloc=alloc_increment; array->elements=0; array->max_element=init_alloc; array->alloc_increment=alloc_increment; array->size_of_element=element_size; if (!(array->buffer=(char*) malloc(element_size*init_alloc))) { array->max_element=0; return(TRUE); } return(FALSE); } my_bool MADB_InsertDynamic(MADB_DynArray *array, void *element) { void *buffer; if (array->elements == array->max_element) { /* Call only when nessesary */ if (!(buffer=MADB_AllocDynamic(array))) return TRUE; } else { buffer=array->buffer+(array->elements * array->size_of_element); array->elements++; } memcpy(buffer,element,(size_t) array->size_of_element); return FALSE; } /* Alloc room for one element */ unsigned char *MADB_AllocDynamic(MADB_DynArray *array) { if (array->elements == array->max_element) { char *new_ptr; if (!(new_ptr=(char*) realloc(array->buffer,(array->max_element+ array->alloc_increment)* array->size_of_element))) return 0; array->buffer=new_ptr; array->max_element+=array->alloc_increment; } return (unsigned char *)array->buffer+(array->elements++ * array->size_of_element); } /* remove last element from array and return it */ unsigned char *MADB_PopDynamic(MADB_DynArray *array) { if (array->elements) return (unsigned char *)array->buffer+(--array->elements * array->size_of_element); return 0; } my_bool MADB_SetDynamic(MADB_DynArray *array, void * element, unsigned int idx) { if (idx >= array->elements) { if (idx >= array->max_element) { unsigned int size; char *new_ptr; size=(idx+array->alloc_increment)/array->alloc_increment; size*= array->alloc_increment; if (!(new_ptr=(char*) realloc(array->buffer,size* array->size_of_element))) return TRUE; array->buffer=new_ptr; array->max_element=size; } memset((array->buffer+array->elements*array->size_of_element), 0, (idx - array->elements)*array->size_of_element); array->elements=idx+1; } memcpy(array->buffer+(idx * array->size_of_element),element, (size_t) array->size_of_element); return FALSE; } void MADB_GetDynamic(MADB_DynArray *array, void * element, unsigned int idx) { if (idx >= array->elements) { memset(element, 0, array->size_of_element); return; } memcpy(element,array->buffer+idx*array->size_of_element, (size_t) array->size_of_element); } void MADB_DeleteDynamic(MADB_DynArray *array) { if (array->buffer) { free(array->buffer); array->buffer=0; array->elements=array->max_element=0; } } void MADB_DeleteDynamicElement(MADB_DynArray *array, unsigned int idx) { char *ptr=array->buffer+array->size_of_element*idx; array->elements--; memmove(ptr,ptr+array->size_of_element, (array->elements-idx)*array->size_of_element); } void MADB_FreezeSizeDynamic(MADB_DynArray *array) { unsigned int elements=MAX(array->elements,1); if (array->buffer && array->max_element != elements) { array->buffer=(char*) realloc(array->buffer, elements*array->size_of_element); array->max_element=elements; } } mariadb-connector-odbc-3.1.15/ma_legacy_helpers.h000066400000000000000000000075071414431724000217220ustar00rootroot00000000000000/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA */ #ifndef _MA_LEGACY_HELPERS_H #define _MA_LEGACY_HELPERS_H /******** * * Function definitions, types, macros for MADB_List, MADB_DynArray, and MADB_DynString copied from C/C * ********/ /* *************** Dubble-linked lists ********* */ typedef struct st_ma_odbc_list { struct st_ma_odbc_list *prev, *next; void *data; } MADB_List; typedef int(*MADB_ListWalkAction)(void *, void *); /* Add a element to start of list */ MADB_List *MADB_ListAdd(MADB_List *root, MADB_List *element); MADB_List *MADB_ListDelete(MADB_List *root, MADB_List *element); void MADB_ListFree(MADB_List *root, unsigned int free_data); MADB_List *MADB_ListCons(void *data, MADB_List *list); MADB_List *MADB_ListReverse(MADB_List *root); unsigned int MADB_ListLength(MADB_List *list); int MADB_ListWalk(MADB_List *list, MADB_ListWalkAction action, char *argument); #define MADB_LIST_REST(a) ((a)->next) #define MADB_LIST_PUSH(a,b) (a)=MADB_ListCons((b),(a)) #define MADB_LIST_POP(A) {MADB_List *old=(A); (A)=MADB_ListDelete(old,old) ; free((char *) old); } /************************** MADB_DynString ************************/ typedef struct st_ma_odbc_dynstr { char *str; size_t length,max_length,alloc_increment; } MADB_DynString; my_bool MADB_InitDynamicString(MADB_DynString *str, const char *init_str, size_t init_alloc, size_t alloc_increment); my_bool MADB_DynstrSet(MADB_DynString *str, const char *init_str); my_bool MADB_DynstrRealloc(MADB_DynString *str, size_t additional_size); my_bool MADB_DynstrAppend(MADB_DynString *str, const char *append); my_bool MADB_DynstrAppendMem(MADB_DynString *str, const char *append, size_t length); void MADB_DynstrFree(MADB_DynString *str); char *MADB_DynstrMake(register char *dst, register const char *src, size_t length); /* Helper-macro to slightly optimize appending of constant strings */ #define MADB_DYNAPPENDCONST(DYNSTR, LITERALCONST) MADB_DynstrAppendMem(DYNSTR, LITERALCONST, sizeof(LITERALCONST) - 1) /************* MADB_DynArray - Arrays that can grow dynamicly. **************/ typedef struct ma_odbc_st_dynarr { char *buffer; unsigned int elements,max_element; unsigned int alloc_increment; unsigned int size_of_element; } MADB_DynArray; my_bool MADB_InitDynamicArray(MADB_DynArray *array, unsigned int element_size, unsigned int init_alloc, unsigned int alloc_increment); my_bool MADB_InsertDynamic(MADB_DynArray *array, void *element); /* Alloc room for one element */ unsigned char *MADB_AllocDynamic(MADB_DynArray *array); /* remove last element from array and return it */ unsigned char *MADB_PopDynamic(MADB_DynArray *array); my_bool MADB_SetDynamic(MADB_DynArray *array, void * element, unsigned int idx); void MADB_GetDynamic(MADB_DynArray *array, void * element, unsigned int idx); void MADB_DeleteDynamic(MADB_DynArray *array); void MADB_DeleteDynamicElement(MADB_DynArray *array, unsigned int idx); void MADB_FreezeSizeDynamic(MADB_DynArray *array); #endif /* #ifndef _MA_LEGACY_HELPERS_H */ mariadb-connector-odbc-3.1.15/ma_odbc.h000066400000000000000000000334061414431724000176400ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_odbc_h_ #define _ma_odbc_h_ #include #ifdef _WIN32 # include "ma_platform_win32.h" #else # include "ma_platform_posix.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct st_ma_odbc_connection MADB_Dbc; typedef struct st_ma_odbc_stmt MADB_Stmt; typedef struct st_ma_odbc_error { char SqlState[SQL_SQLSTATE_SIZE + 1]; char SqlStateV2[SQLSTATE_LENGTH + 1]; char SqlErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1]; SQLRETURN ReturnValue; } MADB_ERROR; typedef struct { size_t PrefixLen; MADB_ERROR *ErrRecord; SQLINTEGER NativeError; /* Order number of last requested error record */ unsigned int ErrorNum; char SqlState[SQLSTATE_LENGTH + 1]; SQLRETURN ReturnValue; char SqlErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1]; } MADB_Error; typedef struct { SQLUINTEGER TargetType; SQLPOINTER TargetValuePtr; SQLLEN BufferLength; SQLLEN Utf8BufferLength; SQLLEN *StrLen_or_Ind; void *InternalBuffer; /* used for conversion */ } MADB_ColBind; typedef struct { SQLSMALLINT InputOutputType; SQLSMALLINT ValueType; SQLSMALLINT ParameterType; SQLULEN ColumnSize; SQLSMALLINT DecimalDigits; SQLPOINTER ParameterValuePtr; SQLLEN BufferLength; SQLLEN *StrLen_or_IndPtr; void *InternalBuffer; /* used for conversion */ } MADB_ParmBind; typedef struct { /* Header */ SQLSMALLINT AllocType; SQLULEN ArraySize; SQLUSMALLINT *ArrayStatusPtr; SQLULEN *BindOffsetPtr; SQLULEN BindType; SQLSMALLINT Count; /* TODO: In IPD this is SQLUINTEGER* field */ SQLULEN *RowsProcessedPtr; /* Header end */ } MADB_Header; typedef struct { SQLUINTEGER BindSize; /* size of each structure if using * Row-wise Binding */ SQLUSMALLINT *RowOperationPtr; SQLULEN *RowOffsetPtr; MADB_ColBind *ColumnBind; MYSQL_BIND *Bind; SQLSMALLINT Allocated; } MADB_Ard; typedef struct { SQLLEN ParamsetSize; SQLUINTEGER ParamBindType; SQLUSMALLINT *ParamOperationPtr; SQLULEN *ParamOffsetPtr; MADB_ParmBind *ParamBind; MYSQL_BIND *Bind; SQLSMALLINT Allocated; SQLLEN Dummy; /* dummy item to fit APD to ARD */ } MADB_Apd; typedef struct { MADB_Stmt *stmt; SQLULEN *RowsFetched; SQLUSMALLINT *RowStatusArray; SQLUINTEGER FieldCount; SQLSMALLINT Allocated; MYSQL_FIELD *Fields; } MADB_Ird; typedef struct { MADB_Header Header; #if (ODBCVER >= 0x0300) SQLUINTEGER *ParamProcessedPtr; #else SQLULEN *ParamProcessedPtr; /* SQLParamOptions */ #endif /* ODBCVER */ SQLUSMALLINT *ParamStatusPtr; SQLSMALLINT Allocated; MADB_ParmBind *Parameters; } MADB_Ipd; typedef struct { SQLINTEGER AutoUniqueValue; char *BaseCatalogName; char *BaseColumnName; char *BaseTableName; SQLINTEGER CaseSensitive; char *CatalogName; char *ColumnName; SQLSMALLINT ConciseType; SQLPOINTER DataPtr; SQLSMALLINT DateTimeIntervalCode; SQLINTEGER DateTimeIntervalPrecision; SQLINTEGER DescLength; SQLLEN DisplaySize; SQLSMALLINT FixedPrecScale; SQLLEN *IndicatorPtr; char *Label; SQLULEN Length; char *LiteralPrefix; char *LiteralSuffix; char *LocalTypeName; SQLSMALLINT Nullable; SQLINTEGER NumPrecRadix; SQLLEN OctetLength; SQLLEN *OctetLengthPtr; SQLSMALLINT ParameterType; SQLSMALLINT Precision; SQLSMALLINT RowVer; SQLSMALLINT Scale; char *SchemaName; SQLSMALLINT Searchable; char *TableName; SQLSMALLINT Type; char *TypeName; SQLSMALLINT Unnamed; SQLSMALLINT Unsigned; SQLSMALLINT Updateable; unsigned long InternalLength; /* This to be used in the MYSQL_BIND. Thus is the type */ char *InternalBuffer; /* used for internal conversion */ char *DefaultValue; char *DaeData; SQLULEN DaeDataLength; /* Doesn't seem to be used anywhere */ my_bool PutData; my_bool inUse; my_bool TruncError; } MADB_DescRecord; typedef struct { MADB_Header Header; SQLINTEGER DescType; /* SQL_ATTR_APP_ROW_DESC or SQL_ATTR_APP_PARAM_DESC */ my_bool AppType; /* Allocated by Application ? */ MADB_DynArray Records; MADB_DynArray Stmts; MADB_Error Error; MADB_Dbc * Dbc; /* Disconnect must automatically free allocated descriptors. Thus descriptor has to know the connection it is allocated on */ MADB_List ListItem; /* To store in the dbc */ union { MADB_Ard Ard; MADB_Apd Apd; MADB_Ipd Ipd; MADB_Ird Ird; } Fields; } MADB_Desc; struct st_ma_desc_fldid { SQLSMALLINT FieldIdentifier; SQLSMALLINT Access[4]; }; struct st_ma_stmt_methods; typedef struct { SQLLEN MaxRows; SQLLEN MaxLength; SQLLEN KeysetSize; SQLUINTEGER CursorType; SQLUINTEGER ScrollConcurrency; SQLUINTEGER RetrieveData; SQLUINTEGER UseBookmarks; void* BookmarkPtr; SQLLEN BookmarkLength; SQLSMALLINT BookmarkType; SQLULEN MetadataId; SQLULEN SimulateCursor; } MADB_StmtOptions; /* TODO: To check is it 0 or 1 based? not quite clear from its usage */ typedef struct { char *Name; SQLLEN Position; SQLLEN RowsetSize; MYSQL_ROW_OFFSET Next; } MADB_Cursor; enum MADB_DaeType {MADB_DAE_NORMAL=0, MADB_DAE_ADD=1, MADB_DAE_UPDATE=2, MADB_DAE_DELETE=3}; #define RESET_DAE_STATUS(Stmt_Hndl) (Stmt_Hndl)->Status=0; (Stmt_Hndl)->PutParam= -1 #define MARK_DAE_DONE(Stmt_Hndl) (Stmt_Hndl)->Status=0; (Stmt_Hndl)->PutParam= (Stmt_Hndl)->ParamCount #define PARAM_IS_DAE(Len_Ptr) ((Len_Ptr) && (*(Len_Ptr) == SQL_DATA_AT_EXEC || *(Len_Ptr) <= SQL_LEN_DATA_AT_EXEC_OFFSET)) #define DAE_DONE(Stmt_Hndl) ((Stmt_Hndl)->PutParam >= (Stmt_Hndl)->ParamCount) enum MADB_StmtState {MADB_SS_INITED= 0, MADB_SS_EMULATED= 1, MADB_SS_PREPARED= 2, MADB_SS_EXECUTED= 3, MADB_SS_OUTPARAMSFETCHED= 4}; #define STMT_WAS_PREPARED(Stmt_Hndl) (Stmt_Hndl->State >= MADB_SS_EMULATED) #define RESET_STMT_STATE(Stmt_Hndl) Stmt_Hndl->State= STMT_WAS_PREPARED(Stmt_Hndl) ?\ (Stmt_Hndl->State == MADB_SS_EMULATED ? MADB_SS_EMULATED : MADB_SS_PREPARED) :\ MADB_SS_INITED /* Struct used to define column type when driver has to fix it (in catalog functions + SQLGetTypeInfo) */ typedef struct { SQLSMALLINT SqlType; my_bool Unsigned; SQLSMALLINT Nullable; SQLLEN OctetLength; } MADB_ShortTypeInfo; typedef struct { unsigned int ArraySize; my_bool HasRowsToSkip; } MADB_BulkOperationInfo; /* Stmt struct needs definitions from my_parse.h */ #include #define STMT_STRING(STMT) (STMT)->Query.Original struct st_ma_odbc_stmt { MADB_Dbc *Connection; struct st_ma_stmt_methods *Methods; MADB_StmtOptions Options; MADB_Error Error; MADB_Cursor Cursor; MYSQL_STMT *stmt; MYSQL_RES *metadata; MADB_List ListItem; MADB_QUERY Query; SQLSMALLINT ParamCount; enum MADB_DaeType DataExecutionType; MYSQL_RES *DefaultsResult; int ArrayOffset; SQLSETPOSIROW DaeRowNumber; int Status; MADB_DescRecord *PutDataRec; MADB_Stmt *DaeStmt; MADB_Stmt *PositionedCursor; my_bool PositionedCommand; enum MADB_StmtState State; MYSQL_STMT **MultiStmts; unsigned int MultiStmtNr; unsigned int MultiStmtMaxParam; SQLLEN LastRowFetched; MYSQL_BIND *result; MYSQL_BIND *params; int PutParam; my_bool RebindParams; my_bool bind_done; long long AffectedRows; unsigned long *CharOffset; unsigned long *Lengths; char *TableName; char *CatalogName; MADB_ShortTypeInfo *ColsTypeFixArr; MADB_BulkOperationInfo Bulk; /* Application Descriptors */ MADB_Desc *Apd; MADB_Desc *Ard; MADB_Desc *Ird; MADB_Desc *Ipd; /* Internal Descriptors */ MADB_Desc *IApd; MADB_Desc *IArd; MADB_Desc *IIrd; MADB_Desc *IIpd; }; enum MADB_AppType{ ATypeGeneral= 0, ATypeMSAccess= 1 }; typedef struct st_ma_odbc_environment { MADB_Error Error; CRITICAL_SECTION cs; MADB_List *Dbcs; SQLWCHAR *TraceFile; SQLUINTEGER Trace; SQLINTEGER OdbcVersion; SQLINTEGER OutputNTS; enum MADB_AppType AppType; } MADB_Env; //const size_t sizeOfT = sizeof(MADB_Env); #include typedef struct st_client_charset { unsigned int CodePage; MARIADB_CHARSET_INFO *cs_info; } Client_Charset; struct st_ma_odbc_connection { MADB_Error Error; CRITICAL_SECTION cs; /* mutex for mariadb handle, i.e. for server communications */ CRITICAL_SECTION ListsCs; /* for operations with lists */ MADB_List ListItem; Client_Charset Charset; MYSQL *mariadb; /* handle to a mariadb connection */ MADB_Env *Environment; /* global environment */ MADB_Dsn *Dsn; struct st_ma_connection_methods *Methods; Client_Charset *ConnOrSrcCharset; /* "Source" here stands for which charset Windows DM was using as source, when converted to unicode. We have to use same charset to recode from unicode to get same string as application sent it. For Unicode application that is the same as "Charset", or in case of ANSI on Windows - defaulst system codepage */ char *CurrentSchema; /* Used to store current schema if the seesion tracking is possible */ MADB_List *Stmts; MADB_List *Descrs; /* Attributes */ char *CatalogName; HWND QuietMode; char *TraceFile; SQLULEN AsyncEnable; SQLPOINTER EnlistInDtc; SQLULEN OdbcCursors; unsigned long Options; SQLUINTEGER AutoIpd; SQLUINTEGER AutoCommit; SQLUINTEGER ConnectionDead; SQLUINTEGER ConnectionTimeout; SQLUINTEGER ReadTimeout; SQLUINTEGER WriteTimeout; SQLUINTEGER PacketSize; SQLINTEGER AccessMode; SQLINTEGER IsolationLevel; /* tx_isolation */ SQLUINTEGER Trace; SQLUINTEGER LoginTimeout; SQLUINTEGER MetadataId; SQLINTEGER TxnIsolation; SQLINTEGER CursorCount; char ServerCapabilities; my_bool IsAnsi; my_bool IsMySQL; }; typedef BOOL (__stdcall *PromptDSN)(HWND hwnd, MADB_Dsn *Dsn); typedef struct { void *LibraryHandle; PromptDSN Call; } MADB_Prompt; #define MADB_PROMPT_NOT_SUPPORTED 1 #define MADB_PROMPT_COULDNT_LOAD 2 int DSNPrompt_Lookup(MADB_Prompt *prompt, const char *SetupLibName); int DSNPrompt_Free (MADB_Prompt *prompt); int InitClientCharset (Client_Charset *cc, const char *name); void CopyClientCharset (Client_Charset *Src, Client_Charset *Dst); void CloseClientCharset(Client_Charset *cc); /* Default precision of SQL_NUMERIC */ #define MADB_DEFAULT_PRECISION 38 #define MADB_MAX_SCALE MADB_DEFAULT_PRECISION #define BINARY_CHARSETNR 63 /* Inexistent param id */ #define MADB_NOPARAM -1 /* Macros to guard communications with the server. TODO: make it(locking) optional depending on designated connection string option */ #define LOCK_MARIADB(Dbc) EnterCriticalSection(&(Dbc)->cs) #define UNLOCK_MARIADB(Dbc) LeaveCriticalSection(&(Dbc)->cs) /* Enabling tracing */ #define MAODBC_DEBUG 1 /* Macro checks return of the suplied SQLRETURN function call, checks if it is succeeded, and in case of error pushes error up */ #define RETURN_ERROR_OR_CONTINUE(sqlreturn_func_call) {\ SQLRETURN rc= (sqlreturn_func_call);\ if (!SQL_SUCCEEDED(rc)) return rc;\ } while(0) #define iOdbc() (sizeof(SQLWCHAR)==4) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ma_api_internal.h" SQLRETURN MADB_GetBookmark(MADB_Stmt *StatementHandle, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr); SQLRETURN MADB_StmtColAttr(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN *NumericAttributePtr, my_bool IsWchar); SQLRETURN MADB_StmtColAttr(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN *NumericAttributePtr, my_bool IsWchar); #endif /* _ma_odbc_h_ */ mariadb-connector-odbc-3.1.15/ma_odbc_version.h.in000066400000000000000000000010731414431724000220050ustar00rootroot00000000000000/* * */ #ifndef _ma_odbc_version_h_ #define _ma_odbc_version_h_ #define MARIADB_ODBC_VERSION_MAJOR @MARIADB_ODBC_VERSION_MAJOR@ #define MARIADB_ODBC_VERSION_MINOR @MARIADB_ODBC_VERSION_MINOR@ #define MARIADB_ODBC_VERSION_PATCH @MARIADB_ODBC_VERSION_PATCH@ #define MARIADB_ODBC_VERSION "@MARIADB_ODBC_VERSION@" #define MARIADB_ODBC_ERR_PREFIX "[ma-@MARIADB_ODBC_VERSION_MAJOR@.@MARIADB_ODBC_VERSION_MINOR@.@MARIADB_ODBC_VERSION_PATCH@]" #define ODBCVER 0x0351 #define MADB_DEFAULT_PLUGINS_SUBDIR "@MARIADB_DEFAULT_PLUGINS_SUBDIR@" #endif /* _ma_odbc_version_h_ */ mariadb-connector-odbc-3.1.15/ma_parse.c000066400000000000000000000356131414431724000200400ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include /* Minimal query length when we tried to avoid full parsing */ #define QUERY_LEN_FOR_POOR_MAN_PARSING 32768 char* SkipSpacesAndComments(char **CurPtr, size_t *Length, BOOL OverWrite) { char *End= *CurPtr + *Length, *Prev= NULL; /* Making sure that we don't have leading whitespaces and/or comments, and the string begins from something meainingful */ while (*CurPtr < End && *CurPtr != Prev) { Prev= *CurPtr; *CurPtr= StripLeadingComments(*CurPtr, Length, OverWrite); *CurPtr= ltrim(*CurPtr); *Length= strlen(*CurPtr); } return *CurPtr; } char* SkipQuotedString(char **CurPtr, char *End, char Quote) { while (*CurPtr < End && **CurPtr != Quote) { /* Skipping backslash and next character, if needed */ if (**CurPtr == '\\') { ++*CurPtr; /* Taking care of the case, when backslash is at last position */ if (*CurPtr == End) { break; } } ++*CurPtr; } return *CurPtr; } char* SkipQuotedString_Noescapes(char **CurPtr, char *End, char Quote) { while (*CurPtr < End && **CurPtr != Quote) { ++*CurPtr; } return *CurPtr; } int MADB_ResetParser(MADB_Stmt *Stmt, char *OriginalQuery, SQLINTEGER OriginalLength) { MADB_DeleteQuery(&Stmt->Query); if (OriginalQuery != NULL) { /* We can have here not NULL-terminated string as a source, thus we need to allocate, copy meaningful characters and add NULL. strndup does that for us. StmtSopy may change, p points to the allocated memory */ Stmt->Query.allocated= Stmt->Query.RefinedText= strndup(OriginalQuery, OriginalLength); if (Stmt->Query.allocated == NULL) { return 1; } Stmt->Query.RefinedLength= OriginalLength; Stmt->Query.BatchAllowed= DSN_OPTION(Stmt->Connection, MADB_OPT_FLAG_MULTI_STATEMENTS) ? '\1' : '\0'; Stmt->Query.AnsiQuotes= MADB_SqlMode(Stmt->Connection, MADB_ANSI_QUOTES); Stmt->Query.NoBackslashEscape= MADB_SqlMode(Stmt->Connection, MADB_NO_BACKSLASH_ESCAPES); } return 0; } void MADB_DeleteSubqueries(MADB_QUERY * Query) { unsigned int i; SINGLE_QUERY SubQuery; for (i= 0; i < Query->SubQuery.elements; ++i) { MADB_GetDynamic(&Query->SubQuery, (char *)&SubQuery, i); MADB_DeleteDynamic(&SubQuery.ParamPos); } MADB_DeleteDynamic(&Query->SubQuery); } void MADB_AddSubQuery(MADB_QUERY * Query, char * SubQueryText, enum enum_madb_query_type QueryType) { SINGLE_QUERY SubQuery; SubQuery.QueryText= SubQueryText; SubQuery.QueryType= QueryType; MADB_InitDynamicArray(&SubQuery.ParamPos, sizeof(unsigned int), 20, 20); MADB_InsertDynamic(&Query->SubQuery, (char*)&SubQuery); } void MADB_DeleteQuery(MADB_QUERY *Query) { MADB_FREE(Query->allocated); MADB_FREE(Query->Original); MADB_DeleteDynamic(&Query->Tokens); MADB_DeleteSubqueries(Query); memset(Query, 0, sizeof(MADB_QUERY)); } int MADB_ParseQuery(MADB_QUERY * Query) { /* make sure we don't have trailing whitespace or semicolon */ Query->RefinedLength= SqlRtrim(Query->RefinedText, (int)Query->RefinedLength); Query->RefinedText= ltrim(Query->RefinedText); Query->RefinedText= FixIsoFormat(Query->RefinedText, &Query->RefinedLength); Query->RefinedLength-= Query->RefinedText - Query->allocated; /* Making copy of "original" string, with minimal changes required to be able to execute */ Query->Original= strndup(Query->RefinedText, Query->RefinedLength); SkipSpacesAndComments(&Query->RefinedText, &Query->RefinedLength, FALSE); return ParseQuery(Query); } /*----------------- Tokens stuff ------------------*/ char *MADB_Token(MADB_QUERY *Query, unsigned int Idx) { char *p; unsigned int Offset= 0; p= Query->RefinedText; if (!Query->Tokens.elements || !p) return NULL; if (Idx >= Query->Tokens.elements) return NULL; MADB_GetDynamic(&Query->Tokens, (char *)&Offset, Idx); return Query->RefinedText + Offset; } my_bool MADB_CompareToken(MADB_QUERY *Query, unsigned int Idx, char *Compare, size_t Length, unsigned int *Offset) { char *TokenString; if (!(TokenString= MADB_Token(Query, Idx))) return FALSE; if (_strnicmp(TokenString, Compare, Length) == 0) { if (Offset) *Offset= (unsigned int)(TokenString - Query->RefinedText); return TRUE; } return FALSE; } /* Not used atm, but may be useful */ unsigned int MADB_FindToken(MADB_QUERY *Query, char *Compare) { unsigned int i, TokenCount= Query->Tokens.elements; unsigned int Offset= 0; for (i=0; i < TokenCount; i++) { if (MADB_CompareToken(Query, i, Compare, strlen(Compare), &Offset)) return Offset; } return 0; } static char * ParseCursorName(MADB_QUERY *Query, unsigned int *Offset) { unsigned int i, TokenCount= Query->Tokens.elements; if (TokenCount < 4) { return NULL; } for (i=0; i < TokenCount - 3; i++) { if (MADB_CompareToken(Query, i, "WHERE", 5, Offset) && MADB_CompareToken(Query, i+1, "CURRENT", 7, 0) && MADB_CompareToken(Query, i+2, "OF", 2, 0)) { return MADB_Token(Query, i + 3); } } return NULL; } static char * PoorManCursorName(MADB_QUERY *Query, unsigned int *Offset) { MADB_QUERY EndPiece; char *Res; memset(&EndPiece, 0, sizeof(MADB_QUERY)); /* We do poor man on long queries only, thus there is no need to check length */ EndPiece.RefinedText= ltrim(Query->RefinedText + Query->RefinedLength - MADB_MAX_CURSOR_NAME - 32/* "WHERE CURRENT OF" + spaces */); EndPiece.RefinedLength= strlen(EndPiece.RefinedText); /* As we did poor man parsing, we don't have full information about the query. Thus, parsing only this part at the end of the query - we need tockens, to check if we have WHERE CURRENT OF in usual way */ if (ParseQuery(&EndPiece)) { return NULL; } /* Now looking for cursor name in usual way */ Res= ParseCursorName(&EndPiece, Offset); /* Incrementing Offset with the offset of our part of the query */ if (Res != NULL) { *Offset= (unsigned int)(*Offset + EndPiece.RefinedText - Query->RefinedText); } MADB_DeleteQuery(&EndPiece); return Res; } char * MADB_ParseCursorName(MADB_QUERY *Query, unsigned int *Offset) { if (Query->PoorManParsing) { return PoorManCursorName(Query, Offset); } return ParseCursorName(Query, Offset); } enum enum_madb_query_type MADB_GetQueryType(const char *Token1, const char *Token2) { /* We need for the case when MS Access adds parenthesis around query - see ODBC-57*/ while (*Token1 && !isalpha(*Token1)) ++Token1; if (_strnicmp(Token1, "SELECT", 6) == 0 || _strnicmp(Token1, "WITH", 4) == 0) { return MADB_QUERY_SELECT; } if (_strnicmp(Token1, "INSERT", 6) == 0 || _strnicmp(Token1, "REPLACE", 7) == 0) { return MADB_QUERY_INSERT; } if (_strnicmp(Token1, "UPDATE", 6) == 0) { return MADB_QUERY_UPDATE; } if (_strnicmp(Token1, "DELETE", 6) == 0) { return MADB_QUERY_DELETE; } if (_strnicmp(Token1, "CALL", 4) == 0) { return MADB_QUERY_CALL; } if (_strnicmp(Token1, "SHOW", 4) == 0) { return MADB_QUERY_SHOW; } if (_strnicmp(Token1, "ANALYZE", 7) == 0) { return MADB_QUERY_ANALYZE; } if (_strnicmp(Token1, "EXPLAIN", 7) == 0) { return MADB_QUERY_EXPLAIN; } if (_strnicmp(Token1, "CHECK", 5) == 0) { return MADB_QUERY_CHECK; } if (_strnicmp(Token1, "EXECUTE", 7) == 0) { return MADB_QUERY_EXECUTE; } if (_strnicmp(Token1, "CREATE", 6) == 0) { if (_strnicmp(Token2, "PROCEDURE", 9) == 0) { return MADB_QUERY_CREATE_PROC; } if (_strnicmp(Token2, "FUNCTION", 8) == 0) { return MADB_QUERY_CREATE_FUNC; } if (_strnicmp(Token2, "DEFINER", 7) == 0) { return MADB_QUERY_CREATE_DEFINER; } } if (_strnicmp(Token1, "SET", 3) == 0) { if (_strnicmp(Token2, "NAMES", 5) == 0) { return MADB_QUERY_SET_NAMES; } else { return MADB_QUERY_SET; } } if (_strnicmp(Token1, "DESC", 4) == 0) { return MADB_QUERY_DESCRIBE; } if (_strnicmp(Token1, "BEGIN", 5) == 0 && _strnicmp(Token2, "NOT", 3) == 0) { return MADB_NOT_ATOMIC_BLOCK; } return MADB_QUERY_NO_RESULT; } /* -------------------- Tokens - End ----------------- */ /* Not used - rather a placeholder in case we need it */ const char * MADB_FindParamPlaceholder(MADB_Stmt *Stmt) { return STMT_STRING(Stmt); } /* Function assumes that query string has been trimmed */ char* FixIsoFormat(char * StmtString, size_t *Length) { if (*Length > 0 && StmtString[0] == '{' && StmtString[*Length -1] == '}') { char *Res; ++StmtString; StmtString[*Length - 1]= '\0'; Res= trim(StmtString); *Length= strlen(Res); return Res; } return StmtString; } #define SAVE_TOKEN(PTR2SAVE) do { Offset= (unsigned int)(PTR2SAVE - Query->RefinedText);\ MADB_InsertDynamic(&Query->Tokens, (char*)&Offset); } while(0) static BOOL ShouldWeTryPoorManParsing(MADB_QUERY *Query) { return (Query->RefinedLength > QUERY_LEN_FOR_POOR_MAN_PARSING) && (strchr(Query->RefinedText, ';')) == NULL && (strchr(Query->RefinedText, '?') == NULL); } int ParseQuery(MADB_QUERY *Query) { char *p= Query->RefinedText, Quote; BOOL ReadingToken= FALSE; unsigned int Offset, StmtTokensCount= 0; size_t Length= Query->RefinedLength; char *end= p + Length, *CurQuery= NULL, *LastSemicolon= NULL; enum enum_madb_query_type StmtType; MADB_InitDynamicArray(&Query->Tokens, (unsigned int)sizeof(unsigned int), (unsigned int)MAX(Length/32, 20), (unsigned int)MAX(Length/20, 40)); MADB_InitDynamicArray(&Query->SubQuery, (unsigned int)sizeof(SINGLE_QUERY), (unsigned int)MAX(Length/64, 20), (unsigned int)MAX(Length/64, 40)); Query->PoorManParsing= ShouldWeTryPoorManParsing(Query); while (p < end) { if (ReadingToken == FALSE) { Length= end - p; SkipSpacesAndComments(&p, &Length, TRUE); SAVE_TOKEN(p); ++StmtTokensCount; ReadingToken= TRUE; /* On saving 1st statement's token, we are incrementing statements counter */ if (StmtTokensCount == 1) { CurQuery= p; } /* Having 2 first tockens we can get statement type. And we need to know it for the case of multistatement - some statements may "legally" contain ';' */ else if (StmtTokensCount == 2) { /* We are currently at 2nd token of statement, and getting previous token position from Tokens array*/ StmtType= MADB_GetQueryType(MADB_Token(Query, Query->Tokens.elements - 2), p); Query->ReturnsResult= Query->ReturnsResult || !QUERY_DOESNT_RETURN_RESULT(StmtType); MADB_AddSubQuery(Query, CurQuery, StmtType); /* If we on first statement, setting QueryType*/ if (Query->Tokens.elements == 2) { Query->QueryType= StmtType; if (Query->PoorManParsing) { return 0; } } } switch (*p) { /* If some of them is opening a string, on the fall-through next `quote` won't be set, as STRING_OR_COMMENT will be `true`. Likewise, if we are already in the string. But if we get hear, we are not supposed to be inside a string */ case '"': case '\'': case '`': { char *SavePosition; Quote= *p++; SavePosition= p; /* In case we go past eos while looking for ending quote */ if (Query->NoBackslashEscape || Quote == '`' || /* Backtick works with ANSI_QUOTES */ (Query->AnsiQuotes && Quote == '"'))/* In indetifier quotation backslash does not escape anything - CLI has error with that */ { SkipQuotedString_Noescapes(&p, end, Quote); } else { SkipQuotedString(&p, end, Quote); } if (p >= end) { /* Basically we got ending quote here - possible in poor man case, when we look for cursor name starting from the position inside string. Other options are bad query of parsing error */ p= SavePosition; ReadingToken= FALSE; } break; } case '?': /* This can break token(w/out space char), and be beginning of a token. Thus we need it in both places */ Query->HasParameters= 1; /* Parameter placeholder is a complete token. And next one may begin right after it*/ ReadingToken= FALSE; break; case ';': if (QueryIsPossiblyMultistmt(Query)) { /* If batches are not allowed, we only need the fact, that this is multi-statement */ if (Query->BatchAllowed) { *p= '\0'; } StmtTokensCount= 0; } ReadingToken= FALSE; /* We should not move pointer here */ break; } } else { switch (*p) { case '?': case '"': case '\'': case '`': case ' ': case '\t': case '\r': case '\n': case '-': case '#': case '/': case ';': ReadingToken= FALSE; /* We should not move pointer here, since this can be already beginning of new token */ continue; default: break; } } ++p; } return 0; } char * StripLeadingComments(char *Str, size_t *Length, BOOL OverWrite) { char *Res= Str; int ClosingStrLen= 1; /* There is nothing to strip for sure */ if (*Length == 0) { return Str; } if (strncmp(Str, "--", 2) == 0) { Res= strchr(Str + 2, '\n'); } else if (*Str == '#') { Res= strchr(Str + 1, '\n'); } else if (strncmp(Str, "/*", 2) == 0) { Res= strstr(Str + 2, "*/"); ClosingStrLen= 2; } if (Res != Str) { if (Res != NULL) { Res+= ClosingStrLen; *Length-= Res - Str; } else /* We found comment opening string, but did not find the closing string */ { /* Thus moving pointer to the end of the string */ Res= Str + *Length; *Length= 0; } /* On request - overwriting comment with white spaces */ if (OverWrite) { memset(Str, ' ', Res - Str); } } return Res; } mariadb-connector-odbc-3.1.15/ma_parse.h000066400000000000000000000077711414431724000200510ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_parse_h_ #define _ma_parse_h_ #include enum enum_madb_query_type { MADB_QUERY_NO_RESULT= 0, /* Default type for the query types we are not interested in */ MADB_QUERY_INSERT, MADB_QUERY_UPDATE= SQL_UPDATE, MADB_QUERY_DELETE= SQL_DELETE, MADB_QUERY_CREATE_PROC, MADB_QUERY_CREATE_FUNC, MADB_QUERY_CREATE_DEFINER, MADB_QUERY_SET, MADB_QUERY_SET_NAMES, MADB_QUERY_SELECT, MADB_QUERY_SHOW, MADB_QUERY_CALL, MADB_QUERY_ANALYZE, MADB_QUERY_EXPLAIN, MADB_QUERY_CHECK, MADB_QUERY_EXECUTE, MADB_QUERY_DESCRIBE, MADB_NOT_ATOMIC_BLOCK }; typedef struct { char *QueryText; enum enum_madb_query_type QueryType; MADB_DynArray ParamPos; } SINGLE_QUERY; typedef struct { char * Original; char * allocated; /* Pointer to the allocated area. The refined query may go to the right */ char * RefinedText; size_t RefinedLength; MADB_DynArray Tokens; MADB_DynArray SubQuery; /* List of queries or batches of queries, that can be executed together at once */ /* So far only falg whether we have any parameters */ my_bool HasParameters; /* This is more for multistatements for optimization - if none of queries returns result, we can send them via text protocol */ my_bool ReturnsResult; enum enum_madb_query_type QueryType; my_bool PoorManParsing; my_bool BatchAllowed; my_bool AnsiQuotes; my_bool NoBackslashEscape; } MADB_QUERY; #define PQUERY_UPDATE_LEN(PARSED_QUERY_PTR) (PARSED_QUERY_PTR)->RefinedLength= strlen((PARSED_QUERY_PTR)->RefinedLength) #define STMT_COUNT(PARSED_QUERY) ((PARSED_QUERY).SubQuery.elements/* + 1*/) #define QUERY_IS_MULTISTMT(PARSED_QUERY) (STMT_COUNT(PARSED_QUERY) > 1) int MADB_ResetParser(MADB_Stmt *Stmt, char *OriginalQuery, SQLINTEGER OriginalLength); void MADB_DeleteSubqueries(MADB_QUERY *Query); void MADB_AddSubQuery(MADB_QUERY *Query, char *SubQueryText, enum enum_madb_query_type QueryType); void MADB_DeleteQuery(MADB_QUERY *Query); int MADB_ParseQuery(MADB_QUERY *Query); #define QUERY_DOESNT_RETURN_RESULT(query_type) ((query_type) < MADB_QUERY_SELECT) char * MADB_ParseCursorName(MADB_QUERY *Query, unsigned int *Offset); unsigned int MADB_FindToken(MADB_QUERY *Query, char *Compare); enum enum_madb_query_type MADB_GetQueryType(const char *Token1, const char *Token2); const char * MADB_FindParamPlaceholder(MADB_Stmt *Stmt); char * FixIsoFormat(char * StmtString, size_t *Length); int ParseQuery(MADB_QUERY *Query); char * StripLeadingComments(char *s, size_t *Length, BOOL OverWrite); #endif /* _ma_parse_h_ */ mariadb-connector-odbc-3.1.15/ma_platform_posix.c000066400000000000000000000240001414431724000217600ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2014,2020 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* MariaDB ODBC driver helper functions for platforms other than windows(so far) */ /* NOTE If you change something in this program, please consider if other platform's version of the function you are changing, needs to be changed accordingly */ #include #include #include "ma_conv_charset.h" extern MARIADB_CHARSET_INFO *DmUnicodeCs; extern Client_Charset utf8; char LogFile[256]; void InitializeCriticalSection(CRITICAL_SECTION *cs) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(cs, &attr); } int DSNPrompt_Lookup(MADB_Prompt *prompt, const char * SetupLibName) { return MADB_PROMPT_NOT_SUPPORTED; } int DSNPrompt_Free (MADB_Prompt *prompt) { prompt->LibraryHandle= NULL; return 0; } /* Mimicking of VS' _snprintf */ int _snprintf(char *buffer, size_t count, const char *format, ...) { va_list list; va_start(list, format); int result= vsnprintf(buffer, count, format, list); va_end(list); /* _snprintf returns negative number if buffer is not big enough */ if (result > count) { return count - result - 1; } return result; } int strcpy_s(char *dest, size_t buffer_size, const char *src) { size_t src_len; if (dest == NULL) { return EINVAL; } if (src == NULL) { *dest= '\0'; return EINVAL; } src_len= strlen(src); if (buffer_size < src_len + 1) { *dest= 0; return ERANGE; } memcpy((void*)dest, (void*)src, src_len + 1); return 0; } const char* GetDefaultLogDir() { const char *DefaultLogDir="/tmp"; char *tmp= getenv("HOME"); if (tmp) { DefaultLogDir= tmp; } _snprintf(LogFile, sizeof(LogFile), "%s/maodbc.log", DefaultLogDir); return LogFile; } /* CharLen < 0 - treat as NTS */ SQLINTEGER SqlwcsOctetLen(const SQLWCHAR *str, SQLINTEGER *CharLen) { SQLINTEGER result= 0, inChars= *CharLen; if (str) { while (inChars > 0 || (inChars < 0 && *str)) { result+= DmUnicodeCs->mb_charlen(*str); --inChars; str+= DmUnicodeCs->mb_charlen(*str)/sizeof(SQLWCHAR); } } if (*CharLen < 0) { *CharLen-= inChars; } return result; } SQLWCHAR *MADB_ConvertToWchar(const char *Ptr, SQLLEN PtrLength, Client_Charset* cc) { SQLWCHAR *WStr= NULL; size_t Length= 0; if (!Ptr) return WStr; if (PtrLength == SQL_NTS) { PtrLength= -1; /* To copy terminating null as well */ Length= 1; } if (!cc || !cc->CodePage) cc= &utf8; Length+= MbstrOctetLen(Ptr, &PtrLength, cc->cs_info); if ((WStr= (SQLWCHAR *)MADB_CALLOC(sizeof(SQLWCHAR) * (PtrLength + 1)))) { size_t wstr_octet_len= sizeof(SQLWCHAR) * (PtrLength + 1); /* TODO: Need error processing. i.e. if MADB_ConvertString returns -1 */ MADB_ConvertString(Ptr, &Length, cc->cs_info, (char*)WStr, &wstr_octet_len, DmUnicodeCs, NULL); } return WStr; } /* {{{ MADB_ConvertFromWChar */ char *MADB_ConvertFromWChar(const SQLWCHAR *Ptr, SQLINTEGER PtrLength, SQLULEN *Length, Client_Charset *cc, BOOL *Error) { char *AscStr; size_t AscLen= PtrLength, PtrOctetLen; BOOL dummyError= 0; if (Error) { *Error= 0; } else { Error= &dummyError; } if (cc == NULL || cc->CodePage < 1) { cc= &utf8; } if (PtrLength == SQL_NTS) { /*-1 - to calculate length as of nts */ SQLINTEGER InCharLen= -1; PtrOctetLen= SqlwcsOctetLen(Ptr, &InCharLen); /* Allocating +1 character for terminating symbol */ AscLen= (InCharLen+1)*cc->cs_info->char_maxlen; } else { /* PtrLength is in characters. MADB_ConvertString(iconv) needs bytes */ PtrOctetLen= SqlwcsOctetLen(Ptr, &PtrLength); AscLen= PtrLength*cc->cs_info->char_maxlen; } if (!(AscStr = (char *)MADB_CALLOC(AscLen))) return NULL; AscLen= MADB_ConvertString((char*)Ptr, &PtrOctetLen, DmUnicodeCs, AscStr, &AscLen, cc->cs_info, Error); if (AscLen != (size_t)-1) { if (PtrLength == -1 && AscLen > 0) { --AscLen; } } else { MADB_FREE(AscStr); AscLen= 0; } if (Length) *Length= (SQLINTEGER)AscLen; return AscStr; } /* }}} */ /* {{{ MADB_ConvertAnsi2Unicode @AnsiLength[in] - number of bytes to copy, negative if AnsiString is Null terminated and the terminating blank has to be copied @UnicodeLength[in] - size of output buffer in chars, that effectively mean in SQLWCHAR units @LengthIndicator[out] - number of available characters not counting terminating null(unless it was included in AnsiLength, and IsNull is FALSE) @IsNull[in] - whether to copy terminating blank. The value has to be 1 or 0(TRUE/FALSE) If AnsiString is negative, its value is neglected(null is copied) @returns 1 in case of error, 0 otherwise */ int MADB_ConvertAnsi2Unicode(Client_Charset *cc, const char *AnsiString, SQLLEN AnsiLength, SQLWCHAR *UnicodeString, SQLLEN UnicodeLength, SQLLEN *LengthIndicator, BOOL IsNull, MADB_Error *Error) { SQLINTEGER RequiredLength; SQLWCHAR *Tmp= UnicodeString; int rc= 0, error; size_t SrcOctetLen, DestOctetLen; if (LengthIndicator) *LengthIndicator= 0; if (Error) MADB_CLEAR_ERROR(Error); if (!AnsiLength || UnicodeLength < 0) { if (Error) MADB_SetError(Error, MADB_ERR_HY090, NULL, 0); return 1; } if (AnsiLength == SQL_NTS || AnsiLength == -1) { IsNull= 1; AnsiLength= strlen(AnsiString); } /* calculate required length */ RequiredLength= MbstrCharLen(AnsiString, AnsiLength, cc->cs_info) + IsNull; /* Set LengthIndicator */ if (LengthIndicator) *LengthIndicator= RequiredLength - IsNull; /* No buffer length, no need to copy - got length and run */ if (!UnicodeLength) return 0; if (RequiredLength > UnicodeLength) { Tmp= (SQLWCHAR *)malloc(RequiredLength * sizeof(SQLWCHAR)); if (Tmp == NULL) { if (Error) { MADB_SetError(Error, MADB_ERR_HY001, NULL, 0); } return 1; } } else { RequiredLength= UnicodeLength; } SrcOctetLen= AnsiLength + IsNull; DestOctetLen= sizeof(SQLWCHAR) * RequiredLength; RequiredLength= MADB_ConvertString(AnsiString, &SrcOctetLen, cc->cs_info, (char*)Tmp, &DestOctetLen, DmUnicodeCs, &error); if (RequiredLength < 1) { if (Error) MADB_SetError(Error, MADB_ERR_HY000, "Ansi to Unicode conversion error occurred", error); rc= 1; goto end; } if (LengthIndicator) *LengthIndicator= SqlwcsCharLen(Tmp, RequiredLength); /* Truncation */ if (Tmp != UnicodeString) { memcpy((void*)UnicodeString, (void*)Tmp, (UnicodeLength-1)*sizeof(SQLWCHAR)); *(UnicodeString + UnicodeLength - 1)= 0; if (Error) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); } end: if (Tmp != UnicodeString) free(Tmp); return rc; } /* }}} */ /* {{{ MADB_ConvertAnsi2Unicode @returns number of characters available at Src */ SQLLEN MADB_SetString(Client_Charset* cc, void *Dest, SQLULEN DestLength, const char *Src, SQLLEN SrcLength/*bytes*/, MADB_Error *Error) { SQLLEN Length= 0; if (SrcLength == SQL_NTS) { if (Src != NULL) { /* Thinking about utf8 - Should be probably len in characters */ SrcLength= strlen(Src); } else { SrcLength= 0; } } /* Not enough space */ if (!DestLength || !Dest) { if (Dest) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); if (!cc) return SrcLength; else { Length= MbstrCharLen(Src, SrcLength, cc->cs_info); /* In case of !DestLength || !Dest(application didn't give buffer and probably wants to know required length) * we most probably have empty Src, and Length will be equal 0 in this case. * Taking source length as character length. MultiByteToWideChar on windows does that for us */ return Length == 0 && SrcLength > 0 ? SrcLength : Length; } } if (!SrcLength || !Src || !strlen(Src)) { memset((char *)Dest, 0, cc ? sizeof(SQLWCHAR) : sizeof(SQLCHAR)); return 0; } if (!cc) { strncpy_s((char *)Dest, DestLength, Src ? Src : "", _TRUNCATE); /* strncpy does not write null at the end */ *((char *)Dest + MIN(SrcLength, DestLength - 1))= '\0'; if (Error && SrcLength >= DestLength) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); return SrcLength; } else { MADB_ConvertAnsi2Unicode(cc, Src, -1, (SQLWCHAR *)Dest, DestLength, &Length, TRUE, Error); return Length; } } /* }}} */ /* Stub - needed on windows only */ int GetSourceAnsiCs(Client_Charset *cc) { CopyClientCharset(&utf8, cc); return cc->CodePage; } /* {{{ MADB_DSN_PossibleConnect(MADB_Dsn *) */ BOOL MADB_DSN_PossibleConnect(MADB_Dsn *Dsn) { return Dsn->Socket || (Dsn->ServerName && Dsn->Port > 0 && Dsn->IsTcpIp); } /* Stub - atm it looks like we don't need to do anything here */ char* MADB_GetDefaultPluginsDir(char* Buffer, size_t Size) { return NULL; } mariadb-connector-odbc-3.1.15/ma_platform_posix.h000066400000000000000000000046761414431724000220060ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2014 SkySQL AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* MariaDB ODBC driver platform dependent declarations */ /* NOTE If you change something in this program, please consider if other platform's declaration require similar change */ /* Only one "platform" header is supposed to be included */ #ifndef _ma_platform_x_h_ # define _ma_platform_x_h_ /* Need size_t */ #include #define __stdcall #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif //#define HAVE_UNICODE #define MADB_DRIVER_NAME "libmaodbc.so" #define CP_UTF8 65001 #define _strdup strdup #define _stricmp strcasecmp #define _strnicmp strncasecmp #define _atoi64(str) strtoll((str), NULL, 10) #define _i64toa(a,b,c) longlong2str((a),(b),(c)) /* Mimicking of VS' _snprintf */ int _snprintf(char *buffer, size_t count, const char *format, ...); /* Error codes fo strcpy_s */ #ifndef EINVAL # define EINVAL 22 #endif #ifndef ERANGE # define ERANGE 34 #endif int strcpy_s(char *dest, size_t buffer_size, const char *src); #define strncpy_s(a,b,c,d) strncpy((a),(c),(b)) /* Critical section -> pthread mutex */ #include #define CRITICAL_SECTION pthread_mutex_t #define EnterCriticalSection(cs) pthread_mutex_lock((cs)) #define LeaveCriticalSection(cs) pthread_mutex_unlock((cs)) #define DeleteCriticalSection(cs) pthread_mutex_destroy((cs)) #ifndef TryEnterCriticalSection # define TryEnterCriticalSection !pthread_mutex_trylock #endif void InitializeCriticalSection(CRITICAL_SECTION *cs); #endif /*_ma_platform_x_h_ */ mariadb-connector-odbc-3.1.15/ma_platform_win32.c000066400000000000000000000231451414431724000215710ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2014,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* MariaDB ODBC driver Win32 specific helper functions */ /* NOTE If you change something in this program, please consider if other platform's version of the function you are changing, needs to be changed accordingly */ #include "ma_odbc.h" #include extern Client_Charset utf8; char LogFile[256]; char* strndup(const char *s, size_t n) { char *res= NULL; if (s != NULL) { res= (char*)malloc(n + 1); if (res != NULL) { memcpy(res, s, n); res[n]= '\0'; } } return res; } const char* GetDefaultLogDir() { const char *DefaultLogDir= "c:"; char *tmp= getenv("USERPROFILE"); if (tmp) { DefaultLogDir= tmp; } tmp= getenv("TMP"); if (tmp) { DefaultLogDir= tmp; } _snprintf(LogFile, sizeof(LogFile), "%s\\MAODBC.LOG", DefaultLogDir); return LogFile; } /* Connection is needed to set custom error */ int DSNPrompt_Lookup(MADB_Prompt *prompt, const char * SetupLibName) { if (!(prompt->LibraryHandle=(void*) LoadLibrary(SetupLibName))) { return MADB_PROMPT_COULDNT_LOAD; } if (!(prompt->Call= (PromptDSN)GetProcAddress((HMODULE)prompt->LibraryHandle, "DSNPrompt"))) { return MADB_PROMPT_COULDNT_LOAD; } return SQL_SUCCESS; } int DSNPrompt_Free(MADB_Prompt *prompt) { if (prompt->LibraryHandle != NULL) { FreeLibrary((HMODULE)prompt->LibraryHandle); } prompt->LibraryHandle= NULL; prompt->Call= NULL; return 0; } SQLWCHAR *MADB_ConvertToWchar(const char *Ptr, SQLLEN PtrLength, Client_Charset* cc) { SQLWCHAR *WStr= NULL; int Length; if (!Ptr) return WStr; if (PtrLength == SQL_NTS) PtrLength= -1; if (!cc || !cc->CodePage) cc= &utf8; /* TODO: Check this In fact MultiByteToWideChar does not copy terminating character by default Thus +1 does not make sense "If the function succeeds and cchWideChar is 0, the return value is the required size, in characters, for the buffer indicated by lpWideCharStr... MultiByteToWideChar does not null-terminate an output string if the input string length is explicitly specified without a terminating null character. To null-terminate an output string for this function, the application should pass in -1 or explicitly count the terminating null character for the input string." */ if ((Length= MultiByteToWideChar(cc->CodePage, 0, Ptr, (int)PtrLength, NULL, 0))) if ((WStr= (SQLWCHAR *)MADB_CALLOC(sizeof(SQLWCHAR) * Length + 1))) MultiByteToWideChar(cc->CodePage, 0, Ptr, (int)PtrLength, WStr, Length); return WStr; } /* {{{ MADB_ConvertFromWChar Length gets number of written bytes including TN (if WstrCharLen == -1 or SQL_NTS or if WstrCharLen includes TN in the Wstr) */ char *MADB_ConvertFromWChar(const SQLWCHAR *Wstr, SQLINTEGER WstrCharLen, SQLULEN *Length/*Bytes*/, Client_Charset *cc, BOOL *Error) { char *AscStr; int AscLen, AllocLen; if (Error) *Error= 0; if (cc == NULL || cc->CodePage < 1) { cc= &utf8; } if (WstrCharLen == SQL_NTS) WstrCharLen= -1; AllocLen= AscLen= WideCharToMultiByte(cc->CodePage, 0, Wstr, WstrCharLen, NULL, 0, NULL, NULL); if (WstrCharLen != -1) ++AllocLen; if (!(AscStr = (char *)MADB_CALLOC(AllocLen))) return NULL; AscLen= WideCharToMultiByte(cc->CodePage, 0, Wstr, WstrCharLen, AscStr, AscLen, NULL, (cc->CodePage != CP_UTF8) ? Error : NULL); if (AscLen && WstrCharLen == -1) --AscLen; if (Length) *Length= (SQLINTEGER)AscLen; return AscStr; } /* }}} */ /* {{{ MADB_ConvertAnsi2Unicode @AnsiLength[in] - number of bytes to copy, negative if AnsiString is Null terminated and the terminating blank has to be copied @UnicodeLength[in] - size of output buffer in chars, that effectively mean in SQLWCHAR units @LengthIndicator[out] - number of available characters not counting terminating null(unless it was included in AnsiLength, and IsNull is FALSE) @IsNull[in] - whether to copy terminating blank. The value has to be 1 or 0(TRUE/FALSE) If AnsiString is negative, its value is neglected(null is copied) @returns 1 in case of error, 0 otherwise */ /* Required Length without or with TN(if IsNull is TRUE, or AnsiLength == -1 or SQL_NTS) is put to LenghtIndicator */ int MADB_ConvertAnsi2Unicode(Client_Charset *cc, const char *AnsiString, SQLLEN AnsiLength, SQLWCHAR *UnicodeString, SQLLEN UnicodeLength, SQLLEN *LengthIndicator, BOOL IsNull, MADB_Error *Error) { SQLLEN RequiredLength; SQLWCHAR *Tmp= UnicodeString; int rc= 0; if (LengthIndicator) *LengthIndicator= 0; if (Error) MADB_CLEAR_ERROR(Error); if (!AnsiLength || UnicodeLength < 0) { if (Error) MADB_SetError(Error, MADB_ERR_HY090, NULL, 0); return 1; } if (AnsiLength == SQL_NTS || AnsiLength == -1) IsNull= 1; /* calculate required length */ RequiredLength= MultiByteToWideChar(cc->CodePage, 0, AnsiString, IsNull ? -1 : (int)AnsiLength, NULL, 0); /* Set LengthIndicator */ if (LengthIndicator) *LengthIndicator= RequiredLength - IsNull; if (!UnicodeLength) return 0; if (RequiredLength > UnicodeLength) { Tmp= (SQLWCHAR*)malloc(RequiredLength * sizeof(SQLWCHAR)); if (Tmp == NULL) { if (Error) { MADB_SetError(Error, MADB_ERR_HY001, NULL, 0); } return 1; } } else { /* Otherwise the size of buffer is, as passed to the this function, UnicodeLength */ RequiredLength= UnicodeLength; } RequiredLength= MultiByteToWideChar(cc->CodePage, 0, AnsiString, IsNull ? -1 : (int)AnsiLength, Tmp, (int)RequiredLength); if (RequiredLength < 1) { if (Error) MADB_SetError(Error, MADB_ERR_HY000, "Ansi to Unicode conversion error occurred", GetLastError()); rc= 1; goto end; } /* Truncation */ if (Tmp != UnicodeString) { wcsncpy(UnicodeString, L"", 1); wcsncat(UnicodeString, Tmp, UnicodeLength- 1); if (Error) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); } end: if (Tmp != UnicodeString) free(Tmp); return rc; } /* Returns required length for result string with(if dest and dest length are provided) or without terminating NULL(otherwise). If cc is NULL, or not initialized(CodePage is 0), then simply SrcLength is returned. If Dest is not NULL, and DestLenth is 0, then error */ SQLLEN MADB_SetString(Client_Charset* cc, void *Dest, SQLULEN DestLength, const char *Src, SQLLEN SrcLength/*bytes*/, MADB_Error *Error) { char *p= (char *)Dest; SQLLEN Length= 0; if (SrcLength == SQL_NTS) { if (Src != NULL) { SrcLength= strlen(Src); } else { SrcLength= 0; } } /* Not enough space */ if (!DestLength || !Dest) { if (Dest) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); if (!cc || !cc->CodePage) return SrcLength; else { Length= MultiByteToWideChar(cc->CodePage, 0, Src, (int)SrcLength, NULL, 0); return Length; } } if (!Src || !strlen(Src) || !SrcLength) { memset(p, 0, cc && cc->CodePage ? sizeof(SQLWCHAR) : sizeof(SQLCHAR)); return 0; } if (!cc || !cc->CodePage) { strncpy_s((char *)Dest, DestLength, Src ? Src : "", _TRUNCATE); if (Error && (SQLULEN)SrcLength >= DestLength) MADB_SetError(Error, MADB_ERR_01004, NULL, 0); return SrcLength; } else { MADB_ConvertAnsi2Unicode(cc, Src, -1, (SQLWCHAR *)Dest, DestLength, &Length, 1, Error); return Length; } } int GetSourceAnsiCs(Client_Charset *cc) { cc->CodePage= GetACP(); /* We don't need cs_info for this */ return cc->CodePage; } BOOL MADB_DirectoryExists(const char *Path) { DWORD FileAttributes = GetFileAttributes(Path); return (FileAttributes != INVALID_FILE_ATTRIBUTES) && (FileAttributes & FILE_ATTRIBUTE_DIRECTORY); } char* MADB_GetDefaultPluginsDir(char* Buffer, size_t Size) { HMODULE hModule = GetModuleHandle(MADB_DRIVER_NAME); wchar_t wOurLocation[_MAX_PATH]; const char *PluginsSubDirName= "\\"MADB_DEFAULT_PLUGINS_SUBDIR; HRESULT hr; memset(Buffer, 0, Size); GetModuleFileNameW(hModule, wOurLocation, _MAX_PATH); hr= PathCchRemoveFileSpec(wOurLocation, _MAX_PATH); WideCharToMultiByte(GetACP(), 0, wOurLocation, -1, Buffer, (int)Size, NULL, NULL); if (strlen(Buffer) < Size - strlen(PluginsSubDirName)) { strcpy(Buffer + strlen(Buffer), PluginsSubDirName); if (MADB_DirectoryExists(Buffer) != FALSE) { return Buffer; } } return NULL; } /* {{{ MADB_DSN_PossibleConnect() */ BOOL MADB_DSN_PossibleConnect(MADB_Dsn *Dsn) { return Dsn->ServerName && (Dsn->IsNamedPipe || Dsn->Port > 0); } /* }}} */ mariadb-connector-odbc-3.1.15/ma_platform_win32.h000066400000000000000000000035011414431724000215700ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2014,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* MariaDB ODBC driver platform dependent declarations(win32) */ /* NOTE If you change something in this program, please consider if other platform's declaration require similar change */ /* Only one "platform" header is supposed to be included */ #ifndef _ma_platform_x_h_ #define _ma_platform_x_h_ #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif // !WIN32_LEAN_AND_MEAN #define _WINSOCKAPI_ #define DONT_DEFINE_VOID #define HAVE_UNICODE #include #include #include #if !defined(HAVE_mit_thread) && !defined(HAVE_STRTOK_R) #define strtok_r(A,B,C) strtok((A),(B)) #endif #define strcasecmp(A,B) _stricmp((A),(B)) #define MADB_DRIVER_NAME "maodbc.dll" char *strndup(const char *s, size_t n); char* strcasestr(const char* HayStack, const char* Needle); #endif /*_ma_platform_x_h_ */ mariadb-connector-odbc-3.1.15/ma_result.c000066400000000000000000000150431414431724000202370ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include /* {{{ MADB_StmtResetResultStructures */ void MADB_StmtResetResultStructures(MADB_Stmt *Stmt) { Stmt->CharOffset= (unsigned long *)MADB_REALLOC((char *)Stmt->CharOffset, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); memset(Stmt->CharOffset, 0, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); Stmt->Lengths= (unsigned long *)MADB_REALLOC((char *)Stmt->Lengths, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); memset(Stmt->Lengths, 0, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); Stmt->LastRowFetched= 0; MADB_STMT_RESET_CURSOR(Stmt); } /* }}} */ /* {{{ MoveNext - moves C/C cursor forward for Offset positions */ SQLRETURN MoveNext(MADB_Stmt *Stmt, unsigned long long Offset) { SQLRETURN result= SQL_SUCCESS; if (Stmt->result != NULL) { unsigned int i; char *SavedFlag; SavedFlag= (char*)MADB_CALLOC(mysql_stmt_field_count(Stmt->stmt)); if (SavedFlag == NULL) { return SQL_ERROR; } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { SavedFlag[i]= Stmt->stmt->bind[i].flags & MADB_BIND_DUMMY; Stmt->stmt->bind[i].flags|= MADB_BIND_DUMMY; } while (Offset--) { if (mysql_stmt_fetch(Stmt->stmt) == 1) { result= SQL_ERROR; break; } } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { Stmt->stmt->bind[i].flags &= (~MADB_BIND_DUMMY | SavedFlag[i]); } MADB_FREE(SavedFlag); } return result; } /* }}} */ /* {{{ MADB_StmtDataSeek */ SQLRETURN MADB_StmtDataSeek(MADB_Stmt *Stmt, my_ulonglong FetchOffset) { MYSQL_ROWS *tmp= NULL; if (!Stmt->stmt->result.data) { return SQL_NO_DATA_FOUND; } mysql_stmt_data_seek(Stmt->stmt, FetchOffset); return SQL_SUCCESS; } /* }}} */ /* {{{ */ void QuickDropAllPendingResults(MYSQL* Mariadb) { int Next= 0; do { if (Next == 0) { if (mysql_field_count(Mariadb) > 0) { MYSQL_RES *Res= mysql_store_result(Mariadb); if (Res) { mysql_free_result(Res); } } } } while ((Next= mysql_next_result(Mariadb)) != -1); } /* }}} */ /* {{{ MADB_StmtMoreResults */ SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt) { SQLRETURN ret= SQL_SUCCESS; if (!Stmt->stmt) { return MADB_SetError(&Stmt->Error, MADB_ERR_08S01, NULL, 0); } /* We can't have it in MADB_StmtResetResultStructures, as it breaks dyn_cursor functionality. Thus we free-ing bind structs on move to new result only */ MADB_FREE(Stmt->result); if (Stmt->MultiStmts && !mysql_stmt_more_results(Stmt->stmt)) { if (Stmt->MultiStmtNr == STMT_COUNT(Stmt->Query) - 1) { return SQL_NO_DATA; } ++Stmt->MultiStmtNr; MADB_InstallStmt(Stmt, Stmt->MultiStmts[Stmt->MultiStmtNr]); return SQL_SUCCESS; } /* in case we executed a multi statement, it was done via mysql_query */ if (Stmt->State == MADB_SS_EMULATED) { if (!mysql_more_results(Stmt->Connection->mariadb)) return SQL_NO_DATA; else { int Next; LOCK_MARIADB(Stmt->Connection); Next= mysql_next_result(Stmt->Connection->mariadb); if (Next > 0) { ret= MADB_SetError(&Stmt->Error, MADB_ERR_HY000, mysql_error(Stmt->Connection->mariadb), 0); } else if (mysql_field_count(Stmt->Connection->mariadb) != 0) { MYSQL_RES *Res= mysql_store_result(Stmt->Connection->mariadb); if (Res != NULL) { mysql_free_result(Res); } ret= MADB_SetError(&Stmt->Error, MADB_ERR_01000, "Internal error - unexpected text result received", 0); } else { Stmt->AffectedRows= mysql_affected_rows(Stmt->Connection->mariadb); } Stmt->Connection->Methods->TrackSession(Stmt->Connection); UNLOCK_MARIADB(Stmt->Connection); } return ret; } if (mysql_stmt_more_results(Stmt->stmt)) { mysql_stmt_free_result(Stmt->stmt); } else { return SQL_NO_DATA; } LOCK_MARIADB(Stmt->Connection); if (mysql_stmt_next_result(Stmt->stmt) > 0) { UNLOCK_MARIADB(Stmt->Connection); return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); } Stmt->Connection->Methods->TrackSession(Stmt->Connection); MADB_StmtResetResultStructures(Stmt); if (mysql_stmt_field_count(Stmt->stmt) == 0) { MADB_DescFree(Stmt->Ird, TRUE); Stmt->AffectedRows= mysql_stmt_affected_rows(Stmt->stmt); } else { unsigned int ServerStatus; MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt)); Stmt->AffectedRows= 0; mariadb_get_infov(Stmt->Connection->mariadb, MARIADB_CONNECTION_SERVER_STATUS, (void*)&ServerStatus); if (ServerStatus & SERVER_PS_OUT_PARAMS) { Stmt->State= MADB_SS_OUTPARAMSFETCHED; ret= Stmt->Methods->GetOutParams(Stmt, 0); } else { if (Stmt->Options.CursorType != SQL_CURSOR_FORWARD_ONLY) { mysql_stmt_store_result(Stmt->stmt); mysql_stmt_data_seek(Stmt->stmt, 0); } } } UNLOCK_MARIADB(Stmt->Connection); return ret; } /* }}} */ /* {{{ MADB_RecordsToFetch */ SQLULEN MADB_RowsToFetch(MADB_Cursor *Cursor, SQLULEN ArraySize, unsigned long long RowsInResultst) { SQLULEN Position= Cursor->Position >= 0 ? Cursor->Position : 0; SQLULEN result= ArraySize; Cursor->RowsetSize= ArraySize; if (Position + ArraySize > RowsInResultst) { if (Position >= 0 && RowsInResultst > Position) { result= (SQLULEN)(RowsInResultst - Position); } else { result= 1; } } return result; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_result.h000066400000000000000000000026211414431724000202420ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2018 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_result_h_ #define _ma_result_h_ void MADB_StmtResetResultStructures(MADB_Stmt *Stmt); SQLRETURN MoveNext(MADB_Stmt *Stmt, unsigned long long Offset); SQLRETURN MADB_StmtDataSeek (MADB_Stmt *Stmt, my_ulonglong FetchOffset); SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt); SQLULEN MADB_RowsToFetch(MADB_Cursor *Cursor, SQLULEN ArraySize, unsigned long long RowsInResultst); #endif /* _ma_result_h_ */ mariadb-connector-odbc-3.1.15/ma_server.c000066400000000000000000000065321414431724000202320ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2016, 2017 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* Server-dependent functionality and helpers to use that functionality */ #include unsigned long VersionCapabilityMap[][2]= {{100202, MADB_CAPABLE_EXEC_DIRECT}, {100207, MADB_ENCLOSES_COLUMN_DEF_WITH_QUOTES}, {100202, MADB_SESSION_TRACKING}}; unsigned long MySQLVersionCapabilityMap[][2]= {{050720, MADB_MYSQL_TRANSACTION_ISOLATION}, {050704, MADB_SESSION_TRACKING} }; unsigned long ExtCapabilitiesMap[][2]= {{MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32, MADB_CAPABLE_PARAM_ARRAYS}}; /* {{{ */ void MADB_SetCapabilities(MADB_Dbc *Dbc, unsigned long ServerVersion, const char* ServerName) { int i; unsigned long ServerCapabilities, ServerExtCapabilities; Dbc->IsMySQL= (strcmp(ServerName, "MySQL") == 0); if (Dbc->IsMySQL == 0) { for (i = 0; i < sizeof(VersionCapabilityMap) / sizeof(VersionCapabilityMap[0]); ++i) { if (ServerVersion >= VersionCapabilityMap[i][0]) { Dbc->ServerCapabilities |= VersionCapabilityMap[i][1]; } } } else { for (i = 0; i < sizeof(MySQLVersionCapabilityMap) / sizeof(MySQLVersionCapabilityMap[0]); ++i) { if (ServerVersion >= MySQLVersionCapabilityMap[i][0]) { Dbc->ServerCapabilities |= MySQLVersionCapabilityMap[i][1]; } } } mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, (void*)&ServerExtCapabilities); mariadb_get_infov(Dbc->mariadb, MARIADB_CONNECTION_SERVER_CAPABILITIES, (void*)&ServerCapabilities); for (i= 0; i < sizeof(ExtCapabilitiesMap)/sizeof(ExtCapabilitiesMap[0]); ++i) { if (!(Dbc->mariadb->server_capabilities & CLIENT_MYSQL) && (ServerExtCapabilities & ExtCapabilitiesMap[i][0])) { Dbc->ServerCapabilities |= ExtCapabilitiesMap[i][1]; } } } BOOL MADB_ServerSupports(MADB_Dbc *Dbc, char Capability) { return test(Dbc->ServerCapabilities & Capability); } const char* MADB_GetTxIsolationQuery(MADB_Dbc* Dbc) { return ((Dbc->ServerCapabilities & MADB_MYSQL_TRANSACTION_ISOLATION) != 0 ? "SELECT @@transaction_isolation" : "SELECT @@tx_isolation"); } /* }}} */ const char* MADB_GetTxIsolationVarName(MADB_Dbc* Dbc) { return ((Dbc->ServerCapabilities & MADB_MYSQL_TRANSACTION_ISOLATION) != 0 ? "transaction_isolation" : "tx_isolation"); } /* }}} */ mariadb-connector-odbc-3.1.15/ma_server.h000066400000000000000000000027451414431724000202410ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_server_h #define _ma_server_h #define MADB_CAPABLE_EXEC_DIRECT 1 #define MADB_CAPABLE_PARAM_ARRAYS 2 #define MADB_ENCLOSES_COLUMN_DEF_WITH_QUOTES 4 #define MADB_MYSQL_TRANSACTION_ISOLATION 8 #define MADB_SESSION_TRACKING 16 void MADB_SetCapabilities(MADB_Dbc *Dbc, unsigned long ServerVersion, const char* ServerName); BOOL MADB_ServerSupports (MADB_Dbc *Dbc, char Capability); const char* MADB_GetTxIsolationQuery(MADB_Dbc* Dbc); const char* MADB_GetTxIsolationVarName(MADB_Dbc* Dbc); #endif mariadb-connector-odbc-3.1.15/ma_statement.c000066400000000000000000004136331414431724000207340ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2021 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #define MADB_MIN_QUERY_LEN 5 struct st_ma_stmt_methods MADB_StmtMethods; /* declared at the end of file */ /* {{{ MADB_StmtInit */ SQLRETURN MADB_StmtInit(MADB_Dbc *Connection, SQLHANDLE *pHStmt) { MADB_Stmt *Stmt= NULL; if (!(Stmt = (MADB_Stmt *)MADB_CALLOC(sizeof(MADB_Stmt)))) goto error; MADB_PutErrorPrefix(Connection, &Stmt->Error); *pHStmt= Stmt; Stmt->Connection= Connection; LOCK_MARIADB(Connection); if (!(Stmt->stmt= MADB_NewStmtHandle(Stmt)) || !(Stmt->IApd= MADB_DescInit(Connection, MADB_DESC_APD, FALSE)) || !(Stmt->IArd= MADB_DescInit(Connection, MADB_DESC_ARD, FALSE)) || !(Stmt->IIpd= MADB_DescInit(Connection, MADB_DESC_IPD, FALSE)) || !(Stmt->IIrd= MADB_DescInit(Connection, MADB_DESC_IRD, FALSE))) { UNLOCK_MARIADB(Stmt->Connection); goto error; } MDBUG_C_PRINT(Stmt->Connection, "-->inited %0x", Stmt->stmt); UNLOCK_MARIADB(Connection); Stmt->PutParam= -1; Stmt->Methods= &MADB_StmtMethods; /* default behaviour is SQL_CURSOR_STATIC. But should be SQL_CURSOR_FORWARD_ONLY according to specs(see bug ODBC-290) */ Stmt->Options.CursorType= MA_ODBC_CURSOR_FORWARD_ONLY(Connection) ? SQL_CURSOR_FORWARD_ONLY : SQL_CURSOR_STATIC; Stmt->Options.UseBookmarks= SQL_UB_OFF; Stmt->Options.MetadataId= Connection->MetadataId; Stmt->Apd= Stmt->IApd; Stmt->Ard= Stmt->IArd; Stmt->Ipd= Stmt->IIpd; Stmt->Ird= Stmt->IIrd; Stmt->ListItem.data= (void *)Stmt; EnterCriticalSection(&Stmt->Connection->ListsCs); Stmt->Connection->Stmts= MADB_ListAdd(Stmt->Connection->Stmts, &Stmt->ListItem); LeaveCriticalSection(&Stmt->Connection->ListsCs); Stmt->Ard->Header.ArraySize= 1; return SQL_SUCCESS; error: if (Stmt && Stmt->stmt) { MADB_STMT_CLOSE_STMT(Stmt); } MADB_DescFree(Stmt->IApd, TRUE); MADB_DescFree(Stmt->IArd, TRUE); MADB_DescFree(Stmt->IIpd, TRUE); MADB_DescFree(Stmt->IIrd, TRUE); MADB_FREE(Stmt); return SQL_ERROR; } /* }}} */ /* {{{ MADB_ExecuteQuery */ SQLRETURN MADB_ExecuteQuery(MADB_Stmt * Stmt, char *StatementText, SQLINTEGER TextLength) { SQLRETURN ret= SQL_ERROR; LOCK_MARIADB(Stmt->Connection); if (StatementText) { MDBUG_C_PRINT(Stmt->Connection, "mysql_real_query(%0x,%s,%lu)", Stmt->Connection->mariadb, StatementText, TextLength); if(!mysql_real_query(Stmt->Connection->mariadb, StatementText, TextLength)) { ret= SQL_SUCCESS; MADB_CLEAR_ERROR(&Stmt->Error); Stmt->AffectedRows= mysql_affected_rows(Stmt->Connection->mariadb); Stmt->Connection->Methods->TrackSession(Stmt->Connection); } else { MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_DBC, Stmt->Connection->mariadb); } } else MADB_SetError(&Stmt->Error, MADB_ERR_HY001, mysql_error(Stmt->Connection->mariadb), mysql_errno(Stmt->Connection->mariadb)); UNLOCK_MARIADB(Stmt->Connection); return ret; } /* }}} */ /* {{{ MADB_StmtBulkOperations */ SQLRETURN MADB_StmtBulkOperations(MADB_Stmt *Stmt, SQLSMALLINT Operation) { MADB_CLEAR_ERROR(&Stmt->Error); switch(Operation) { case SQL_ADD: return Stmt->Methods->SetPos(Stmt, 0, SQL_ADD, SQL_LOCK_NO_CHANGE, 0); default: return SQL_ERROR; } } /* }}} */ /* {{{ RemoveStmtRefFromDesc Helper function removing references to the stmt in the descriptor when explisitly allocated descriptor is substituted by some other descriptor */ void RemoveStmtRefFromDesc(MADB_Desc *desc, MADB_Stmt *Stmt, BOOL all) { if (desc->AppType) { unsigned int i; for (i=0; i < desc->Stmts.elements; ++i) { MADB_Stmt **refStmt= ((MADB_Stmt **)desc->Stmts.buffer) + i; if (Stmt == *refStmt) { MADB_DeleteDynamicElement(&desc->Stmts, i); if (!all) { return; } } } } } /* }}} */ /* {{{ ResetMetadata */ void ResetMetadata(MYSQL_RES** metadata, MYSQL_RES* new_metadata) { if (*metadata != NULL) { mysql_free_result(*metadata); } *metadata= new_metadata; } /* }}} */ /* {{{ MADB_StmtFree */ SQLRETURN MADB_StmtFree(MADB_Stmt *Stmt, SQLUSMALLINT Option) { if (!Stmt) return SQL_INVALID_HANDLE; switch (Option) { case SQL_CLOSE: if (Stmt->stmt) { if (Stmt->Ird) MADB_DescFree(Stmt->Ird, TRUE); if (Stmt->State > MADB_SS_PREPARED && !QUERY_IS_MULTISTMT(Stmt->Query)) { MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_free_result(%0x)", Stmt->stmt); mysql_stmt_free_result(Stmt->stmt); LOCK_MARIADB(Stmt->Connection); MDBUG_C_PRINT(Stmt->Connection, "-->resetting %0x", Stmt->stmt); if (mysql_stmt_more_results(Stmt->stmt)) { while (mysql_stmt_next_result(Stmt->stmt) == 0); } UNLOCK_MARIADB(Stmt->Connection); } if (QUERY_IS_MULTISTMT(Stmt->Query) && Stmt->MultiStmts) { unsigned int i; LOCK_MARIADB(Stmt->Connection); for (i=0; i < STMT_COUNT(Stmt->Query); ++i) { if (Stmt->MultiStmts[i] != NULL) { MDBUG_C_PRINT(Stmt->Connection, "-->resetting %0x(%u)", Stmt->MultiStmts[i], i); if (mysql_stmt_more_results(Stmt->MultiStmts[i])) { while (mysql_stmt_next_result(Stmt->MultiStmts[i]) == 0); } } } UNLOCK_MARIADB(Stmt->Connection); } ResetMetadata(&Stmt->metadata, NULL); MADB_FREE(Stmt->result); MADB_FREE(Stmt->CharOffset); MADB_FREE(Stmt->Lengths); RESET_STMT_STATE(Stmt); RESET_DAE_STATUS(Stmt); } break; case SQL_UNBIND: MADB_FREE(Stmt->result); MADB_DescFree(Stmt->Ard, TRUE); break; case SQL_RESET_PARAMS: MADB_FREE(Stmt->params); MADB_DescFree(Stmt->Apd, TRUE); RESET_DAE_STATUS(Stmt); break; case SQL_DROP: MADB_FREE(Stmt->params); MADB_FREE(Stmt->result); MADB_FREE(Stmt->Cursor.Name); MADB_FREE(Stmt->CatalogName); MADB_FREE(Stmt->TableName); ResetMetadata(&Stmt->metadata, NULL); /* For explicit descriptors we only remove reference to the stmt*/ if (Stmt->Apd->AppType) { EnterCriticalSection(&Stmt->Connection->ListsCs); RemoveStmtRefFromDesc(Stmt->Apd, Stmt, TRUE); LeaveCriticalSection(&Stmt->Connection->ListsCs); MADB_DescFree(Stmt->IApd, FALSE); } else { MADB_DescFree( Stmt->Apd, FALSE); } if (Stmt->Ard->AppType) { EnterCriticalSection(&Stmt->Connection->ListsCs); RemoveStmtRefFromDesc(Stmt->Ard, Stmt, TRUE); LeaveCriticalSection(&Stmt->Connection->ListsCs); MADB_DescFree(Stmt->IArd, FALSE); } else { MADB_DescFree(Stmt->Ard, FALSE); } MADB_DescFree(Stmt->Ipd, FALSE); MADB_DescFree(Stmt->Ird, FALSE); MADB_FREE(Stmt->CharOffset); MADB_FREE(Stmt->Lengths); ResetMetadata(&Stmt->DefaultsResult, NULL); if (Stmt->DaeStmt != NULL) { Stmt->DaeStmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); Stmt->DaeStmt= NULL; } EnterCriticalSection(&Stmt->Connection->cs); /* TODO: if multistatement was prepared, but not executed, we would get here Stmt->stmt leaked. Unlikely that is very probable scenario, thus leaving this for new version */ if (QUERY_IS_MULTISTMT(Stmt->Query) && Stmt->MultiStmts) { unsigned int i; for (i= 0; i < STMT_COUNT(Stmt->Query); ++i) { /* This dirty hack allows to avoid crash in case stmt object was not allocated TODO: The better place for this check would be where MultiStmts was not allocated to avoid inconsistency(MultiStmtCount > 0 and MultiStmts is NULL */ if (Stmt->MultiStmts!= NULL && Stmt->MultiStmts[i] != NULL) { MDBUG_C_PRINT(Stmt->Connection, "-->closing %0x(%u)", Stmt->MultiStmts[i], i); mysql_stmt_close(Stmt->MultiStmts[i]); } } MADB_FREE(Stmt->MultiStmts); Stmt->MultiStmtNr= 0; } else if (Stmt->stmt != NULL) { MDBUG_C_PRINT(Stmt->Connection, "-->closing %0x", Stmt->stmt); MADB_STMT_CLOSE_STMT(Stmt); } /* Query has to be deleted after multistmt handles are closed, since the depends on info in the Query */ MADB_DeleteQuery(&Stmt->Query); LeaveCriticalSection(&Stmt->Connection->cs); EnterCriticalSection(&Stmt->Connection->ListsCs); Stmt->Connection->Stmts= MADB_ListDelete(Stmt->Connection->Stmts, &Stmt->ListItem); LeaveCriticalSection(&Stmt->Connection->ListsCs); MADB_FREE(Stmt); } /* End of switch (Option) */ return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_CheckIfExecDirectPossible Checking if we can deploy mariadb_stmt_execute_direct */ BOOL MADB_CheckIfExecDirectPossible(MADB_Stmt *Stmt) { return MADB_ServerSupports(Stmt->Connection, MADB_CAPABLE_EXEC_DIRECT) && !(Stmt->Apd->Header.ArraySize > 1) /* With array of parameters exec_direct will be not optimal */ && MADB_FindNextDaeParam(Stmt->Apd, -1, 1) == MADB_NOPARAM; } /* }}} */ /* {{{ MADB_BulkInsertPossible Checking if we can deploy mariadb_stmt_execute_direct */ BOOL MADB_BulkInsertPossible(MADB_Stmt *Stmt) { return MADB_ServerSupports(Stmt->Connection, MADB_CAPABLE_PARAM_ARRAYS) && (Stmt->Apd->Header.ArraySize > 1) && (Stmt->Apd->Header.BindType == SQL_PARAM_BIND_BY_COLUMN) /* First we support column-wise binding */ && (Stmt->Query.QueryType == MADB_QUERY_INSERT || Stmt->Query.QueryType == MADB_QUERY_UPDATE) && MADB_FindNextDaeParam(Stmt->Apd, -1, 1) == MADB_NOPARAM; /* TODO: should be not very hard ot optimize to use bulk in this case for chunks of the array, delimitered by param rows with DAE In particular, MADB_FindNextDaeParam should consider Stmt->ArrayOffset */ } /* }}} */ /* {{{ MADB_StmtExecDirect */ SQLRETURN MADB_StmtExecDirect(MADB_Stmt *Stmt, char *StatementText, SQLINTEGER TextLength) { SQLRETURN ret; BOOL ExecDirect= TRUE; ret= Stmt->Methods->Prepare(Stmt, StatementText, TextLength, ExecDirect); /* In case statement is not supported, we use mysql_query instead */ if (!SQL_SUCCEEDED(ret)) { /* This is not quite good - 1064 may simply mean that syntax is wrong. we are screwed then */ if ((Stmt->Error.NativeError == 1295/*ER_UNSUPPORTED_PS*/ || Stmt->Error.NativeError == 1064/*ER_PARSE_ERROR*/)) { Stmt->State= MADB_SS_EMULATED; } else { return ret; } } /* For multistmt we don't use mariadb_stmt_execute_direct so far */ if (QUERY_IS_MULTISTMT(Stmt->Query)) { ExecDirect= FALSE; } return Stmt->Methods->Execute(Stmt, ExecDirect); } /* }}} */ /* {{{ MADB_FindCursor */ MADB_Stmt *MADB_FindCursor(MADB_Stmt *Stmt, const char *CursorName) { MADB_Dbc *Dbc= Stmt->Connection; MADB_List *LStmt, *LStmtNext; for (LStmt= Dbc->Stmts; LStmt; LStmt= LStmtNext) { MADB_Cursor *Cursor= &((MADB_Stmt *)LStmt->data)->Cursor; LStmtNext= LStmt->next; if (Stmt != (MADB_Stmt *)LStmt->data && Cursor->Name && _stricmp(Cursor->Name, CursorName) == 0) { return (MADB_Stmt *)LStmt->data; } } MADB_SetError(&Stmt->Error, MADB_ERR_34000, NULL, 0); return NULL; } /* }}} */ /* {{{ FetchMetadata */ MYSQL_RES* FetchMetadata(MADB_Stmt *Stmt) { ResetMetadata(&Stmt->metadata, mysql_stmt_result_metadata(Stmt->stmt)); return Stmt->metadata; } /* }}} */ /* {{{ MADB_StmtReset - reseting Stmt handler for new use. Has to be called inside a lock */ void MADB_StmtReset(MADB_Stmt *Stmt) { if (!QUERY_IS_MULTISTMT(Stmt->Query) || Stmt->MultiStmts == NULL) { if (Stmt->State > MADB_SS_PREPARED) { MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_free_result(%0x)", Stmt->stmt); mysql_stmt_free_result(Stmt->stmt); } if (Stmt->State >= MADB_SS_PREPARED) { MDBUG_C_PRINT(Stmt->Connection, "-->closing %0x", Stmt->stmt); MADB_STMT_CLOSE_STMT(Stmt); Stmt->stmt= MADB_NewStmtHandle(Stmt); MDBUG_C_PRINT(Stmt->Connection, "-->inited %0x", Stmt->stmt); } } else { CloseMultiStatements(Stmt); Stmt->stmt= MADB_NewStmtHandle(Stmt); MDBUG_C_PRINT(Stmt->Connection, "-->inited %0x", Stmt->stmt); } switch (Stmt->State) { case MADB_SS_EXECUTED: case MADB_SS_OUTPARAMSFETCHED: MADB_FREE(Stmt->result); MADB_FREE(Stmt->CharOffset); MADB_FREE(Stmt->Lengths); RESET_DAE_STATUS(Stmt); case MADB_SS_PREPARED: ResetMetadata(&Stmt->metadata, NULL); Stmt->PositionedCursor= NULL; Stmt->Ird->Header.Count= 0; case MADB_SS_EMULATED: /* We can have the case, then query did not succeed, and in case of direct execution we wouldn't have ane state set, but some of stuff still needs to be cleaned. Perhaps we could introduce a state for such case, smth like DIREXEC_PREPARED. Would be more proper, but yet overkill */ if (QUERY_IS_MULTISTMT(Stmt->Query)) { while (mysql_more_results(Stmt->Connection->mariadb)) { mysql_next_result(Stmt->Connection->mariadb); } } default: Stmt->PositionedCommand= 0; Stmt->State= MADB_SS_INITED; MADB_CLEAR_ERROR(&Stmt->Error); } } /* }}} */ /* {{{ MADB_EDPrepare - Method called from SQLPrepare in case it is SQLExecDirect and if server >= 10.2 (i.e. we gonna do mariadb_stmt_exec_direct) */ SQLRETURN MADB_EDPrepare(MADB_Stmt *Stmt) { /* TODO: In case of positioned command it shouldn't be always*/ if ((Stmt->ParamCount= Stmt->Apd->Header.Count + (MADB_POSITIONED_COMMAND(Stmt) ? MADB_POS_COMM_IDX_FIELD_COUNT(Stmt) : 0)) != 0) { if (Stmt->params) { MADB_FREE(Stmt->params); } /* If we have "WHERE CURRENT OF", we will need bind additionaly parameters for each field in the index */ Stmt->params= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * Stmt->ParamCount); } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_RegularPrepare - Method called from SQLPrepare in case it is SQLExecDirect and if !(server > 10.2) (i.e. we aren't going to do mariadb_stmt_exec_direct) */ SQLRETURN MADB_RegularPrepare(MADB_Stmt *Stmt) { LOCK_MARIADB(Stmt->Connection); MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_prepare(%0x,%s)", Stmt->stmt, STMT_STRING(Stmt)); if (mysql_stmt_prepare(Stmt->stmt, STMT_STRING(Stmt), (unsigned long)strlen(STMT_STRING(Stmt)))) { /* Need to save error first */ MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); /* We need to close the stmt here, or it becomes unusable like in ODBC-21 */ MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_close(%0x)", Stmt->stmt); MADB_STMT_CLOSE_STMT(Stmt); Stmt->stmt= MADB_NewStmtHandle(Stmt); UNLOCK_MARIADB(Stmt->Connection); MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_init(%0x)->%0x", Stmt->Connection->mariadb, Stmt->stmt); return Stmt->Error.ReturnValue; } UNLOCK_MARIADB(Stmt->Connection); Stmt->State= MADB_SS_PREPARED; /* If we have result returning query - fill descriptor records with metadata */ if (mysql_stmt_field_count(Stmt->stmt) > 0) { MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt)); } if ((Stmt->ParamCount= (SQLSMALLINT)mysql_stmt_param_count(Stmt->stmt))) { if (Stmt->params) { MADB_FREE(Stmt->params); } Stmt->params= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * Stmt->ParamCount); } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_StmtPrepare */ SQLRETURN MADB_StmtPrepare(MADB_Stmt *Stmt, char *StatementText, SQLINTEGER TextLength, BOOL ExecDirect) { char *CursorName= NULL; unsigned int WhereOffset; BOOL HasParameters= 0; MDBUG_C_PRINT(Stmt->Connection, "%sMADB_StmtPrepare", "\t->"); LOCK_MARIADB(Stmt->Connection); MADB_StmtReset(Stmt); /* After this point we can't have SQL_NTS*/ ADJUST_LENGTH(StatementText, TextLength); /* There is no need to send anything to the server to find out there is syntax error here */ if (TextLength < MADB_MIN_QUERY_LEN) { return MADB_SetError(&Stmt->Error, MADB_ERR_42000, NULL, 0); } MADB_ResetParser(Stmt, StatementText, TextLength); MADB_ParseQuery(&Stmt->Query); if ((Stmt->Query.QueryType == MADB_QUERY_INSERT || Stmt->Query.QueryType == MADB_QUERY_UPDATE || Stmt->Query.QueryType == MADB_QUERY_DELETE) && MADB_FindToken(&Stmt->Query, "RETURNING")) { Stmt->Query.ReturnsResult= '\1'; } /* if we have multiple statements we save single statements in Stmt->StrMultiStmt and store the number in Stmt->MultiStmts */ if (QueryIsPossiblyMultistmt(&Stmt->Query) && QUERY_IS_MULTISTMT(Stmt->Query) && (Stmt->Query.ReturnsResult || Stmt->Query.HasParameters) && Stmt->Query.BatchAllowed) /* If neither of statements returns result, and does not have parameters, we will run them using text protocol */ { if (ExecDirect != FALSE) { return MADB_EDPrepare(Stmt); } /* We had error preparing any of statements */ else if (GetMultiStatements(Stmt, ExecDirect)) { return Stmt->Error.ReturnValue; } /* all statemtens successfully prepared */ UNLOCK_MARIADB(Stmt->Connection); return SQL_SUCCESS; } UNLOCK_MARIADB(Stmt->Connection); if (!MADB_ValidateStmt(&Stmt->Query)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "SQL command SET NAMES is not allowed", 0); return Stmt->Error.ReturnValue; } /* Transform WHERE CURRENT OF [cursorname]: Append WHERE with Parameter Markers In StmtExecute we will call SQLSetPos with update or delete: */ if ((CursorName = MADB_ParseCursorName(&Stmt->Query, &WhereOffset))) { MADB_DynString StmtStr; char *TableName; /* Make sure we have a delete or update statement MADB_QUERY_DELETE and MADB_QUERY_UPDATE defined in the enum to have the same value as SQL_UPDATE and SQL_DELETE, respectively */ if (Stmt->Query.QueryType == MADB_QUERY_DELETE || Stmt->Query.QueryType == MADB_QUERY_UPDATE) { Stmt->PositionedCommand= 1; } else { MADB_SetError(&Stmt->Error, MADB_ERR_42000, "Invalid SQL Syntax: DELETE or UPDATE expected for positioned update", 0); return Stmt->Error.ReturnValue; } if (!(Stmt->PositionedCursor= MADB_FindCursor(Stmt, CursorName))) return Stmt->Error.ReturnValue; TableName= MADB_GetTableName(Stmt->PositionedCursor); MADB_InitDynamicString(&StmtStr, "", 8192, 1024); MADB_DynstrAppendMem(&StmtStr, Stmt->Query.RefinedText, WhereOffset); MADB_DynStrGetWhere(Stmt->PositionedCursor, &StmtStr, TableName, TRUE); MADB_RESET(STMT_STRING(Stmt), StmtStr.str); /* Constructed query we've copied for execution has parameters */ Stmt->Query.HasParameters= 1; MADB_DynstrFree(&StmtStr); } if (Stmt->Options.MaxRows) { /* TODO: LIMIT is not always the last clause. And not applicable to each query type. Thus we need to check query type and last tokens, and possibly put limit before them */ char *p; STMT_STRING(Stmt)= realloc((char *)STMT_STRING(Stmt), strlen(STMT_STRING(Stmt)) + 40); p= STMT_STRING(Stmt) + strlen(STMT_STRING(Stmt)); _snprintf(p, 40, " LIMIT %zd", Stmt->Options.MaxRows); } if (!Stmt->Query.ReturnsResult && !Stmt->Query.HasParameters && /* If have multistatement query, and this is not allowed, we want to do normal prepare. To give it last chance. And to return correct error otherwise */ ! (QUERY_IS_MULTISTMT(Stmt->Query) && !Stmt->Query.BatchAllowed)) { Stmt->State= MADB_SS_EMULATED; return SQL_SUCCESS; } if (ExecDirect && MADB_CheckIfExecDirectPossible(Stmt)) { return MADB_EDPrepare(Stmt); } else { return MADB_RegularPrepare(Stmt); } } /* }}} */ /* {{{ MADB_StmtParamData */ SQLRETURN MADB_StmtParamData(MADB_Stmt *Stmt, SQLPOINTER *ValuePtrPtr) { MADB_Desc *Desc; MADB_DescRecord *Record; int ParamCount; int i; SQLRETURN ret; if (Stmt->DataExecutionType == MADB_DAE_NORMAL) { if (!Stmt->Apd || !(ParamCount= Stmt->ParamCount)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY010, NULL, 0); return Stmt->Error.ReturnValue; } Desc= Stmt->Apd; } else { if (!Stmt->Ard || !(ParamCount= Stmt->DaeStmt->ParamCount)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY010, NULL, 0); return Stmt->Error.ReturnValue; } Desc= Stmt->DaeStmt->Apd; } /* If we have last DAE param(Stmt->PutParam), we are starting from the next one. Otherwise from first */ for (i= Stmt->PutParam > -1 ? Stmt->PutParam + 1 : 0; i < ParamCount; i++) { if ((Record= MADB_DescGetInternalRecord(Desc, i, MADB_DESC_READ))) { if (Record->OctetLengthPtr) { /* Stmt->DaeRowNumber is 1 based */ SQLLEN *OctetLength = (SQLLEN *)GetBindOffset(Desc, Record, Record->OctetLengthPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber - 1 : 0, sizeof(SQLLEN)); if (PARAM_IS_DAE(OctetLength)) { Stmt->PutDataRec= Record; *ValuePtrPtr = GetBindOffset(Desc, Record, Record->DataPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber - 1 : 0, Record->OctetLength); Stmt->PutParam= i; Stmt->Status= SQL_NEED_DATA; return SQL_NEED_DATA; } } } } /* reset status, otherwise SQLSetPos and SQLExecute will fail */ MARK_DAE_DONE(Stmt); if (Stmt->DataExecutionType == MADB_DAE_ADD || Stmt->DataExecutionType == MADB_DAE_UPDATE) { MARK_DAE_DONE(Stmt->DaeStmt); } switch (Stmt->DataExecutionType) { case MADB_DAE_NORMAL: ret= Stmt->Methods->Execute(Stmt, FALSE); RESET_DAE_STATUS(Stmt); break; case MADB_DAE_UPDATE: ret= Stmt->Methods->SetPos(Stmt, Stmt->DaeRowNumber, SQL_UPDATE, SQL_LOCK_NO_CHANGE, 1); RESET_DAE_STATUS(Stmt); break; case MADB_DAE_ADD: ret= Stmt->DaeStmt->Methods->Execute(Stmt->DaeStmt, FALSE); MADB_CopyError(&Stmt->Error, &Stmt->DaeStmt->Error); RESET_DAE_STATUS(Stmt->DaeStmt); break; default: ret= SQL_ERROR; } /* Interesting should we reset if execution failed? */ return ret; } /* }}} */ /* {{{ MADB_StmtPutData */ SQLRETURN MADB_StmtPutData(MADB_Stmt *Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind) { MADB_DescRecord *Record; MADB_Stmt *MyStmt= Stmt; SQLPOINTER ConvertedDataPtr= NULL; SQLULEN Length= 0; MADB_CLEAR_ERROR(&Stmt->Error); if (DataPtr != NULL && StrLen_or_Ind < 0 && StrLen_or_Ind != SQL_NTS && StrLen_or_Ind != SQL_NULL_DATA) { MADB_SetError(&Stmt->Error, MADB_ERR_HY090, NULL, 0); return Stmt->Error.ReturnValue; } if (Stmt->DataExecutionType != MADB_DAE_NORMAL) { MyStmt= Stmt->DaeStmt; } Record= MADB_DescGetInternalRecord(MyStmt->Apd, Stmt->PutParam, MADB_DESC_READ); assert(Record); if (StrLen_or_Ind == SQL_NULL_DATA) { /* Check if we've already sent any data */ if (MyStmt->stmt->params[Stmt->PutParam].long_data_used) { MADB_SetError(&Stmt->Error, MADB_ERR_HY011, NULL, 0); return Stmt->Error.ReturnValue; } Record->Type= SQL_TYPE_NULL; return SQL_SUCCESS; } /* This normally should be enforced by DM */ if (DataPtr == NULL && StrLen_or_Ind != 0) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, NULL, 0); return Stmt->Error.ReturnValue; } /* if (StrLen_or_Ind == SQL_NTS) { if (Record->ConciseType == SQL_C_WCHAR) StrLen_or_Ind= wcslen((SQLWCHAR *)DataPtr); else StrLen_or_Ind= strlen((char *)DataPtr); } */ if (Record->ConciseType == SQL_C_WCHAR) { /* Conn cs */ ConvertedDataPtr= MADB_ConvertFromWChar((SQLWCHAR *)DataPtr, (SQLINTEGER)(StrLen_or_Ind/sizeof(SQLWCHAR)), &Length, &Stmt->Connection->Charset, NULL); if ((ConvertedDataPtr == NULL || Length == 0) && StrLen_or_Ind > 0) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return Stmt->Error.ReturnValue; } } else { if (StrLen_or_Ind == SQL_NTS) { Length= strlen((char *)DataPtr); } else { Length= StrLen_or_Ind; } } /* To make sure that we will not consume the doble amount of memory, we need to send data via mysql_send_long_data directly to the server instead of allocating a separate buffer. This means we need to process Update and Insert statements row by row. */ if (mysql_stmt_send_long_data(MyStmt->stmt, Stmt->PutParam, (ConvertedDataPtr ? (char *)ConvertedDataPtr : DataPtr), (unsigned long)Length)) { MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, MyStmt->stmt); } else { Record->InternalLength+= (unsigned long)Length; } MADB_FREE(ConvertedDataPtr); return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_ExecutePositionedUpdate */ SQLRETURN MADB_ExecutePositionedUpdate(MADB_Stmt *Stmt, BOOL ExecDirect) { SQLSMALLINT j; SQLRETURN ret; MADB_DynArray DynData; MADB_Stmt *SaveCursor; char *p; MADB_CLEAR_ERROR(&Stmt->Error); if (!Stmt->PositionedCursor->result) { MADB_SetError(&Stmt->Error, MADB_ERR_34000, "Cursor has no result set or is not open", 0); return Stmt->Error.ReturnValue; } MADB_StmtDataSeek(Stmt->PositionedCursor, Stmt->PositionedCursor->Cursor.Position); Stmt->Methods->RefreshRowPtrs(Stmt->PositionedCursor); memcpy(&Stmt->Apd->Header, &Stmt->Ard->Header, sizeof(MADB_Header)); Stmt->AffectedRows= 0; MADB_InitDynamicArray(&DynData, sizeof(char *), 8, 8); for (j= 1; j < MADB_POS_COMM_IDX_FIELD_COUNT(Stmt) + 1; ++j) { SQLLEN Length; MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Stmt->PositionedCursor->Ard, j, MADB_DESC_READ); Length= Rec->OctetLength; /* if (Rec->inUse) MA_SQLBindParameter(Stmt, j+1, SQL_PARAM_INPUT, Rec->ConciseType, Rec->Type, Rec->DisplaySize, Rec->Scale, Rec->DataPtr, Length, Rec->OctetLengthPtr); else */ { Stmt->Methods->GetData(Stmt->PositionedCursor, j, SQL_CHAR, NULL, 0, &Length, TRUE); p= (char *)MADB_CALLOC(Length + 2); MADB_InsertDynamic(&DynData, (char *)&p); Stmt->Methods->GetData(Stmt->PositionedCursor, j, SQL_CHAR, p, Length + 1, NULL, TRUE); Stmt->Methods->BindParam(Stmt, j + (Stmt->ParamCount - MADB_POS_COMM_IDX_FIELD_COUNT(Stmt)), SQL_PARAM_INPUT, SQL_CHAR, SQL_CHAR, 0, 0, p, Length, NULL); } } SaveCursor= Stmt->PositionedCursor; Stmt->PositionedCursor= NULL; ret= Stmt->Methods->Execute(Stmt, ExecDirect); Stmt->PositionedCursor= SaveCursor; /* For the case of direct execution we need to restore number of parameters bound by application, for the case when application re-uses handle with same parameters for another query. Otherwise we won't know that number (of application's parameters) */ if (ExecDirect) { Stmt->Apd->Header.Count-= MADB_POS_COMM_IDX_FIELD_COUNT(Stmt); } for (j=0; j < (int)DynData.elements; j++) { MADB_GetDynamic(&DynData, (char *)&p, j); MADB_FREE(p); } MADB_DeleteDynamic(&DynData); if (Stmt->PositionedCursor->Options.CursorType == SQL_CURSOR_DYNAMIC && (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)) { SQLRETURN rc; rc= Stmt->Methods->RefreshDynamicCursor(Stmt->PositionedCursor); if (!SQL_SUCCEEDED(rc)) { MADB_CopyError(&Stmt->Error, &Stmt->PositionedCursor->Error); return Stmt->Error.ReturnValue; } if (Stmt->Query.QueryType == SQL_DELETE) { MADB_STMT_RESET_CURSOR(Stmt->PositionedCursor); } } //MADB_FREE(DataPtr); return ret; } /* }}} */ /* {{{ MADB_GetOutParams */ SQLRETURN MADB_GetOutParams(MADB_Stmt *Stmt, int CurrentOffset) { MYSQL_BIND *Bind; unsigned int i=0, ParameterNr= 0; /* Since Outparams are only one row, we use store result */ if (mysql_stmt_store_result(Stmt->stmt)) { return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt); } Bind= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * mysql_stmt_field_count(Stmt->stmt)); for (i=0; i < (unsigned int)Stmt->ParamCount && ParameterNr < mysql_stmt_field_count(Stmt->stmt); i++) { MADB_DescRecord *IpdRecord, *ApdRecord; if ((IpdRecord= MADB_DescGetInternalRecord(Stmt->Ipd, i, MADB_DESC_READ))!= NULL) { if (IpdRecord->ParameterType == SQL_PARAM_INPUT_OUTPUT || IpdRecord->ParameterType == SQL_PARAM_OUTPUT) { ApdRecord= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ); Bind[ParameterNr].buffer= GetBindOffset(Stmt->Apd, ApdRecord, ApdRecord->DataPtr, CurrentOffset, ApdRecord->OctetLength); if (ApdRecord->OctetLengthPtr) { Bind[ParameterNr].length= (unsigned long *)GetBindOffset(Stmt->Apd, ApdRecord, ApdRecord->OctetLengthPtr, CurrentOffset, sizeof(SQLLEN)); } Bind[ParameterNr].buffer_length= (unsigned long)ApdRecord->OctetLength; Bind[ParameterNr].buffer_type= Stmt->stmt->params[i].buffer_type; ParameterNr++; } } } mysql_stmt_bind_result(Stmt->stmt, Bind); mysql_stmt_fetch(Stmt->stmt); mysql_stmt_data_seek(Stmt->stmt, 0); MADB_FREE(Bind); return SQL_SUCCESS; } /* }}} */ /* {{{ ResetInternalLength */ static void ResetInternalLength(MADB_Stmt *Stmt, unsigned int ParamOffset) { unsigned int i; MADB_DescRecord *ApdRecord; for (i= ParamOffset; i < ParamOffset + Stmt->ParamCount; ++i) { if ((ApdRecord= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ))) { ApdRecord->InternalLength= 0; } } } /* }}} */ /* {{{ MADB_DoExecute */ /* Actually executing on the server, doing required actions with C API, and processing execution result */ SQLRETURN MADB_DoExecute(MADB_Stmt *Stmt, BOOL ExecDirect) { SQLRETURN ret= SQL_SUCCESS; /**************************** mysql_stmt_bind_param **********************************/ if (ExecDirect) { /* Use datatype same as MYSQL_STMT->prebind_param */ unsigned int pCount = (unsigned int)Stmt->ParamCount; mysql_stmt_attr_set(Stmt->stmt, STMT_ATTR_PREBIND_PARAMS, &pCount); } mysql_stmt_attr_set(Stmt->stmt, STMT_ATTR_ARRAY_SIZE, (void*)&Stmt->Bulk.ArraySize); if (Stmt->ParamCount) { mysql_stmt_bind_param(Stmt->stmt, Stmt->params); } ret= SQL_SUCCESS; /**************************** mysql_stmt_execute *************************************/ MDBUG_C_PRINT(Stmt->Connection, ExecDirect ? "mariadb_stmt_execute_direct(%0x,%s)" : "mariadb_stmt_execute(%0x)(%s)", Stmt->stmt, STMT_STRING(Stmt)); if ((ExecDirect && mariadb_stmt_execute_direct(Stmt->stmt, STMT_STRING(Stmt), strlen(STMT_STRING(Stmt)))) || (!ExecDirect && mysql_stmt_execute(Stmt->stmt))) { ret= MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); MDBUG_C_PRINT(Stmt->Connection, "mysql_stmt_execute:ERROR%s", ""); } else { unsigned int ServerStatus; Stmt->State= MADB_SS_EXECUTED; Stmt->Connection->Methods->TrackSession(Stmt->Connection); mariadb_get_infov(Stmt->Connection->mariadb, MARIADB_CONNECTION_SERVER_STATUS, (void*)&ServerStatus); if (ServerStatus & SERVER_PS_OUT_PARAMS) { Stmt->State= MADB_SS_OUTPARAMSFETCHED; ret= Stmt->Methods->GetOutParams(Stmt, 0); } } return ret; } /* }}} */ void MADB_SetStatusArray(MADB_Stmt *Stmt, SQLUSMALLINT Status) { if (Stmt->Ipd->Header.ArrayStatusPtr != NULL) { memset(Stmt->Ipd->Header.ArrayStatusPtr, 0x00ff & Status, Stmt->Apd->Header.ArraySize*sizeof(SQLUSMALLINT)); if (Stmt->Apd->Header.ArrayStatusPtr != NULL) { unsigned int i; for (i= 0; i < Stmt->Apd->Header.ArraySize; ++i) { if (Stmt->Apd->Header.ArrayStatusPtr[i] == SQL_PARAM_IGNORE) { Stmt->Ipd->Header.ArrayStatusPtr[i]= SQL_PARAM_UNUSED; } } } } } /* For first row we just take its result as initial. For the rest, if all rows SQL_SUCCESS or SQL_ERROR - aggregated result is SQL_SUCCESS or SQL_ERROR, respectively Otherwise - SQL_SUCCESS_WITH_INFO */ #define CALC_ALL_ROWS_RC(_accumulated_rc, _cur_row_rc, _row_num)\ if (_row_num == 0) _accumulated_rc= _cur_row_rc;\ else if (_cur_row_rc != _accumulated_rc) _accumulated_rc= SQL_SUCCESS_WITH_INFO /* {{{ MADB_StmtExecute */ SQLRETURN MADB_StmtExecute(MADB_Stmt *Stmt, BOOL ExecDirect) { unsigned int i; MYSQL_RES *DefaultResult= NULL; SQLRETURN ret= SQL_SUCCESS, IntegralRc= SQL_SUCCESS; unsigned int ErrorCount= 0; unsigned int StatementNr; unsigned int ParamOffset= 0; /* for multi statements */ /* Will use it for STMT_ATTR_ARRAY_SIZE and as indicator if we are deploying MariaDB bulk insert feature */ unsigned int MariadbArrSize= MADB_BulkInsertPossible(Stmt) != FALSE ? (unsigned int)Stmt->Apd->Header.ArraySize : 0; SQLULEN j, Start= 0; /* For multistatement direct execution */ char *CurQuery= Stmt->Query.RefinedText, *QueriesEnd= Stmt->Query.RefinedText + Stmt->Query.RefinedLength; MDBUG_C_PRINT(Stmt->Connection, "%sMADB_StmtExecute", "\t->"); MADB_CLEAR_ERROR(&Stmt->Error); if (Stmt->State == MADB_SS_EMULATED) { return MADB_ExecuteQuery(Stmt, STMT_STRING(Stmt), (SQLINTEGER)strlen(STMT_STRING(Stmt))); } if (MADB_POSITIONED_COMMAND(Stmt)) { return MADB_ExecutePositionedUpdate(Stmt, ExecDirect); } /* Stmt->params was allocated during prepare, but could be cleared by SQLResetStmt. In latter case we need to allocate it again */ if (!Stmt->params && !(Stmt->params = (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * MADB_STMT_PARAM_COUNT(Stmt)))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return Stmt->Error.ReturnValue; } /* Normally this check is done by a DM. We are doing that too, keeping in mind direct linking. If exectution routine called from the SQLParamData, DataExecutionType has been reset */ if (Stmt->Status == SQL_NEED_DATA && !DAE_DONE(Stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY010, NULL, 0); } LOCK_MARIADB(Stmt->Connection); Stmt->AffectedRows= 0; Start+= Stmt->ArrayOffset; if (Stmt->Ipd->Header.RowsProcessedPtr) { *Stmt->Ipd->Header.RowsProcessedPtr= 0; } if (MariadbArrSize > 1) { if (MADB_DOING_BULK_OPER(Stmt)) { //MADB_CleanBulkOperationData(Stmt); } Stmt->Bulk.ArraySize= MariadbArrSize; Stmt->Bulk.HasRowsToSkip= 0; } for (StatementNr= 0; StatementNr < STMT_COUNT(Stmt->Query); ++StatementNr) { if (QUERY_IS_MULTISTMT(Stmt->Query)) { if (Stmt->MultiStmts && Stmt->MultiStmts[StatementNr] != NULL) { Stmt->stmt= Stmt->MultiStmts[StatementNr]; } else { /* We have direct execution, since otherwise it'd already prepared, and thus Stmt->MultiStmts would be set */ if (CurQuery >= QueriesEnd) { /* Something went wrong(with parsing). But we've got here, and everything worked. Giving it chance to fail later. This shouldn't really happen */ MDBUG_C_PRINT(Stmt->Connection, "Got past end of query direct-executing %s on stmt #%u", Stmt->Query.RefinedText, StatementNr); continue; } if (StatementNr > 0) { Stmt->stmt= MADB_NewStmtHandle(Stmt); } else { Stmt->MultiStmts= (MYSQL_STMT **)MADB_CALLOC(sizeof(MYSQL_STMT) * STMT_COUNT(Stmt->Query)); } Stmt->MultiStmts[StatementNr]= Stmt->stmt; if (mysql_stmt_prepare(Stmt->stmt, CurQuery, (unsigned long)strlen(CurQuery))) { return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); } CurQuery+= strlen(CurQuery) + 1; } Stmt->RebindParams= TRUE; if (Stmt->ParamCount != mysql_stmt_param_count(Stmt->stmt)) { Stmt->ParamCount= (SQLSMALLINT)mysql_stmt_param_count(Stmt->stmt); Stmt->params= (MYSQL_BIND*)MADB_REALLOC(Stmt->params, sizeof(MYSQL_BIND) * MADB_STMT_PARAM_COUNT(Stmt)); } memset(Stmt->params, 0, sizeof(MYSQL_BIND) * MADB_STMT_PARAM_COUNT(Stmt)); } if (MADB_DOING_BULK_OPER(Stmt)) { if (!SQL_SUCCEEDED(MADB_ExecuteBulk(Stmt, ParamOffset))) { /* Doing just the same thing as we would do in general case */ MADB_CleanBulkOperData(Stmt, ParamOffset); ErrorCount= (unsigned int)Stmt->Apd->Header.ArraySize; MADB_SetStatusArray(Stmt, SQL_PARAM_DIAG_UNAVAILABLE); goto end; } else if (!mysql_stmt_field_count(Stmt->stmt) && !Stmt->MultiStmts) { Stmt->AffectedRows+= mysql_stmt_affected_rows(Stmt->stmt); } /* Suboptimal, but more reliable and simple */ MADB_CleanBulkOperData(Stmt, ParamOffset); Stmt->ArrayOffset+= (int)Stmt->Apd->Header.ArraySize; if (Stmt->Ipd->Header.RowsProcessedPtr) { *Stmt->Ipd->Header.RowsProcessedPtr= *Stmt->Ipd->Header.RowsProcessedPtr + Stmt->Apd->Header.ArraySize; } MADB_SetStatusArray(Stmt, SQL_PARAM_SUCCESS); } else { /* Convert and bind parameters */ for (j= Start; j < Start + Stmt->Apd->Header.ArraySize; ++j) { /* "... In an IPD, this SQLUINTEGER * header field points to a buffer containing the number of sets of parameters that have been processed, including error sets. ..." */ if (Stmt->Ipd->Header.RowsProcessedPtr) { *Stmt->Ipd->Header.RowsProcessedPtr= *Stmt->Ipd->Header.RowsProcessedPtr + 1; } if (Stmt->Apd->Header.ArrayStatusPtr && Stmt->Apd->Header.ArrayStatusPtr[j-Start] == SQL_PARAM_IGNORE) { if (Stmt->Ipd->Header.ArrayStatusPtr) { Stmt->Ipd->Header.ArrayStatusPtr[j-Start]= SQL_PARAM_UNUSED; } continue; } for (i= ParamOffset; i < ParamOffset + MADB_STMT_PARAM_COUNT(Stmt); ++i) { MADB_DescRecord *ApdRecord, *IpdRecord; if ((ApdRecord= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ)) && (IpdRecord= MADB_DescGetInternalRecord(Stmt->Ipd, i, MADB_DESC_READ))) { /* check if parameter was bound */ if (!ApdRecord->inUse) { IntegralRc= MADB_SetError(&Stmt->Error, MADB_ERR_07002, NULL, 0); goto end; } if (MADB_ConversionSupported(ApdRecord, IpdRecord) == FALSE) { IntegralRc= MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); goto end; } Stmt->params[i-ParamOffset].length= NULL; ret= MADB_C2SQL(Stmt, ApdRecord, IpdRecord, j - Start, &Stmt->params[i-ParamOffset]); if (!SQL_SUCCEEDED(ret)) { if (ret == SQL_NEED_DATA) { IntegralRc= ret; ErrorCount= 0; } else { ++ErrorCount; } goto end; } CALC_ALL_ROWS_RC(IntegralRc, ret, j - Start); } } /* End of for() on parameters */ if (Stmt->RebindParams && MADB_STMT_PARAM_COUNT(Stmt)) { Stmt->stmt->bind_param_done= 1; Stmt->RebindParams= FALSE; } ret= MADB_DoExecute(Stmt, ExecDirect && MADB_CheckIfExecDirectPossible(Stmt)); if (!SQL_SUCCEEDED(ret)) { ++ErrorCount; } else { /* We had result from type conversions, thus here we put row as 1(!=0, i.e. not first) */ CALC_ALL_ROWS_RC(IntegralRc, ret, 1); } /* We need to unset InternalLength, i.e. reset dae length counters for next stmt. However that length is not used anywhere, and is not clear what is it needed for */ ResetInternalLength(Stmt, ParamOffset); if (Stmt->Ipd->Header.ArrayStatusPtr) { Stmt->Ipd->Header.ArrayStatusPtr[j-Start]= SQL_SUCCEEDED(ret) ? SQL_PARAM_SUCCESS : (j == Stmt->Apd->Header.ArraySize - 1) ? SQL_PARAM_ERROR : SQL_PARAM_DIAG_UNAVAILABLE; } if (!mysql_stmt_field_count(Stmt->stmt) && SQL_SUCCEEDED(ret) && !Stmt->MultiStmts) { Stmt->AffectedRows+= mysql_stmt_affected_rows(Stmt->stmt); } ++Stmt->ArrayOffset; if (!SQL_SUCCEEDED(ret) && j == Start + Stmt->Apd->Header.ArraySize) { goto end; } } /* End of for() thru paramsets(parameters array) */ } /* End of if (bulk/not bulk) execution */ if (QUERY_IS_MULTISTMT(Stmt->Query)) { /* If we optimize memory allocation, then we will need to free bulk operation data here(among other places) */ /* MADB_CleanBulkOperData(Stmt, ParamOffset); */ ParamOffset+= MADB_STMT_PARAM_COUNT(Stmt); if (mysql_stmt_field_count(Stmt->stmt)) { mysql_stmt_store_result(Stmt->stmt); } } } /* End of for() on statements(Multistatmt) */ /* All rows processed, so we can unset ArrayOffset */ Stmt->ArrayOffset= 0; if (Stmt->MultiStmts) { Stmt->MultiStmtNr= 0; MADB_InstallStmt(Stmt, Stmt->MultiStmts[Stmt->MultiStmtNr]); } else if (mysql_stmt_field_count(Stmt->stmt) > 0) { MADB_StmtResetResultStructures(Stmt); /* Todo: for SQL_CURSOR_FORWARD_ONLY we should use cursor and prefetch rows */ /*************************** mysql_stmt_store_result ******************************/ /*If we did OUT params already, we should not store */ if (Stmt->State == MADB_SS_EXECUTED && mysql_stmt_store_result(Stmt->stmt) != 0) { UNLOCK_MARIADB(Stmt->Connection); if (DefaultResult) { mysql_free_result(DefaultResult); } return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); } /* I don't think we can reliably establish the fact that we do not need to re-fetch the metadata, thus we are re-fetching always The fact that we have resultset has been established above in "if" condition(fields count is > 0) */ MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt)); Stmt->AffectedRows= -1; } end: UNLOCK_MARIADB(Stmt->Connection); Stmt->LastRowFetched= 0; if (DefaultResult) mysql_free_result(DefaultResult); if (ErrorCount) { if (ErrorCount < Stmt->Apd->Header.ArraySize) IntegralRc= SQL_SUCCESS_WITH_INFO; else IntegralRc= SQL_ERROR; } return IntegralRc; } /* }}} */ /* {{{ MADB_StmtBindCol */ SQLRETURN MADB_StmtBindCol(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind) { MADB_Desc *Ard= Stmt->Ard; MADB_DescRecord *Record; if ((ColumnNumber < 1 && Stmt->Options.UseBookmarks == SQL_UB_OFF) || (mysql_stmt_field_count(Stmt->stmt) && Stmt->stmt->state > MYSQL_STMT_PREPARED && ColumnNumber > mysql_stmt_field_count(Stmt->stmt))) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return SQL_ERROR; } /* Bookmark */ if (ColumnNumber == 0) { if (TargetType == SQL_C_BOOKMARK || TargetType == SQL_C_VARBOOKMARK) { Stmt->Options.BookmarkPtr= TargetValuePtr; Stmt->Options.BookmarkLength = BufferLength; Stmt->Options.BookmarkType= TargetType; return SQL_SUCCESS; } MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); return Stmt->Error.ReturnValue; } if (!(Record= MADB_DescGetInternalRecord(Ard, ColumnNumber - 1, MADB_DESC_WRITE))) { MADB_CopyError(&Stmt->Error, &Ard->Error); return Stmt->Error.ReturnValue; } /* check if we need to unbind and delete a record */ if (!TargetValuePtr && !StrLen_or_Ind) { int i; Record->inUse= 0; /* Update counter */ for (i= Ard->Records.elements; i > 0; i--) { MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Ard, i-1, MADB_DESC_READ); if (Rec && Rec->inUse) { Ard->Header.Count= i; return SQL_SUCCESS; } } Ard->Header.Count= 0; return SQL_SUCCESS; } if (!SQL_SUCCEEDED(MADB_DescSetField(Ard, ColumnNumber, SQL_DESC_TYPE, (SQLPOINTER)(SQLLEN)TargetType, SQL_IS_SMALLINT, 0)) || !SQL_SUCCEEDED(MADB_DescSetField(Ard, ColumnNumber, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER)StrLen_or_Ind, SQL_IS_POINTER, 0)) || !SQL_SUCCEEDED(MADB_DescSetField(Ard, ColumnNumber, SQL_DESC_INDICATOR_PTR, (SQLPOINTER)StrLen_or_Ind, SQL_IS_POINTER, 0)) || !SQL_SUCCEEDED(MADB_DescSetField(Ard, ColumnNumber, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)MADB_GetTypeLength(TargetType, BufferLength), SQL_IS_INTEGER, 0)) || !SQL_SUCCEEDED(MADB_DescSetField(Ard, ColumnNumber, SQL_DESC_DATA_PTR, TargetValuePtr, SQL_IS_POINTER, 0))) { MADB_CopyError(&Stmt->Error, &Ard->Error); return Stmt->Error.ReturnValue; } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_StmtBindParam */ SQLRETURN MADB_StmtBindParam(MADB_Stmt *Stmt, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) { MADB_Desc *Apd= Stmt->Apd, *Ipd= Stmt->Ipd; MADB_DescRecord *ApdRecord, *IpdRecord; SQLRETURN ret= SQL_SUCCESS; MADB_CLEAR_ERROR(&Stmt->Error); if (!(ApdRecord= MADB_DescGetInternalRecord(Apd, ParameterNumber - 1, MADB_DESC_WRITE))) { MADB_CopyError(&Stmt->Error, &Apd->Error); return Stmt->Error.ReturnValue; } if (!(IpdRecord= MADB_DescGetInternalRecord(Ipd, ParameterNumber - 1, MADB_DESC_WRITE))) { MADB_CopyError(&Stmt->Error, &Ipd->Error); return Stmt->Error.ReturnValue; } /* Map to the correspoinding type */ if (ValueType == SQL_C_DEFAULT) { ValueType= MADB_GetDefaultType(ParameterType); } if (!(SQL_SUCCEEDED(MADB_DescSetField(Apd, ParameterNumber, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)(SQLLEN)ValueType, SQL_IS_SMALLINT, 0))) || !(SQL_SUCCEEDED(MADB_DescSetField(Apd, ParameterNumber, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER)StrLen_or_IndPtr, SQL_IS_POINTER, 0))) || !(SQL_SUCCEEDED(MADB_DescSetField(Apd, ParameterNumber, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)MADB_GetTypeLength(ValueType, BufferLength), SQL_IS_INTEGER, 0))) || !(SQL_SUCCEEDED(MADB_DescSetField(Apd, ParameterNumber, SQL_DESC_INDICATOR_PTR, (SQLPOINTER)StrLen_or_IndPtr, SQL_IS_POINTER, 0))) || !(SQL_SUCCEEDED(MADB_DescSetField(Apd, ParameterNumber, SQL_DESC_DATA_PTR, ParameterValuePtr, SQL_IS_POINTER, 0)))) { MADB_CopyError(&Stmt->Error, &Apd->Error); return Stmt->Error.ReturnValue; } if (!(SQL_SUCCEEDED(MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)(SQLLEN)ParameterType, SQL_IS_SMALLINT, 0))) || !(SQL_SUCCEEDED(MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_PARAMETER_TYPE, (SQLPOINTER)(SQLLEN)InputOutputType, SQL_IS_SMALLINT, 0)))) { MADB_CopyError(&Stmt->Error, &Ipd->Error); return Stmt->Error.ReturnValue; } switch(ParameterType) { case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_WCHAR: case SQL_WLONGVARCHAR: case SQL_WVARCHAR: ret= MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_LENGTH, (SQLPOINTER)ColumnSize, SQL_IS_INTEGER, 0); break; case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: ret= MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_PRECISION, (SQLPOINTER)ColumnSize, SQL_IS_INTEGER, 0); break; case SQL_DECIMAL: case SQL_NUMERIC: ret= MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_PRECISION, (SQLPOINTER)ColumnSize, SQL_IS_SMALLINT, 0); if (SQL_SUCCEEDED(ret)) ret= MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_SCALE, (SQLPOINTER)(SQLLEN)DecimalDigits, SQL_IS_SMALLINT, 0); break; case SQL_INTERVAL_MINUTE_TO_SECOND: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_SECOND: case SQL_TYPE_TIMESTAMP: case SQL_TYPE_TIME: ret= MADB_DescSetField(Ipd, ParameterNumber, SQL_DESC_PRECISION, (SQLPOINTER)(SQLLEN)DecimalDigits, SQL_IS_SMALLINT, 0); break; } if(!SQL_SUCCEEDED(ret)) MADB_CopyError(&Stmt->Error, &Ipd->Error); Stmt->RebindParams= TRUE; return ret; } /* }}} */ void MADB_InitStatusPtr(SQLUSMALLINT *Ptr, SQLULEN Size, SQLSMALLINT InitialValue) { SQLULEN i; for (i=0; i < Size; i++) Ptr[i]= InitialValue; } /* Not used for now, but leaving it so far here - it may be useful */ /* BOOL MADB_NumericBufferType(SQLSMALLINT BufferType) { switch (BufferType) { case SQL_C_TINYINT: case SQL_C_UTINYINT: case SQL_C_STINYINT: case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_FLOAT: case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_SLONG: case SQL_C_DOUBLE: return TRUE; default: return FALSE; } }*/ /* {{{ MADB_BinaryFieldType */ BOOL MADB_BinaryFieldType(SQLSMALLINT FieldType) { return FieldType == SQL_BINARY || FieldType == SQL_BIT; } /* }}} */ /* {{{ MADB_PrepareBind Filling bind structures in */ SQLRETURN MADB_PrepareBind(MADB_Stmt *Stmt, int RowNumber) { MADB_DescRecord *IrdRec, *ArdRec; int i; void *DataPtr= NULL; for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); ++i) { SQLSMALLINT ConciseType; ArdRec= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ); if (ArdRec == NULL || !ArdRec->inUse) { Stmt->result[i].flags|= MADB_BIND_DUMMY; continue; } DataPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, ArdRec, ArdRec->DataPtr, RowNumber, ArdRec->OctetLength); MADB_FREE(ArdRec->InternalBuffer); if (!DataPtr) { Stmt->result[i].flags|= MADB_BIND_DUMMY; continue; } else { Stmt->result[i].flags&= ~MADB_BIND_DUMMY; } IrdRec= MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_READ); /* assert(IrdRec != NULL) */ /* We can't use application's buffer directly, as it has/can have different size, than C/C needs */ Stmt->result[i].length= &Stmt->result[i].length_value; ConciseType= ArdRec->ConciseType; if (ConciseType == SQL_C_DEFAULT) { ConciseType= IrdRec->ConciseType; } switch(ConciseType) { case SQL_C_WCHAR: /* In worst case for 2 bytes of UTF16 in result, we need 3 bytes of utf8. For ASCII we need 2 times less(for 2 bytes of UTF16 - 1 byte UTF8, in other cases we need same 2 of 4 bytes. */ ArdRec->InternalBuffer= (char *)MADB_CALLOC((size_t)((ArdRec->OctetLength)*1.5)); Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_length= (unsigned long)(ArdRec->OctetLength*1.5); Stmt->result[i].buffer_type= MYSQL_TYPE_STRING; break; case SQL_C_CHAR: Stmt->result[i].buffer= DataPtr; Stmt->result[i].buffer_length= (unsigned long)ArdRec->OctetLength; Stmt->result[i].buffer_type= MYSQL_TYPE_STRING; break; case SQL_C_NUMERIC: MADB_FREE(ArdRec->InternalBuffer); Stmt->result[i].buffer_length= MADB_DEFAULT_PRECISION + 1/*-*/ + 1/*.*/; ArdRec->InternalBuffer= (char *)MADB_CALLOC(Stmt->result[i].buffer_length); Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_type= MYSQL_TYPE_STRING; break; case SQL_TYPE_TIMESTAMP: case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_C_TIMESTAMP: case SQL_C_TIME: case SQL_C_DATE: MADB_FREE(ArdRec->InternalBuffer); if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { ArdRec->InternalBuffer= (char *)MADB_CALLOC(Stmt->stmt->fields[i].max_length + 1); if (ArdRec->InternalBuffer == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_type= MYSQL_TYPE_STRING; Stmt->result[i].buffer_length= Stmt->stmt->fields[i].max_length + 1; } else { ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME)); Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_length= sizeof(MYSQL_TIME); Stmt->result[i].buffer_type= MYSQL_TYPE_TIMESTAMP; } break; case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: { MYSQL_FIELD *Field= mysql_fetch_field_direct(Stmt->metadata, i); MADB_FREE(ArdRec->InternalBuffer); if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { ArdRec->InternalBuffer= (char *)MADB_CALLOC(Stmt->stmt->fields[i].max_length + 1); if (ArdRec->InternalBuffer == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_type= MYSQL_TYPE_STRING; Stmt->result[i].buffer_length= Stmt->stmt->fields[i].max_length + 1; } else { ArdRec->InternalBuffer= (char *)MADB_CALLOC(sizeof(MYSQL_TIME)); Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_length= sizeof(MYSQL_TIME); Stmt->result[i].buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP; } } break; case SQL_C_TINYINT: case SQL_C_UTINYINT: case SQL_C_STINYINT: case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_FLOAT: case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_SLONG: case SQL_C_DOUBLE: if (MADB_BinaryFieldType(IrdRec->ConciseType)) { /* To keep things simple - we will use internal buffer of the column size, and later(in the MADB_FixFetchedValues) will copy (correct part of) it to the application's buffer taking care of endianness. Perhaps it'd be better just not to support this type of conversion */ MADB_FREE(ArdRec->InternalBuffer); ArdRec->InternalBuffer= (char *)MADB_CALLOC(IrdRec->OctetLength); Stmt->result[i].buffer= ArdRec->InternalBuffer; Stmt->result[i].buffer_length= (unsigned long)IrdRec->OctetLength; Stmt->result[i].buffer_type= MYSQL_TYPE_BLOB; break; } /* else {we are falling through below} */ default: if (!MADB_CheckODBCType(ArdRec->ConciseType)) { return MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); } Stmt->result[i].buffer_length= (unsigned long)ArdRec->OctetLength; Stmt->result[i].buffer= DataPtr; Stmt->result[i].buffer_type= MADB_GetMaDBTypeAndLength(ConciseType, &Stmt->result[i].is_unsigned, &Stmt->result[i].buffer_length); break; } } return SQL_SUCCESS; } /* }}} */ /* {{{ LittleEndian */ char LittleEndian() { int x= 1; char *c= (char*)&x; return *c; } /* }}} */ /* {{{ SwitchEndianness */ void SwitchEndianness(char *Src, SQLLEN SrcBytes, char *Dst, SQLLEN DstBytes) { /* SrcBytes can only be less or equal DstBytes */ while (SrcBytes--) { *Dst++= *(Src + SrcBytes); } } /* }}} */ #define CALC_ALL_FLDS_RC(_agg_rc, _field_rc) if (_field_rc != SQL_SUCCESS && _agg_rc != SQL_ERROR) _agg_rc= _field_rc /* {{{ MADB_FixFetchedValues Converting and/or fixing fetched values if needed */ SQLRETURN MADB_FixFetchedValues(MADB_Stmt *Stmt, int RowNumber, MYSQL_ROW_OFFSET SaveCursor) { MADB_DescRecord *IrdRec, *ArdRec; int i; SQLLEN *IndicatorPtr= NULL, *LengthPtr= NULL, Dummy= 0; void *DataPtr= NULL; SQLRETURN rc= SQL_SUCCESS, FieldRc; for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); ++i) { if ((ArdRec= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ)) && ArdRec->inUse) { /* set indicator and dataptr */ LengthPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, ArdRec, ArdRec->OctetLengthPtr, RowNumber, sizeof(SQLLEN)); IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, ArdRec, ArdRec->IndicatorPtr, RowNumber, sizeof(SQLLEN)); DataPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, ArdRec, ArdRec->DataPtr, RowNumber, ArdRec->OctetLength); if (LengthPtr == NULL) { LengthPtr= &Dummy; } /* clear IndicatorPtr */ if (IndicatorPtr != NULL && IndicatorPtr != LengthPtr && *IndicatorPtr < 0) { *IndicatorPtr= 0; } IrdRec= MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_READ); /* assert(IrdRec != NULL) */ if (*Stmt->stmt->bind[i].is_null) { if (IndicatorPtr) { *IndicatorPtr= SQL_NULL_DATA; } else { if (SaveCursor) { mysql_stmt_row_seek(Stmt->stmt, SaveCursor); } rc= MADB_SetError(&Stmt->Error, MADB_ERR_22002, NULL, 0); continue; } } else { switch (ArdRec->ConciseType) { case SQL_C_BIT: { char *p= (char *)Stmt->result[i].buffer; if (p) { *p= test(*p != '\0'); } } break; case SQL_C_TYPE_TIMESTAMP: case SQL_C_TYPE_DATE: case SQL_C_TYPE_TIME: case SQL_C_TIMESTAMP: case SQL_C_TIME: case SQL_C_DATE: { MYSQL_TIME tm, *Intermidiate; if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { BOOL isTime; FieldRc= MADB_Str2Ts(ArdRec->InternalBuffer, *Stmt->stmt->bind[i].length, &tm, FALSE, &Stmt->Error, &isTime); if (SQL_SUCCEEDED(FieldRc)) { Intermidiate= &tm; } else { CALC_ALL_FLDS_RC(rc, FieldRc); break; } } else { Intermidiate= (MYSQL_TIME *)ArdRec->InternalBuffer; } FieldRc= MADB_CopyMadbTimestamp(Stmt, Intermidiate, DataPtr, LengthPtr, IndicatorPtr, ArdRec->Type, IrdRec->ConciseType); CALC_ALL_FLDS_RC(rc, FieldRc); } break; case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: { MYSQL_TIME *tm= (MYSQL_TIME*)ArdRec->InternalBuffer, ForConversion; SQL_INTERVAL_STRUCT *ts= (SQL_INTERVAL_STRUCT *)DataPtr; if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { BOOL isTime; FieldRc= MADB_Str2Ts(ArdRec->InternalBuffer, *Stmt->stmt->bind[i].length, &ForConversion, FALSE, &Stmt->Error, &isTime); if (SQL_SUCCEEDED(FieldRc)) { tm= &ForConversion; } else { CALC_ALL_FLDS_RC(rc, FieldRc); break; } } /* If we have ts == NULL we (may) have tm also NULL, since we didn't really bind this column */ if (ts != NULL) { if (tm->hour > 99999) { FieldRc= MADB_SetError(&Stmt->Error, MADB_ERR_22015, NULL, 0); CALC_ALL_FLDS_RC(rc, FieldRc); break; } ts->intval.day_second.hour= tm->hour; ts->intval.day_second.minute= tm->minute; ts->interval_sign= tm->neg ? SQL_TRUE : SQL_FALSE; if (ArdRec->Type == SQL_C_INTERVAL_HOUR_TO_MINUTE) { ts->intval.day_second.second= 0; ts->interval_type= SQL_INTERVAL_HOUR_TO_MINUTE; if (tm->second) { FieldRc= MADB_SetError(&Stmt->Error, MADB_ERR_01S07, NULL, 0); CALC_ALL_FLDS_RC(rc, FieldRc); break; } } else { ts->interval_type= SQL_INTERVAL_HOUR_TO_SECOND; ts->intval.day_second.second= tm->second; } } *LengthPtr= sizeof(SQL_INTERVAL_STRUCT); } break; case SQL_C_NUMERIC: { int LocalRc= 0; MADB_CLEAR_ERROR(&Stmt->Error); if (DataPtr != NULL && Stmt->result[i].buffer_length < Stmt->stmt->fields[i].max_length) { MADB_SetError(&Stmt->Error, MADB_ERR_22003, NULL, 0); ArdRec->InternalBuffer[Stmt->result[i].buffer_length - 1]= 0; return Stmt->Error.ReturnValue; } if ((LocalRc= MADB_CharToSQLNumeric(ArdRec->InternalBuffer, Stmt->Ard, ArdRec, NULL, RowNumber))) { FieldRc= MADB_SetError(&Stmt->Error, LocalRc, NULL, 0); CALC_ALL_FLDS_RC(rc, FieldRc); } /* TODO: why is it here individually for Numeric type?! */ if (Stmt->Ard->Header.ArrayStatusPtr) { Stmt->Ard->Header.ArrayStatusPtr[RowNumber]= Stmt->Error.ReturnValue; } *LengthPtr= sizeof(SQL_NUMERIC_STRUCT); } break; case SQL_C_WCHAR: { SQLLEN CharLen= MADB_SetString(&Stmt->Connection->Charset, DataPtr, ArdRec->OctetLength/sizeof(SQLWCHAR), (char *)Stmt->result[i].buffer, *Stmt->stmt->bind[i].length, &Stmt->Error); /* If returned len is 0 while source len is not - taking it as error occurred */ if ((CharLen == 0 || (SQLULEN)CharLen > (ArdRec->OctetLength / sizeof(SQLWCHAR))) && *Stmt->stmt->bind[i].length != 0 && *(char*)Stmt->result[i].buffer != '\0' && Stmt->Error.ReturnValue != SQL_SUCCESS) { CALC_ALL_FLDS_RC(rc, Stmt->Error.ReturnValue); } /* Not quite right */ *LengthPtr= CharLen * sizeof(SQLWCHAR); } break; case SQL_C_TINYINT: case SQL_C_UTINYINT: case SQL_C_STINYINT: case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: case SQL_C_FLOAT: case SQL_C_LONG: case SQL_C_ULONG: case SQL_C_SLONG: case SQL_C_DOUBLE: if (MADB_BinaryFieldType(IrdRec->ConciseType)) { if (DataPtr != NULL) { if (Stmt->result[i].buffer_length >= (unsigned long)ArdRec->OctetLength) { if (LittleEndian()) { /* We currently got the bigendian number. If we or littleendian machine, we need to switch bytes */ SwitchEndianness((char*)Stmt->result[i].buffer + Stmt->result[i].buffer_length - ArdRec->OctetLength, ArdRec->OctetLength, (char*)DataPtr, ArdRec->OctetLength); } else { memcpy(DataPtr, (void*)((char*)Stmt->result[i].buffer + Stmt->result[i].buffer_length - ArdRec->OctetLength), ArdRec->OctetLength); } } else { /* We won't write to the whole memory pointed by DataPtr, thus to need to zerofill prior to that */ memset(DataPtr, 0, ArdRec->OctetLength); if (LittleEndian()) { SwitchEndianness((char*)Stmt->result[i].buffer, Stmt->result[i].buffer_length, (char*)DataPtr, ArdRec->OctetLength); } else { memcpy((void*)((char*)DataPtr + ArdRec->OctetLength - Stmt->result[i].buffer_length), Stmt->result[i].buffer, Stmt->result[i].buffer_length); } } *LengthPtr= *Stmt->stmt->bind[i].length; } break; } /* else {we are falling through below} */ default: if (DataPtr != NULL) { if (Stmt->Ard->Header.ArraySize > 1) { if (Stmt->Ard->Header.BindType) { Stmt->result[i].buffer= (char *)Stmt->result[i].buffer + Stmt->Ard->Header.BindType; } else { Stmt->result[i].buffer = (char *)ArdRec->DataPtr + (RowNumber + 1) * ArdRec->OctetLength; } } *LengthPtr= *Stmt->stmt->bind[i].length; } break; } } } } return rc; } /* }}} */ #undef CALC_ALL_FLDS_RC SQLUSMALLINT MADB_MapToRowStatus(SQLRETURN rc) { switch (rc) { case SQL_SUCCESS_WITH_INFO: return SQL_ROW_SUCCESS_WITH_INFO; case SQL_ERROR: return SQL_ROW_ERROR; /* Assuming is that status array pre-filled with SQL_ROW_NOROW, and it never needs to be mapped to */ } return SQL_ROW_SUCCESS; } void ResetDescIntBuffers(MADB_Desc *Desc) { MADB_DescRecord *Rec; SQLSMALLINT i; for (i= 0; i < Desc->Header.Count; ++i) { Rec= MADB_DescGetInternalRecord(Desc, i, MADB_DESC_READ); if (Rec) { MADB_FREE(Rec->InternalBuffer); } } } /* {{{ MADB_StmtFetch */ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt) { unsigned int RowNum, j, rc; SQLULEN Rows2Fetch= Stmt->Ard->Header.ArraySize, Processed, *ProcessedPtr= &Processed; MYSQL_ROW_OFFSET SaveCursor= NULL; SQLRETURN Result= SQL_SUCCESS, RowResult; MADB_CLEAR_ERROR(&Stmt->Error); if (!(MADB_STMT_COLUMN_COUNT(Stmt) > 0)) { return MADB_SetError(&Stmt->Error, MADB_ERR_24000, NULL, 0); } if ((Stmt->Options.UseBookmarks == SQL_UB_VARIABLE && Stmt->Options.BookmarkType == SQL_C_BOOKMARK) || (Stmt->Options.UseBookmarks != SQL_UB_VARIABLE && Stmt->Options.BookmarkType == SQL_C_VARBOOKMARK)) { MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); return Stmt->Error.ReturnValue; } /* We don't have much to do if ArraySize == 0 */ if (Stmt->Ard->Header.ArraySize == 0) { return SQL_SUCCESS; } Stmt->LastRowFetched= 0; Rows2Fetch= MADB_RowsToFetch(&Stmt->Cursor, Stmt->Ard->Header.ArraySize, mysql_stmt_num_rows(Stmt->stmt)); if (Stmt->result == NULL) { if (!(Stmt->result= (MYSQL_BIND *)MADB_CALLOC(sizeof(MYSQL_BIND) * mysql_stmt_field_count(Stmt->stmt)))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return Stmt->Error.ReturnValue; } if (Rows2Fetch > 1) { // We need something to be bound after executing for MoveNext function mysql_stmt_bind_result(Stmt->stmt, Stmt->result); } } if (Rows2Fetch == 0) { return SQL_NO_DATA; } if (Stmt->Ard->Header.ArrayStatusPtr) { MADB_InitStatusPtr(Stmt->Ard->Header.ArrayStatusPtr, Stmt->Ard->Header.ArraySize, SQL_NO_DATA); } if (Stmt->Ird->Header.RowsProcessedPtr) { ProcessedPtr= Stmt->Ird->Header.RowsProcessedPtr; } if (Stmt->Ird->Header.ArrayStatusPtr) { MADB_InitStatusPtr(Stmt->Ird->Header.ArrayStatusPtr, Stmt->Ard->Header.ArraySize, SQL_ROW_NOROW); } *ProcessedPtr= 0; /* We need to return to 1st row in the rowset only if there are >1 rows in it. Otherwise we stay on it anyway */ if (Rows2Fetch > 1 && Stmt->Options.CursorType != SQL_CURSOR_FORWARD_ONLY) { SaveCursor= mysql_stmt_row_tell(Stmt->stmt); /* Skipping current row for for reading now, it will be read when the Cursor is returned to it */ MoveNext(Stmt, 1LL); } for (j= 0; j < Rows2Fetch; ++j) { RowResult= SQL_SUCCESS; /* If we need to return the cursor to 1st row in the rowset, we start to read it from 2nd, and 1st row we read the last */ if (SaveCursor != NULL) { RowNum= j + 1; if (RowNum == Rows2Fetch) { RowNum= 0; Stmt->Cursor.Next= mysql_stmt_row_tell(Stmt->stmt); mysql_stmt_row_seek(Stmt->stmt, SaveCursor); } } else { RowNum= j; } /*************** Setting up BIND structures ********************/ /* Basically, nothing should happen here, but if happens, then it will happen on each row. Thus it's ok to stop */ RETURN_ERROR_OR_CONTINUE(MADB_PrepareBind(Stmt, RowNum)); /************************ Bind! ********************************/ mysql_stmt_bind_result(Stmt->stmt, Stmt->result); if (Stmt->Options.UseBookmarks && Stmt->Options.BookmarkPtr != NULL) { /* TODO: Bookmark can be not only "unsigned long*", but also "unsigned char*". Can be determined by examining Stmt->Options.BookmarkType */ long *p= (long *)Stmt->Options.BookmarkPtr; p+= RowNum * Stmt->Options.BookmarkLength; *p= (long)Stmt->Cursor.Position; } /************************ Fetch! ********************************/ rc= mysql_stmt_fetch(Stmt->stmt); *ProcessedPtr += 1; if (Stmt->Cursor.Position < 0) { Stmt->Cursor.Position= 0; } switch(rc) { case 1: RowResult= MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); /* If mysql_stmt_fetch returned error, there is no sense to continue */ if (Stmt->Ird->Header.ArrayStatusPtr) { Stmt->Ird->Header.ArrayStatusPtr[RowNum]= MADB_MapToRowStatus(RowResult); } CALC_ALL_ROWS_RC(Result, RowResult, RowNum); return Result; case MYSQL_DATA_TRUNCATED: { /* We will not report truncation if a dummy buffer was bound */ int col; for (col = 0; col < MADB_STMT_COLUMN_COUNT(Stmt); ++col) { if (Stmt->stmt->bind[col].error && *Stmt->stmt->bind[col].error > 0 && !(Stmt->stmt->bind[col].flags & MADB_BIND_DUMMY)) { MADB_DescRecord* ArdRec = MADB_DescGetInternalRecord(Stmt->Ard, col, MADB_DESC_READ), * IrdRec = MADB_DescGetInternalRecord(Stmt->Ird, col, MADB_DESC_READ); /* If (numeric) field value and buffer are of the same size - ignoring truncation. In some cases specs are not clear enough if certain column signed or not(think of catalog functions for example), and some apps bind signed buffer where we return unsigdned value. And in general - if application want to fetch unsigned as signed, or vice versa, why we should prevent that. */ if (ArdRec->OctetLength == IrdRec->OctetLength && MADB_IsIntType(IrdRec->ConciseType) && (ArdRec->ConciseType == SQL_C_DEFAULT || MADB_IsIntType(ArdRec->ConciseType))) { continue; } /* For numeric types we return either 22003 or 01S07, 01004 for the rest. if ird type is not fractional - we return 22003. But as a matter of fact, it's possible that we have 22003 if converting from fractional types */ RowResult= MADB_SetError(&Stmt->Error, ArdRec != NULL && MADB_IsNumericType(ArdRec->ConciseType) ? (MADB_IsIntType(IrdRec->ConciseType) ? MADB_ERR_22003 : MADB_ERR_01S07) : MADB_ERR_01004, NULL, 0); /* One found such column is enough */ break; } } break; } case MYSQL_NO_DATA: /* We have already incremented this counter, since there was no more rows, need to decrement */ --*ProcessedPtr; /* SQL_NO_DATA should be only returned if first fetched row is already beyond end of the resultset */ if (RowNum > 0) { continue; } return SQL_NO_DATA; } /* End of switch on fetch result */ ++Stmt->LastRowFetched; ++Stmt->PositionedCursor; /*Conversion etc. At this point, after fetch we can have RowResult either SQL_SUCCESS or SQL_SUCCESS_WITH_INFO */ switch (MADB_FixFetchedValues(Stmt, RowNum, SaveCursor)) { case SQL_ERROR: RowResult= SQL_ERROR; break; case SQL_SUCCESS_WITH_INFO: RowResult= SQL_SUCCESS_WITH_INFO; /* And if result of conversions - success, just leaving that we had before */ } CALC_ALL_ROWS_RC(Result, RowResult, RowNum); if (Stmt->Ird->Header.ArrayStatusPtr) { Stmt->Ird->Header.ArrayStatusPtr[RowNum]= MADB_MapToRowStatus(RowResult); } } memset(Stmt->CharOffset, 0, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); memset(Stmt->Lengths, 0, sizeof(long) * mysql_stmt_field_count(Stmt->stmt)); ResetDescIntBuffers(Stmt->Ird); return Result; } /* }}} */ #undef CALC_ALL_ROWS_RC /* {{{ MADB_StmtGetAttr */ SQLRETURN MADB_StmtGetAttr(MADB_Stmt *Stmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { SQLINTEGER StringLength; SQLRETURN ret= SQL_SUCCESS; if (!StringLengthPtr) StringLengthPtr= &StringLength; if (!Stmt) return SQL_INVALID_HANDLE; switch(Attribute) { case SQL_ATTR_APP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= Stmt->Apd; *StringLengthPtr= sizeof(SQLPOINTER *); break; case SQL_ATTR_APP_ROW_DESC: *(SQLPOINTER *)ValuePtr= Stmt->Ard; *StringLengthPtr= sizeof(SQLPOINTER *); break; case SQL_ATTR_IMP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= Stmt->Ipd; *StringLengthPtr= sizeof(SQLPOINTER *); break; case SQL_ATTR_IMP_ROW_DESC: *(SQLPOINTER *)ValuePtr= Stmt->Ird; *StringLengthPtr= sizeof(SQLPOINTER *); break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: *(SQLPOINTER *)ValuePtr= Stmt->Apd->Header.BindOffsetPtr; break; case SQL_ATTR_PARAM_BIND_TYPE: *(SQLULEN *)ValuePtr= Stmt->Apd->Header.BindType; break; case SQL_ATTR_PARAM_OPERATION_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)Stmt->Apd->Header.ArrayStatusPtr; break; case SQL_ATTR_PARAM_STATUS_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)Stmt->Ipd->Header.ArrayStatusPtr; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)(SQLULEN)Stmt->Ipd->Header.BindType; break; case SQL_ATTR_PARAMSET_SIZE: *(SQLULEN *)ValuePtr= Stmt->Apd->Header.ArraySize; break; case SQL_ATTR_ASYNC_ENABLE: *(SQLPOINTER *)ValuePtr= SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: *(SQLULEN *)ValuePtr= Stmt->Ard->Header.ArraySize; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)Stmt->Ard->Header.BindOffsetPtr; break; case SQL_ATTR_ROW_BIND_TYPE: *(SQLULEN *)ValuePtr= Stmt->Ard->Header.BindType; break; case SQL_ATTR_ROW_OPERATION_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)Stmt->Ard->Header.ArrayStatusPtr; break; case SQL_ATTR_ROW_STATUS_PTR: *(SQLPOINTER *)ValuePtr= (SQLPOINTER)Stmt->Ird->Header.ArrayStatusPtr; break; case SQL_ATTR_ROWS_FETCHED_PTR: *(SQLULEN **)ValuePtr= Stmt->Ird->Header.RowsProcessedPtr; break; case SQL_ATTR_USE_BOOKMARKS: *(SQLUINTEGER *)ValuePtr= Stmt->Options.UseBookmarks; case SQL_ATTR_SIMULATE_CURSOR: *(SQLULEN *)ValuePtr= Stmt->Options.SimulateCursor; break; case SQL_ATTR_CURSOR_SCROLLABLE: *(SQLULEN *)ValuePtr= Stmt->Options.CursorType; break; case SQL_ATTR_CURSOR_SENSITIVITY: *(SQLULEN *)ValuePtr= SQL_UNSPECIFIED; break; case SQL_ATTR_CURSOR_TYPE: *(SQLULEN *)ValuePtr= Stmt->Options.CursorType; break; case SQL_ATTR_CONCURRENCY: *(SQLULEN *)ValuePtr= SQL_CONCUR_READ_ONLY; break; case SQL_ATTR_ENABLE_AUTO_IPD: *(SQLULEN *)ValuePtr= SQL_FALSE; break; case SQL_ATTR_MAX_LENGTH: *(SQLULEN *)ValuePtr= Stmt->Options.MaxLength; break; case SQL_ATTR_MAX_ROWS: *(SQLULEN *)ValuePtr= Stmt->Options.MaxRows; break; case SQL_ATTR_METADATA_ID: /* SQL_ATTR_METADATA_ID is SQLUINTEGER attribute on connection level, but SQLULEN on statement level :/ */ *(SQLULEN *)ValuePtr= Stmt->Options.MetadataId; break; case SQL_ATTR_NOSCAN: *(SQLULEN *)ValuePtr= SQL_NOSCAN_ON; break; case SQL_ATTR_QUERY_TIMEOUT: *(SQLULEN *)ValuePtr= 0; break; case SQL_ATTR_RETRIEVE_DATA: *(SQLULEN *)ValuePtr= SQL_RD_ON; break; } return ret; } /* }}} */ /* {{{ MADB_StmtSetAttr */ SQLRETURN MADB_StmtSetAttr(MADB_Stmt *Stmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { SQLRETURN ret= SQL_SUCCESS; if (!Stmt) return SQL_INVALID_HANDLE; switch(Attribute) { case SQL_ATTR_APP_PARAM_DESC: if (ValuePtr) { MADB_Desc *Desc= (MADB_Desc *)ValuePtr; if (!Desc->AppType && Desc != Stmt->IApd) { MADB_SetError(&Stmt->Error, MADB_ERR_HY017, NULL, 0); return Stmt->Error.ReturnValue; } if (Desc->DescType != MADB_DESC_APD && Desc->DescType != MADB_DESC_UNKNOWN) { MADB_SetError(&Stmt->Error, MADB_ERR_HY024, NULL, 0); return Stmt->Error.ReturnValue; } RemoveStmtRefFromDesc(Stmt->Apd, Stmt, FALSE); Stmt->Apd= (MADB_Desc *)ValuePtr; Stmt->Apd->DescType= MADB_DESC_APD; if (Stmt->Apd != Stmt->IApd) { MADB_Stmt **IntStmt; IntStmt = (MADB_Stmt **)MADB_AllocDynamic(&Stmt->Apd->Stmts); *IntStmt= Stmt; } } else { RemoveStmtRefFromDesc(Stmt->Apd, Stmt, FALSE); Stmt->Apd= Stmt->IApd; } break; case SQL_ATTR_APP_ROW_DESC: if (ValuePtr) { MADB_Desc *Desc= (MADB_Desc *)ValuePtr; if (!Desc->AppType && Desc != Stmt->IArd) { MADB_SetError(&Stmt->Error, MADB_ERR_HY017, NULL, 0); return Stmt->Error.ReturnValue; } if (Desc->DescType != MADB_DESC_ARD && Desc->DescType != MADB_DESC_UNKNOWN) { MADB_SetError(&Stmt->Error, MADB_ERR_HY024, NULL, 0); return Stmt->Error.ReturnValue; } RemoveStmtRefFromDesc(Stmt->Ard, Stmt, FALSE); Stmt->Ard= Desc; Stmt->Ard->DescType= MADB_DESC_ARD; if (Stmt->Ard != Stmt->IArd) { MADB_Stmt **IntStmt; IntStmt = (MADB_Stmt **)MADB_AllocDynamic(&Stmt->Ard->Stmts); *IntStmt= Stmt; } } else { RemoveStmtRefFromDesc(Stmt->Ard, Stmt, FALSE); Stmt->Ard= Stmt->IArd; } break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: Stmt->Apd->Header.BindOffsetPtr= (SQLULEN*)ValuePtr; break; case SQL_ATTR_PARAM_BIND_TYPE: Stmt->Apd->Header.BindType= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_ATTR_PARAM_OPERATION_PTR: Stmt->Apd->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; break; case SQL_ATTR_PARAM_STATUS_PTR: Stmt->Ipd->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: Stmt->Ipd->Header.RowsProcessedPtr = (SQLULEN *)ValuePtr; break; case SQL_ATTR_PARAMSET_SIZE: Stmt->Apd->Header.ArraySize= (SQLULEN)ValuePtr; break; case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: Stmt->Ard->Header.ArraySize= (SQLULEN)ValuePtr; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: Stmt->Ard->Header.BindOffsetPtr= (SQLULEN*)ValuePtr; break; case SQL_ATTR_ROW_BIND_TYPE: Stmt->Ard->Header.BindType= (SQLINTEGER)(SQLLEN)ValuePtr; break; case SQL_ATTR_ROW_OPERATION_PTR: Stmt->Ard->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; break; case SQL_ATTR_ROW_STATUS_PTR: Stmt->Ird->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; break; case SQL_ATTR_ROWS_FETCHED_PTR: Stmt->Ird->Header.RowsProcessedPtr= (SQLULEN*)ValuePtr; break; case SQL_ATTR_ASYNC_ENABLE: if ((SQLULEN)ValuePtr != SQL_ASYNC_ENABLE_OFF) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_ATTR_ASYNC_ENABLE)", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_SIMULATE_CURSOR: Stmt->Options.SimulateCursor= (SQLULEN) ValuePtr; break; case SQL_ATTR_CURSOR_SCROLLABLE: Stmt->Options.CursorType= ((SQLULEN)ValuePtr == SQL_NONSCROLLABLE) ? SQL_CURSOR_FORWARD_ONLY : SQL_CURSOR_STATIC; break; case SQL_ATTR_CURSOR_SENSITIVITY: /* we only support default value = SQL_UNSPECIFIED */ if ((SQLULEN)ValuePtr != SQL_UNSPECIFIED) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default cursor sensitivity", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_CURSOR_TYPE: /* We need to check global DSN/Connection settings */ if (MA_ODBC_CURSOR_FORWARD_ONLY(Stmt->Connection) && (SQLULEN)ValuePtr != SQL_CURSOR_FORWARD_ONLY) { Stmt->Options.CursorType= SQL_CURSOR_FORWARD_ONLY; MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_CURSOR_FORWARD_ONLY)", 0); return Stmt->Error.ReturnValue; } else if (MA_ODBC_CURSOR_DYNAMIC(Stmt->Connection)) { if ((SQLULEN)ValuePtr == SQL_CURSOR_KEYSET_DRIVEN) { Stmt->Options.CursorType= SQL_CURSOR_STATIC; MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_CURSOR_STATIC)", 0); return Stmt->Error.ReturnValue; } Stmt->Options.CursorType= (SQLUINTEGER)(SQLULEN)ValuePtr; } /* only FORWARD or Static is allowed */ else { if ((SQLULEN)ValuePtr != SQL_CURSOR_FORWARD_ONLY && (SQLULEN)ValuePtr != SQL_CURSOR_STATIC) { Stmt->Options.CursorType= SQL_CURSOR_STATIC; MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_CURSOR_STATIC)", 0); return Stmt->Error.ReturnValue; } Stmt->Options.CursorType= (SQLUINTEGER)(SQLULEN)ValuePtr; } break; case SQL_ATTR_CONCURRENCY: if ((SQLULEN)ValuePtr != SQL_CONCUR_READ_ONLY) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_CONCUR_READ_ONLY). ", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_ENABLE_AUTO_IPD: /* MariaDB doesn't deliver param metadata after prepare, so we can't autopopulate ird */ MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, NULL, 0); return Stmt->Error.ReturnValue; break; case SQL_ATTR_MAX_LENGTH: Stmt->Options.MaxLength= (SQLULEN)ValuePtr; break; case SQL_ATTR_MAX_ROWS: Stmt->Options.MaxRows= (SQLULEN)ValuePtr; break; case SQL_ATTR_METADATA_ID: Stmt->Options.MetadataId= (SQLULEN)ValuePtr; break; case SQL_ATTR_NOSCAN: if ((SQLULEN)ValuePtr != SQL_NOSCAN_ON) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_NOSCAN_ON)", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_QUERY_TIMEOUT: if ((SQLULEN)ValuePtr != 0) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (no timeout)", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_RETRIEVE_DATA: if ((SQLULEN)ValuePtr != SQL_RD_ON) { MADB_SetError(&Stmt->Error, MADB_ERR_01S02, "Option value changed to default (SQL_RD_ON)", 0); ret= SQL_SUCCESS_WITH_INFO; } break; case SQL_ATTR_USE_BOOKMARKS: Stmt->Options.UseBookmarks= (SQLUINTEGER)(SQLULEN)ValuePtr; break; case SQL_ATTR_FETCH_BOOKMARK_PTR: MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, NULL, 0); return Stmt->Error.ReturnValue; break; default: MADB_SetError(&Stmt->Error, MADB_ERR_HY024, NULL, 0); return Stmt->Error.ReturnValue; break; } return ret; } /* }}} */ SQLRETURN MADB_GetBookmark(MADB_Stmt *Stmt, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) { if (Stmt->Options.UseBookmarks == SQL_UB_OFF) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return Stmt->Error.ReturnValue; } if ((Stmt->Options.UseBookmarks == SQL_UB_VARIABLE && TargetType != SQL_C_VARBOOKMARK) || (Stmt->Options.UseBookmarks != SQL_UB_VARIABLE && TargetType == SQL_C_VARBOOKMARK)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY003, NULL, 0); return Stmt->Error.ReturnValue; } if (TargetValuePtr && TargetType == SQL_C_BOOKMARK && BufferLength <= sizeof(SQLULEN)) { *(SQLULEN *)TargetValuePtr= Stmt->Cursor.Position; if (StrLen_or_IndPtr) { *StrLen_or_IndPtr= sizeof(SQLULEN); } return SQL_SUCCESS; } /* Keeping compiler happy */ return SQL_SUCCESS; } /* {{{ MADB_StmtGetData */ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle, SQLUSMALLINT Col_or_Param_Num, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr, BOOL InternalUse /* Currently this is respected for SQL_CHAR type only, since all "internal" calls of the function need string representation of datat */) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLUSMALLINT Offset= Col_or_Param_Num - 1; SQLSMALLINT OdbcType= 0, MadbType= 0; MYSQL_BIND Bind; my_bool IsNull= FALSE; my_bool ZeroTerminated= 0; unsigned long CurrentOffset= InternalUse == TRUE ? 0 : Stmt->CharOffset[Offset]; /* We are supposed not get bookmark column here */ my_bool Error; MADB_DescRecord *IrdRec= NULL; MYSQL_FIELD *Field= mysql_fetch_field_direct(Stmt->metadata, Offset); MADB_CLEAR_ERROR(&Stmt->Error); /* Should not really happen, and is evidence of that something wrong happened in some previous call(SQLFetch?) */ if (Stmt->stmt->bind == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY109, NULL, 0); return Stmt->Error.ReturnValue; } if (Stmt->stmt->bind[Offset].is_null != NULL && *Stmt->stmt->bind[Offset].is_null != '\0') { if (!StrLen_or_IndPtr) { MADB_SetError(&Stmt->Error, MADB_ERR_22002, NULL, 0); return Stmt->Error.ReturnValue; } *StrLen_or_IndPtr= SQL_NULL_DATA; return SQL_SUCCESS; } memset(&Bind, 0, sizeof(MYSQL_BIND)); /* We might need it for SQL_C_DEFAULT type, or to obtain length of fixed length types(Access likes to have it) */ IrdRec= MADB_DescGetInternalRecord(Stmt->Ird, Offset, MADB_DESC_READ); if (!IrdRec) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return Stmt->Error.ReturnValue; } switch (TargetType) { case SQL_ARD_TYPE: { MADB_DescRecord *Ard= MADB_DescGetInternalRecord(Stmt->Ard, Offset, MADB_DESC_READ); if (!Ard) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return Stmt->Error.ReturnValue; } OdbcType= Ard->ConciseType; } break; case SQL_C_DEFAULT: { /* Taking type from IRD record. This way, if mysql type was fixed(currently that is mainly for catalog functions, we don't lose it. (Access uses default types on getting catalog functions results, and not quite happy when it gets something unexpected. Seemingly it cares about returned data lenghts even for types, for which standard says application should not care about */ OdbcType= IrdRec->ConciseType; } break; default: OdbcType= TargetType; break; } /* Restoring mariadb/mysql type from odbc type */ MadbType= MADB_GetMaDBTypeAndLength(OdbcType, &Bind.is_unsigned, &Bind.buffer_length); /* set global values for Bind */ Bind.error= &Error; Bind.length= &Bind.length_value; Bind.is_null= &IsNull; switch(OdbcType) { case SQL_DATE: case SQL_C_TYPE_DATE: case SQL_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: case SQL_TIME: case SQL_C_TYPE_TIME: { MYSQL_TIME tm; if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { char *ClientValue= NULL; BOOL isTime; if (!(ClientValue = (char *)MADB_CALLOC(Stmt->stmt->fields[Offset].max_length + 1))) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } Bind.buffer= ClientValue; Bind.buffer_type= MYSQL_TYPE_STRING; Bind.buffer_length= Stmt->stmt->fields[Offset].max_length + 1; mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(ClientValue, Bind.length_value, &tm, FALSE, &Stmt->Error, &isTime)); } else { Bind.buffer_length= sizeof(MYSQL_TIME); Bind.buffer= (void *)&tm; /* c/c is too smart to convert hours to days and days to hours, we don't need that */ if ((OdbcType == SQL_C_TIME || OdbcType == SQL_C_TYPE_TIME) && (IrdRec->ConciseType == SQL_TIME || IrdRec->ConciseType == SQL_TYPE_TIME)) { Bind.buffer_type= MYSQL_TYPE_TIME; } else { Bind.buffer_type= MYSQL_TYPE_TIMESTAMP; } mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); } RETURN_ERROR_OR_CONTINUE(MADB_CopyMadbTimestamp(Stmt, &tm, TargetValuePtr, StrLen_or_IndPtr, StrLen_or_IndPtr, OdbcType, IrdRec->ConciseType)); break; } case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: { MYSQL_TIME tm; SQL_INTERVAL_STRUCT *ts= (SQL_INTERVAL_STRUCT *)TargetValuePtr; if (IrdRec->ConciseType == SQL_CHAR || IrdRec->ConciseType == SQL_VARCHAR) { char *ClientValue= NULL; BOOL isTime; if (!(ClientValue = (char *)MADB_CALLOC(Stmt->stmt->fields[Offset].max_length + 1))) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } Bind.buffer= ClientValue; Bind.buffer_type= MYSQL_TYPE_STRING; Bind.buffer_length= Stmt->stmt->fields[Offset].max_length + 1; mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(ClientValue, Bind.length_value, &tm, TRUE, &Stmt->Error, &isTime)); } else { Bind.buffer_length= sizeof(MYSQL_TIME); Bind.buffer= (void *)&tm; /* c/c is too smart to convert hours to days and days to hours, we don't need that */ Bind.buffer_type= Field && Field->type == MYSQL_TYPE_TIME ? MYSQL_TYPE_TIME : MYSQL_TYPE_TIMESTAMP; mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); } if (tm.hour > 99999) { return MADB_SetError(&Stmt->Error, MADB_ERR_22015, NULL, 0); } ts->intval.day_second.hour= tm.hour; ts->intval.day_second.minute= tm.minute; ts->interval_sign= tm.neg ? SQL_TRUE : SQL_FALSE; if (TargetType == SQL_C_INTERVAL_HOUR_TO_MINUTE) { ts->intval.day_second.second= 0; ts->interval_type= SQL_INTERVAL_HOUR_TO_MINUTE; if (tm.second) { return MADB_SetError(&Stmt->Error, MADB_ERR_01S07, NULL, 0); } } else { ts->interval_type= SQL_INTERVAL_HOUR_TO_SECOND; ts->intval.day_second.second= tm.second; } if (StrLen_or_IndPtr) { *StrLen_or_IndPtr= sizeof(SQL_INTERVAL_STRUCT); } } break; case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: { char *ClientValue= NULL; size_t CharLength= 0; /* Kinda this it not 1st call for this value, and we have it nice and recoded */ if (IrdRec->InternalBuffer == NULL/* && Stmt->Lengths[Offset] == 0*/) { if (!(ClientValue = (char *)MADB_CALLOC(Stmt->stmt->fields[Offset].max_length + 1))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return Stmt->Error.ReturnValue; } Bind.buffer= ClientValue; Bind.buffer_type= MYSQL_TYPE_STRING; Bind.buffer_length= Stmt->stmt->fields[Offset].max_length + 1; if (mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, Stmt->CharOffset[Offset])) { MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); return Stmt->Error.ReturnValue; } /* check total length: if not enough space, we need to calculate new CharOffset for next fetch */ if (Stmt->stmt->fields[Offset].max_length) { size_t ReqBuffOctetLen; /* Size in chars */ CharLength= MbstrCharLen(ClientValue, Stmt->stmt->fields[Offset].max_length - Stmt->CharOffset[Offset], Stmt->Connection->Charset.cs_info); /* MbstrCharLen gave us length in characters. For encoding of each character we might need 2 SQLWCHARs in case of UTF16, or 1 SQLWCHAR in case of UTF32. Probably we need calcualate better number of required SQLWCHARs */ ReqBuffOctetLen= (CharLength + 1)*(4/sizeof(SQLWCHAR))*sizeof(SQLWCHAR); if (BufferLength) { /* Buffer is not big enough. Alocating InternalBuffer. MADB_SetString would do that anyway if - allocate buffer fitting the whole wide string, and then copied its part to the application's buffer */ if (ReqBuffOctetLen > (size_t)BufferLength) { IrdRec->InternalBuffer= (char*)MADB_CALLOC(ReqBuffOctetLen); if (IrdRec->InternalBuffer == 0) { MADB_FREE(ClientValue); return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } CharLength= MADB_SetString(&Stmt->Connection->Charset, IrdRec->InternalBuffer, (SQLINTEGER)ReqBuffOctetLen / sizeof(SQLWCHAR), ClientValue, Stmt->stmt->fields[Offset].max_length - Stmt->CharOffset[Offset], &Stmt->Error); } else { /* Application's buffer is big enough - writing directly there */ CharLength= MADB_SetString(&Stmt->Connection->Charset, TargetValuePtr, (SQLINTEGER)(BufferLength / sizeof(SQLWCHAR)), ClientValue, Stmt->stmt->fields[Offset].max_length - Stmt->CharOffset[Offset], &Stmt->Error); } if (!SQL_SUCCEEDED(Stmt->Error.ReturnValue)) { MADB_FREE(ClientValue); MADB_FREE(IrdRec->InternalBuffer); return Stmt->Error.ReturnValue; } } if (!Stmt->CharOffset[Offset]) { Stmt->Lengths[Offset]= (unsigned long)(CharLength*sizeof(SQLWCHAR)); } } else if (BufferLength >= sizeof(SQLWCHAR)) { *(SQLWCHAR*)TargetValuePtr= 0; } } else /* IrdRec->InternalBuffer == NULL && Stmt->Lengths[Offset] == 0 */ { CharLength= SqlwcsLen((SQLWCHAR*)((char*)IrdRec->InternalBuffer + Stmt->CharOffset[Offset]), -1); } if (StrLen_or_IndPtr) { *StrLen_or_IndPtr= CharLength * sizeof(SQLWCHAR); } if (!BufferLength) { MADB_FREE(ClientValue); return MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); } if (IrdRec->InternalBuffer) { /* If we have more place than only for the TN */ if (BufferLength > sizeof(SQLWCHAR)) { memcpy(TargetValuePtr, (char*)IrdRec->InternalBuffer + Stmt->CharOffset[Offset], MIN(BufferLength - sizeof(SQLWCHAR), CharLength*sizeof(SQLWCHAR))); } /* Terminating Null */ *(SQLWCHAR*)((char*)TargetValuePtr + MIN(BufferLength - sizeof(SQLWCHAR), CharLength*sizeof(SQLWCHAR)))= 0; } if (CharLength >= BufferLength / sizeof(SQLWCHAR)) { /* Calculate new offset and substract 1 byte for null termination */ Stmt->CharOffset[Offset]+= (unsigned long)BufferLength - sizeof(SQLWCHAR); MADB_FREE(ClientValue); return MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); } else { Stmt->CharOffset[Offset]= Stmt->Lengths[Offset]; MADB_FREE(IrdRec->InternalBuffer); } MADB_FREE(ClientValue); } break; case SQL_CHAR: case SQL_VARCHAR: if (Stmt->stmt->fields[Offset].type == MYSQL_TYPE_BLOB && Stmt->stmt->fields[Offset].charsetnr == 63) { if (!BufferLength && StrLen_or_IndPtr) { *StrLen_or_IndPtr= Stmt->stmt->fields[Offset].max_length * 2; return SQL_SUCCESS_WITH_INFO; } #ifdef CONVERSION_TO_HEX_IMPLEMENTED { /*TODO: */ char *TmpBuffer; if (!(TmpBuffer= (char *)MADB_CALLOC(BufferLength))) { } } #endif } ZeroTerminated= 1; case SQL_LONGVARCHAR: case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: { Bind.buffer= TargetValuePtr; Bind.buffer_length= (unsigned long)BufferLength; Bind.buffer_type= MadbType; if (!(BufferLength) && StrLen_or_IndPtr) { /* Paranoid - before StrLen_or_IndPtr was used as length directly. so leaving same value in Bind.length. Unlikely needed */ Bind.length_value= (unsigned long)*StrLen_or_IndPtr; Bind.length= &Bind.length_value; mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, Stmt->CharOffset[Offset]); if (InternalUse) { *StrLen_or_IndPtr= MIN(*Bind.length, Stmt->stmt->fields[Offset].max_length); } else { if (!Stmt->CharOffset[Offset]) { Stmt->Lengths[Offset]= MIN(*Bind.length, Stmt->stmt->fields[Offset].max_length); } *StrLen_or_IndPtr= Stmt->Lengths[Offset] - Stmt->CharOffset[Offset]; } MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); return SQL_SUCCESS_WITH_INFO; } if (mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, CurrentOffset)) { MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt); return Stmt->Error.ReturnValue; } /* Dirty temporary hack before we know what is going on. Yes, there is nothing more eternal, than temporary It's not that bad, after all */ if ((long)*Bind.length == -1) { *Bind.length= 0; } /* end of dirty hack */ if (!InternalUse && !Stmt->CharOffset[Offset]) { Stmt->Lengths[Offset]= MIN(*Bind.length, Stmt->stmt->fields[Offset].max_length); } if (ZeroTerminated) { char *p= (char *)Bind.buffer; if (BufferLength > (SQLLEN)*Bind.length) { p[*Bind.length]= 0; } else { p[BufferLength-1]= 0; } } if (StrLen_or_IndPtr) { *StrLen_or_IndPtr= *Bind.length - CurrentOffset; } if (InternalUse == FALSE) { /* Recording new offset only if that is API call, and not getting data for internal use */ Stmt->CharOffset[Offset]+= MIN((unsigned long)BufferLength - ZeroTerminated, *Bind.length); if ((BufferLength - ZeroTerminated) && Stmt->Lengths[Offset] > Stmt->CharOffset[Offset]) { MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); return Stmt->Error.ReturnValue; } } if (StrLen_or_IndPtr && BufferLength - ZeroTerminated < *StrLen_or_IndPtr) { MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); return SQL_SUCCESS_WITH_INFO; } } break; case SQL_NUMERIC: { SQLRETURN rc; char *tmp; MADB_DescRecord *Ard= MADB_DescGetInternalRecord(Stmt->Ard, Offset, MADB_DESC_READ); Bind.buffer_length= MADB_DEFAULT_PRECISION + 1/*-*/ + 1/*.*/; tmp= (char *)MADB_CALLOC(Bind.buffer_length); Bind.buffer= tmp; Bind.buffer_type= MadbType; mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); MADB_CLEAR_ERROR(&Stmt->Error); if (Bind.buffer_length < Stmt->stmt->fields[Offset].max_length) { MADB_SetError(&Stmt->Error, MADB_ERR_22003, NULL, 0); MADB_FREE(tmp); return Stmt->Error.ReturnValue; } rc= MADB_CharToSQLNumeric(tmp, Stmt->Ard, Ard, TargetValuePtr, 0); /* Ugly */ if (rc != SQL_SUCCESS) { MADB_SetError(&Stmt->Error, rc, NULL, 0); if (rc == SQL_ERROR) { return SQL_ERROR; } } if (StrLen_or_IndPtr != NULL) { *StrLen_or_IndPtr= sizeof(SQL_NUMERIC_STRUCT); } break; } default: { /* Set the conversion function */ Bind.buffer_type= MadbType; Bind.buffer= TargetValuePtr; if (Bind.buffer_length == 0 && BufferLength > 0) { Bind.buffer_length= (unsigned long)BufferLength; } mysql_stmt_fetch_column(Stmt->stmt, &Bind, Offset, 0); if (StrLen_or_IndPtr != NULL) { /* We get here only for fixed data types. Thus, according to the specs "this is the length of the data after conversion; that is, it is the size of the type to which the data was converted". For us that is the size of the buffer in bind structure. Not the size of the field */ *StrLen_or_IndPtr= Bind.buffer_length; /* Paranoid - it was here, so leaving it in place */ if ((long)Bind.length_value == -1) { Bind.length_value= 0; } /* We do this for catalog functions and MS Access in first turn. The thing is that for some columns in catalog functions result, we fix column type manually, since we can't make field of desired type in the query to I_S. Mostly that is for SQLSMALLINT fields, and we can cast only to int, not to short. MSAccess in its turn like to to get length for fixed length types, and throws error if the length is not what it expected (ODBC-131) Probably it makes sense to do this only for SQL_C_DEFAULT type, which MS Access uses. But atm it looks like this should not hurt if done for other types, too */ if (*StrLen_or_IndPtr == 0 || (Bind.length_value > (unsigned long)IrdRec->OctetLength && *StrLen_or_IndPtr > IrdRec->OctetLength)) { *StrLen_or_IndPtr= IrdRec->OctetLength; } } } } /* End of switch(OdbcType) */ /* Marking fixed length fields to be able to return SQL_NO_DATA on subsequent calls, as standard prescribes "SQLGetData cannot be used to return fixed-length data in parts. If SQLGetData is called more than one time in a row for a column containing fixed-length data, it returns SQL_NO_DATA for all calls after the first." Stmt->Lengths[Offset] would be set for variable length types */ if (!InternalUse && Stmt->Lengths[Offset] == 0) { Stmt->CharOffset[Offset]= MAX((unsigned long)Bind.buffer_length, Bind.length_value); } if (IsNull) { if (!StrLen_or_IndPtr) { return MADB_SetError(&Stmt->Error, MADB_ERR_22002, NULL, 0); } *StrLen_or_IndPtr= SQL_NULL_DATA; } return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_StmtRowCount */ SQLRETURN MADB_StmtRowCount(MADB_Stmt *Stmt, SQLLEN *RowCountPtr) { if (Stmt->AffectedRows != -1) *RowCountPtr= (SQLLEN)Stmt->AffectedRows; else if (Stmt->stmt && Stmt->stmt->result.rows && mysql_stmt_field_count(Stmt->stmt)) *RowCountPtr= (SQLLEN)mysql_stmt_num_rows(Stmt->stmt); else *RowCountPtr= 0; return SQL_SUCCESS; } /* }}} */ /* {{{ MapColAttributesDescType */ SQLUSMALLINT MapColAttributeDescType(SQLUSMALLINT FieldIdentifier) { /* we need to map the old field identifiers, see bug ODBC-8 */ switch (FieldIdentifier) { case SQL_COLUMN_SCALE: return SQL_DESC_SCALE; case SQL_COLUMN_PRECISION: return SQL_DESC_PRECISION; case SQL_COLUMN_NULLABLE: return SQL_DESC_NULLABLE; case SQL_COLUMN_LENGTH: return SQL_DESC_OCTET_LENGTH; case SQL_COLUMN_NAME: return SQL_DESC_NAME; default: return FieldIdentifier; } } /* }}} */ /* {{{ MADB_StmtRowCount */ SQLRETURN MADB_StmtParamCount(MADB_Stmt *Stmt, SQLSMALLINT *ParamCountPtr) { *ParamCountPtr= (SQLSMALLINT)mysql_stmt_param_count(Stmt->stmt); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_StmtColumnCount */ SQLRETURN MADB_StmtColumnCount(MADB_Stmt *Stmt, SQLSMALLINT *ColumnCountPtr) { /* We supposed to have that data in the descriptor by now. No sense to ask C/C API one more time for that */ *ColumnCountPtr= (SQLSMALLINT)MADB_STMT_COLUMN_COUNT(Stmt); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_StmtColAttr */ SQLRETURN MADB_StmtColAttr(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN *NumericAttributePtr, my_bool IsWchar) { MADB_DescRecord *Record; SQLSMALLINT StringLength= 0; SQLLEN NumericAttribute; BOOL IsNumericAttr= TRUE; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (StringLengthPtr) *StringLengthPtr= 0; if (!Stmt->stmt || !mysql_stmt_field_count(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_07005, NULL, 0); return Stmt->Error.ReturnValue; } if (ColumnNumber < 1 || ColumnNumber > mysql_stmt_field_count(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return Stmt->Error.ReturnValue; } /* We start at offset zero */ --ColumnNumber; if (!(Record= MADB_DescGetInternalRecord(Stmt->Ird, ColumnNumber, MADB_DESC_READ))) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return Stmt->Error.ReturnValue; } /* Mapping ODBC2 attributes to ODBC3 TODO: it looks like it takes more than that "In ODBC 3.x driver must support SQL_COLUMN_PRECISION and SQL_DESC_PRECISION, SQL_COLUMN_SCALE and SQL_DESC_SCALE, and SQL_COLUMN_LENGTH and SQL_DESC_LENGTH. These values are different because precision, scale, and length are defined differently in ODBC 3.x than they were in ODBC 2.x." */ FieldIdentifier= MapColAttributeDescType(FieldIdentifier); switch(FieldIdentifier) { case SQL_DESC_AUTO_UNIQUE_VALUE: NumericAttribute= (SQLLEN)Record->AutoUniqueValue; break; case SQL_DESC_BASE_COLUMN_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : NULL, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->BaseColumnName, strlen(Record->BaseColumnName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_BASE_TABLE_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : NULL, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->BaseTableName, strlen(Record->BaseTableName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_CASE_SENSITIVE: NumericAttribute= (SQLLEN)Record->CaseSensitive; break; case SQL_DESC_CATALOG_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->CatalogName, strlen(Record->CatalogName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_SCHEMA_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, "", 0, &Stmt->Error); IsNumericAttr= FALSE; case SQL_DESC_CONCISE_TYPE: NumericAttribute= (SQLLEN)Record->ConciseType; break; case SQL_DESC_SEARCHABLE: NumericAttribute= (SQLLEN)Record->Searchable; break; case SQL_DESC_COUNT: NumericAttribute= (SQLLEN)Stmt->Ird->Header.Count; break; case SQL_DESC_DISPLAY_SIZE: NumericAttribute= (SQLLEN)Record->DisplaySize; break; case SQL_DESC_FIXED_PREC_SCALE: NumericAttribute= (SQLLEN)Record->FixedPrecScale; break; case SQL_DESC_PRECISION: NumericAttribute= (SQLLEN)Record->Precision; break; case SQL_DESC_LENGTH: NumericAttribute= (SQLLEN)Record->Length; break; case SQL_DESC_LITERAL_PREFIX: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->LiteralPrefix, strlen(Record->LiteralPrefix), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_LITERAL_SUFFIX: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->LiteralSuffix, strlen(Record->LiteralSuffix), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_LOCAL_TYPE_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, "", 0, &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_LABEL: case SQL_DESC_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->ColumnName, strlen(Record->ColumnName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_TYPE_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->TypeName, strlen(Record->TypeName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_NULLABLE: NumericAttribute= Record->Nullable; break; case SQL_DESC_UNNAMED: NumericAttribute= Record->Unnamed; break; case SQL_DESC_UNSIGNED: NumericAttribute= Record->Unsigned; break; case SQL_DESC_UPDATABLE: NumericAttribute= Record->Updateable; break; case SQL_DESC_OCTET_LENGTH: NumericAttribute= Record->OctetLength; break; case SQL_DESC_SCALE: NumericAttribute= Record->Scale; break; case SQL_DESC_TABLE_NAME: StringLength= (SQLSMALLINT)MADB_SetString(IsWchar ? &Stmt->Connection->Charset : 0, CharacterAttributePtr, (IsWchar) ? BufferLength / sizeof(SQLWCHAR) : BufferLength, Record->TableName, strlen(Record->TableName), &Stmt->Error); IsNumericAttr= FALSE; break; case SQL_DESC_TYPE: NumericAttribute= Record->Type; break; case SQL_COLUMN_COUNT: NumericAttribute= mysql_stmt_field_count(Stmt->stmt); break; default: MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, NULL, 0); return Stmt->Error.ReturnValue; } /* We need to return the number of bytes, not characters! */ if (StringLength) { if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)StringLength; if (!BufferLength && CharacterAttributePtr) MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); } /* We shouldn't touch application memory without purpose, writing garbage there. Thus IsNumericAttr. Besides .Net was quite disappointed about that */ if (NumericAttributePtr && IsNumericAttr == TRUE) *NumericAttributePtr= NumericAttribute; if (StringLengthPtr && IsWchar) *StringLengthPtr*= sizeof(SQLWCHAR); return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_StmtDescribeCol */ SQLRETURN MADB_StmtDescribeCol(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, void *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, SQLSMALLINT *DataTypePtr, SQLULEN *ColumnSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr, my_bool isWChar) { MADB_DescRecord *Record; MADB_CLEAR_ERROR(&Stmt->Error); if (!mysql_stmt_field_count(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_07005, NULL, 0); return Stmt->Error.ReturnValue; } if (ColumnNumber < 1 || ColumnNumber > mysql_stmt_field_count(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_07009, NULL, 0); return SQL_ERROR; } if (!(Record= MADB_DescGetInternalRecord(Stmt->Ird, ColumnNumber - 1, MADB_DESC_WRITE))) { MADB_CopyError(&Stmt->Error, &Stmt->Ird->Error); return Stmt->Error.ReturnValue; } if (NameLengthPtr) *NameLengthPtr= 0; /* Don't map types if ansi mode was set */ if (DataTypePtr) *DataTypePtr= (isWChar && !Stmt->Connection->IsAnsi) ? MADB_GetWCharType(Record->ConciseType) : Record->ConciseType; /* Columnsize in characters, not bytes! */ if (ColumnSizePtr) *ColumnSizePtr= Record->Length; //Record->Precision ? MIN(Record->DisplaySize, Record->Precision) : Record->DisplaySize; if (DecimalDigitsPtr) *DecimalDigitsPtr= Record->Scale; if (NullablePtr) *NullablePtr= Record->Nullable; if ((ColumnName || BufferLength) && Record->ColumnName) { size_t Length= MADB_SetString(isWChar ? &Stmt->Connection->Charset : 0, ColumnName, ColumnName ? BufferLength : 0, Record->ColumnName, SQL_NTS, &Stmt->Error); if (NameLengthPtr) *NameLengthPtr= (SQLSMALLINT)Length; if (!BufferLength) MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); } return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_SetCursorName */ SQLRETURN MADB_SetCursorName(MADB_Stmt *Stmt, char *Buffer, SQLINTEGER BufferLength) { MADB_List *LStmt, *LStmtNext; if (!Buffer) { MADB_SetError(&Stmt->Error, MADB_ERR_HY009, NULL, 0); return SQL_ERROR; } if (BufferLength== SQL_NTS) BufferLength= (SQLINTEGER)strlen(Buffer); if (BufferLength < 0) { MADB_SetError(&Stmt->Error, MADB_ERR_HY090, NULL, 0); return SQL_ERROR; } if ((BufferLength > 5 && strncmp(Buffer, "SQLCUR", 6) == 0) || (BufferLength > 6 && strncmp(Buffer, "SQL_CUR", 7) == 0)) { MADB_SetError(&Stmt->Error, MADB_ERR_34000, NULL, 0); return SQL_ERROR; } /* check if cursor name is unique */ for (LStmt= Stmt->Connection->Stmts; LStmt; LStmt= LStmtNext) { MADB_Cursor *Cursor= &((MADB_Stmt *)LStmt->data)->Cursor; LStmtNext= LStmt->next; if (Stmt != (MADB_Stmt *)LStmt->data && Cursor->Name && strncmp(Cursor->Name, Buffer, BufferLength) == 0) { MADB_SetError(&Stmt->Error, MADB_ERR_3C000, NULL, 0); return SQL_ERROR; } } MADB_FREE(Stmt->Cursor.Name); Stmt->Cursor.Name= MADB_CALLOC(BufferLength + 1); MADB_SetString(0, Stmt->Cursor.Name, BufferLength + 1, Buffer, BufferLength, NULL); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_GetCursorName */ SQLRETURN MADB_GetCursorName(MADB_Stmt *Stmt, void *CursorName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, my_bool isWChar) { SQLSMALLINT Length; MADB_CLEAR_ERROR(&Stmt->Error); if (BufferLength < 0) { MADB_SetError(&Stmt->Error, MADB_ERR_HY090, NULL, 0); return Stmt->Error.ReturnValue; } if (!Stmt->Cursor.Name) { Stmt->Cursor.Name= (char *)MADB_CALLOC(MADB_MAX_CURSOR_NAME); _snprintf(Stmt->Cursor.Name, MADB_MAX_CURSOR_NAME, "SQL_CUR%d", Stmt->Connection->CursorCount++); } Length= (SQLSMALLINT)MADB_SetString(isWChar ? &Stmt->Connection->Charset : 0, CursorName, BufferLength, Stmt->Cursor.Name, SQL_NTS, &Stmt->Error); if (NameLengthPtr) *NameLengthPtr= (SQLSMALLINT)Length; if (!BufferLength) MADB_SetError(&Stmt->Error, MADB_ERR_01004, NULL, 0); return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_RefreshRowPtrs */ SQLRETURN MADB_RefreshRowPtrs(MADB_Stmt *Stmt) { return MoveNext(Stmt, 1LL); } /* {{{ MADB_RefreshDynamicCursor */ SQLRETURN MADB_RefreshDynamicCursor(MADB_Stmt *Stmt) { SQLRETURN ret; SQLLEN CurrentRow= Stmt->Cursor.Position; long long AffectedRows= Stmt->AffectedRows; SQLLEN LastRowFetched= Stmt->LastRowFetched; ret= Stmt->Methods->Execute(Stmt, FALSE); Stmt->Cursor.Position= CurrentRow; if (Stmt->Cursor.Position > 0 && (my_ulonglong)Stmt->Cursor.Position >= mysql_stmt_num_rows(Stmt->stmt)) { Stmt->Cursor.Position= (long)mysql_stmt_num_rows(Stmt->stmt) - 1; } Stmt->LastRowFetched= LastRowFetched; Stmt->AffectedRows= AffectedRows; if (Stmt->Cursor.Position < 0) { Stmt->Cursor.Position= 0; } return ret; } /* }}} */ /* Couple of macsros for this function specifically */ #define MADB_SETPOS_FIRSTROW(agg_result) (agg_result == SQL_INVALID_HANDLE) #define MADB_SETPOS_AGG_RESULT(agg_result, row_result) if (MADB_SETPOS_FIRSTROW(agg_result)) agg_result= row_result; \ else if (row_result != agg_result) agg_result= SQL_SUCCESS_WITH_INFO /* {{{ MADB_SetPos */ SQLRETURN MADB_StmtSetPos(MADB_Stmt *Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT Operation, SQLUSMALLINT LockType, int ArrayOffset) { if (!Stmt->result && !Stmt->stmt->fields) { MADB_SetError(&Stmt->Error, MADB_ERR_24000, NULL, 0); return Stmt->Error.ReturnValue; } /* skip this for now, since we don't use unbuffered result sets if (Stmt->Options.CursorType == SQL_CURSOR_FORWARD_ONLY) { MADB_SetError(&Stmt->Error, MADB_ERR_24000, NULL, 0); return Stmt->Error.ReturnValue; } */ if (LockType != SQL_LOCK_NO_CHANGE) { MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, NULL, 0); return Stmt->Error.ReturnValue; } switch(Operation) { case SQL_POSITION: { if (RowNumber < 1 || RowNumber > mysql_stmt_num_rows(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY109, NULL, 0); return Stmt->Error.ReturnValue; } if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt))) return Stmt->Error.ReturnValue; EnterCriticalSection(&Stmt->Connection->cs); Stmt->Cursor.Position+=(RowNumber - 1); MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position); LeaveCriticalSection(&Stmt->Connection->cs); } break; case SQL_ADD: { MADB_DynString DynStmt; SQLRETURN ret; char *TableName= MADB_GetTableName(Stmt); char *CatalogName= MADB_GetCatalogName(Stmt); int column, param= 0; if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt))) return Stmt->Error.ReturnValue; Stmt->DaeRowNumber= RowNumber; if (Stmt->DataExecutionType != MADB_DAE_ADD) { Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); MA_SQLAllocHandle(SQL_HANDLE_STMT, Stmt->Connection, (SQLHANDLE *)&Stmt->DaeStmt); if (MADB_InitDynamicString(&DynStmt, "INSERT INTO ", 8192, 1024) || MADB_DynStrAppendQuoted(&DynStmt, CatalogName) || MADB_DynstrAppend(&DynStmt, ".") || MADB_DynStrAppendQuoted(&DynStmt, TableName)|| MADB_DynStrInsertSet(Stmt, &DynStmt)) { MADB_DynstrFree(&DynStmt); return Stmt->Error.ReturnValue; } ResetMetadata(&Stmt->DaeStmt->DefaultsResult, MADB_GetDefaultColumnValues(Stmt, Stmt->stmt->fields)); Stmt->DataExecutionType= MADB_DAE_ADD; ret= Stmt->Methods->Prepare(Stmt->DaeStmt, DynStmt.str, SQL_NTS, FALSE); MADB_DynstrFree(&DynStmt); if (!SQL_SUCCEEDED(ret)) { MADB_CopyError(&Stmt->Error, &Stmt->DaeStmt->Error); Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); return Stmt->Error.ReturnValue; } } /* Bind parameters - DaeStmt will process whole array of values, thus we don't need to iterate through the array*/ for (column= 0; column < MADB_STMT_COLUMN_COUNT(Stmt); ++column) { MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Stmt->Ard, column, MADB_DESC_READ), *ApdRec= NULL; if (Rec->inUse && MADB_ColumnIgnoredInAllRows(Stmt->Ard, Rec) == FALSE) { Stmt->DaeStmt->Methods->BindParam(Stmt->DaeStmt, param + 1, SQL_PARAM_INPUT, Rec->ConciseType, Rec->Type, Rec->DisplaySize, Rec->Scale, Rec->DataPtr, Rec->OctetLength, Rec->OctetLengthPtr); } else { /*Stmt->DaeStmt->Methods->BindParam(Stmt->DaeStmt, param + 1, SQL_PARAM_INPUT, SQL_CHAR, SQL_C_CHAR, 0, 0, ApdRec->DefaultValue, strlen(ApdRec->DefaultValue), NULL);*/ continue; } ApdRec= MADB_DescGetInternalRecord(Stmt->DaeStmt->Apd, param, MADB_DESC_READ); ApdRec->DefaultValue= MADB_GetDefaultColumnValue(Stmt->DaeStmt->DefaultsResult, Stmt->stmt->fields[column].org_name); ++param; } memcpy(&Stmt->DaeStmt->Apd->Header, &Stmt->Ard->Header, sizeof(MADB_Header)); ret= Stmt->Methods->Execute(Stmt->DaeStmt, FALSE); if (!SQL_SUCCEEDED(ret)) { /* We can have SQL_NEED_DATA here, which would not set error (and its ReturnValue) */ MADB_CopyError(&Stmt->Error, &Stmt->DaeStmt->Error); return ret; } if (Stmt->AffectedRows == -1) { Stmt->AffectedRows= 0; } Stmt->AffectedRows+= Stmt->DaeStmt->AffectedRows; Stmt->DataExecutionType= MADB_DAE_NORMAL; Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); Stmt->DaeStmt= NULL; } break; case SQL_UPDATE: { char *TableName= MADB_GetTableName(Stmt); my_ulonglong Start= 0, End= mysql_stmt_num_rows(Stmt->stmt); SQLRETURN result= SQL_INVALID_HANDLE; /* Just smth we cannot normally get */ if (!TableName) { MADB_SetError(&Stmt->Error, MADB_ERR_IM001, "Updatable Cursors with multiple tables are not supported", 0); return Stmt->Error.ReturnValue; } Stmt->AffectedRows= 0; if ((SQLLEN)RowNumber > Stmt->LastRowFetched) { MADB_SetError(&Stmt->Error, MADB_ERR_S1107, NULL, 0); return Stmt->Error.ReturnValue; } if (RowNumber < 0 || RowNumber > End) { MADB_SetError(&Stmt->Error, MADB_ERR_HY109, NULL, 0); return Stmt->Error.ReturnValue; } if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt))) return Stmt->Error.ReturnValue; Stmt->DaeRowNumber= MAX(1,RowNumber); /* Cursor is open, but no row was fetched, so we simulate that first row was fetched */ if (Stmt->Cursor.Position < 0) Stmt->Cursor.Position= 1; if (RowNumber) Start= End= Stmt->Cursor.Position + RowNumber -1; else { Start= Stmt->Cursor.Position; /* TODO: if num_rows returns 1, End is 0? Start would be 1, no */ End= MIN(mysql_stmt_num_rows(Stmt->stmt)-1, Start + Stmt->Ard->Header.ArraySize - 1); } /* Stmt->ArrayOffset will be incremented in StmtExecute() */ Start+= Stmt->ArrayOffset; /* TODO: SQL_ATTR_ROW_STATUS_PTR should be filled */ while (Start <= End) { SQLSMALLINT param= 0, column; MADB_StmtDataSeek(Stmt, Start); Stmt->Methods->RefreshRowPtrs(Stmt); /* We don't need to prepare the statement, if SetPos was called from SQLParamData() function */ if (!ArrayOffset) { if (!SQL_SUCCEEDED(MADB_DaeStmt(Stmt, SQL_UPDATE))) { MADB_SETPOS_AGG_RESULT(result, Stmt->Error.ReturnValue); /* Moving to the next row */ Stmt->DaeRowNumber++; Start++; continue; } for(column= 0; column < MADB_STMT_COLUMN_COUNT(Stmt); ++column) { SQLLEN *LengthPtr= NULL; my_bool GetDefault= FALSE; MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Stmt->Ard, column, MADB_DESC_READ); /* TODO: shouldn't here be IndicatorPtr? */ if (Rec->OctetLengthPtr) LengthPtr= GetBindOffset(Stmt->Ard, Rec, Rec->OctetLengthPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber - 1 : 0, sizeof(SQLLEN)/*Rec->OctetLength*/); if (!Rec->inUse || (LengthPtr && *LengthPtr == SQL_COLUMN_IGNORE)) { GetDefault= TRUE; continue; } /* TODO: Looks like this whole thing is not really needed. Not quite clear if !InUse should result in going this way */ if (GetDefault) { SQLLEN Length= 0; /* set a default value */ if (Stmt->Methods->GetData(Stmt, column + 1, SQL_C_CHAR, NULL, 0, &Length, TRUE) != SQL_ERROR && Length) { MADB_FREE(Rec->DefaultValue); if (Length > 0) { Rec->DefaultValue= (char *)MADB_CALLOC(Length + 1); Stmt->Methods->GetData(Stmt, column + 1, SQL_C_CHAR, Rec->DefaultValue, Length+1, 0, TRUE); } Stmt->DaeStmt->Methods->BindParam(Stmt->DaeStmt, param + 1, SQL_PARAM_INPUT, SQL_CHAR, SQL_C_CHAR, 0, 0, Rec->DefaultValue, Length, NULL); ++param; continue; } } if (!GetDefault) { Stmt->DaeStmt->Methods->BindParam(Stmt->DaeStmt, param + 1, SQL_PARAM_INPUT, Rec->ConciseType, Rec->Type, Rec->DisplaySize, Rec->Scale, GetBindOffset(Stmt->Ard, Rec, Rec->DataPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber -1 : 0, Rec->OctetLength), Rec->OctetLength, LengthPtr); } if (PARAM_IS_DAE(LengthPtr) && !DAE_DONE(Stmt->DaeStmt)) { Stmt->Status= SQL_NEED_DATA; ++param; continue; } ++param; } /* End of for(column=0;...) */ if (Stmt->Status == SQL_NEED_DATA) return SQL_NEED_DATA; } /* End of if (!ArrayOffset) */ if (Stmt->DaeStmt->Methods->Execute(Stmt->DaeStmt, FALSE) != SQL_ERROR) { Stmt->AffectedRows+= Stmt->DaeStmt->AffectedRows; } else { MADB_CopyError(&Stmt->Error, &Stmt->DaeStmt->Error); } MADB_SETPOS_AGG_RESULT(result, Stmt->DaeStmt->Error.ReturnValue); Stmt->DaeRowNumber++; Start++; } /* End of while (Start <= End) */ Stmt->Methods->StmtFree(Stmt->DaeStmt, SQL_DROP); Stmt->DaeStmt= NULL; Stmt->DataExecutionType= MADB_DAE_NORMAL; /* Making sure we do not return initial value */ return result == SQL_INVALID_HANDLE ? SQL_SUCCESS :result; } case SQL_DELETE: { MADB_DynString DynamicStmt; SQLULEN SaveArraySize= Stmt->Ard->Header.ArraySize; my_ulonglong Start= 0, End= mysql_stmt_num_rows(Stmt->stmt); char *TableName= MADB_GetTableName(Stmt); if (!TableName) { MADB_SetError(&Stmt->Error, MADB_ERR_IM001, "Updatable Cursors with multiple tables are not supported", 0); return Stmt->Error.ReturnValue; } Stmt->Ard->Header.ArraySize= 1; if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt))) return Stmt->Error.ReturnValue; Stmt->AffectedRows= 0; if (RowNumber < 0 || RowNumber > End) { MADB_SetError(&Stmt->Error, MADB_ERR_HY109, NULL, 0); return Stmt->Error.ReturnValue; } Start= (RowNumber) ? Stmt->Cursor.Position + RowNumber - 1 : Stmt->Cursor.Position; if (SaveArraySize && !RowNumber) End= MIN(End, Start + SaveArraySize - 1); else End= Start; while (Start <= End) { MADB_StmtDataSeek(Stmt, Start); Stmt->Methods->RefreshRowPtrs(Stmt); MADB_InitDynamicString(&DynamicStmt, "DELETE FROM ", 8192, 1024); if (MADB_DynStrAppendQuoted(&DynamicStmt, TableName) || MADB_DynStrGetWhere(Stmt, &DynamicStmt, TableName, FALSE)) { MADB_DynstrFree(&DynamicStmt); MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return Stmt->Error.ReturnValue; } LOCK_MARIADB(Stmt->Connection); if (mysql_real_query(Stmt->Connection->mariadb, DynamicStmt.str, (unsigned long)DynamicStmt.length)) { MADB_DynstrFree(&DynamicStmt); MADB_SetError(&Stmt->Error, MADB_ERR_HY001, mysql_error(Stmt->Connection->mariadb), mysql_errno(Stmt->Connection->mariadb)); UNLOCK_MARIADB(Stmt->Connection); return Stmt->Error.ReturnValue; } MADB_DynstrFree(&DynamicStmt); Stmt->AffectedRows+= mysql_affected_rows(Stmt->Connection->mariadb); Start++; } UNLOCK_MARIADB(Stmt->Connection); Stmt->Ard->Header.ArraySize= SaveArraySize; /* if we have a dynamic cursor we need to adjust the rowset size */ if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) { Stmt->LastRowFetched-= (unsigned long)Stmt->AffectedRows; } } break; case SQL_REFRESH: /* todo*/ break; default: MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Only SQL_POSITION and SQL_REFRESH Operations are supported", 0); return Stmt->Error.ReturnValue; } return SQL_SUCCESS; } /* }}} */ #undef MADB_SETPOS_FIRSTROW #undef MADB_SETPOS_AGG_RESULT /* {{{ MADB_StmtFetchScroll */ SQLRETURN MADB_StmtFetchScroll(MADB_Stmt *Stmt, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset) { SQLRETURN ret= SQL_SUCCESS; SQLLEN Position; SQLLEN RowsProcessed; RowsProcessed= Stmt->LastRowFetched; if (Stmt->Options.CursorType == SQL_CURSOR_FORWARD_ONLY && FetchOrientation != SQL_FETCH_NEXT) { MADB_SetError(&Stmt->Error, MADB_ERR_HY106, NULL, 0); return Stmt->Error.ReturnValue; } if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) { SQLRETURN rc; rc= Stmt->Methods->RefreshDynamicCursor(Stmt); if (!SQL_SUCCEEDED(rc)) { return Stmt->Error.ReturnValue; } } if (FetchOrientation != SQL_FETCH_NEXT) { MADB_STMT_FORGET_NEXT_POS(Stmt); } switch(FetchOrientation) { case SQL_FETCH_NEXT: Position= Stmt->Cursor.Position < 0 ? 0 : Stmt->Cursor.Position + RowsProcessed; break; case SQL_FETCH_PRIOR: Position= Stmt->Cursor.Position < 0 ? - 1: Stmt->Cursor.Position - MAX(1, Stmt->Ard->Header.ArraySize); break; case SQL_FETCH_RELATIVE: Position= Stmt->Cursor.Position + FetchOffset; if (Position < 0 && Stmt->Cursor.Position > 0 && -FetchOffset <= (SQLINTEGER)Stmt->Ard->Header.ArraySize) Position= 0; break; case SQL_FETCH_ABSOLUTE: if (FetchOffset < 0) { if ((long long)mysql_stmt_num_rows(Stmt->stmt) - 1 + FetchOffset < 0 && ((SQLULEN)-FetchOffset <= Stmt->Ard->Header.ArraySize)) Position= 0; else Position= (SQLLEN)mysql_stmt_num_rows(Stmt->stmt) + FetchOffset; } else Position= FetchOffset - 1; break; case SQL_FETCH_FIRST: Position= 0; break; case SQL_FETCH_LAST: Position= (SQLLEN)mysql_stmt_num_rows(Stmt->stmt) - MAX(1, Stmt->Ard->Header.ArraySize); /* if (Stmt->Ard->Header.ArraySize > 1) Position= MAX(0, Position - Stmt->Ard->Header.ArraySize + 1); */ break; case SQL_FETCH_BOOKMARK: if (Stmt->Options.UseBookmarks == SQL_UB_OFF) { MADB_SetError(&Stmt->Error, MADB_ERR_HY106, NULL, 0); return Stmt->Error.ReturnValue; } if (!Stmt->Options.BookmarkPtr) { MADB_SetError(&Stmt->Error, MADB_ERR_HY111, NULL, 0); return Stmt->Error.ReturnValue; } Position= *((long *)Stmt->Options.BookmarkPtr); if (Stmt->Connection->Environment->OdbcVersion >= SQL_OV_ODBC3) Position+= FetchOffset; break; default: MADB_SetError(&Stmt->Error, MADB_ERR_HY106, NULL, 0); return Stmt->Error.ReturnValue; break; } if (Position < 0) { MADB_STMT_RESET_CURSOR(Stmt); } else { Stmt->Cursor.Position= (SQLLEN)MIN((my_ulonglong)Position, mysql_stmt_num_rows(Stmt->stmt)); } if (Position < 0 || (my_ulonglong)Position > mysql_stmt_num_rows(Stmt->stmt) - 1) { /* We need to put cursor before RS start, not only return error */ if (Position < 0) { MADB_StmtDataSeek(Stmt, 0); } return SQL_NO_DATA; } /* For dynamic cursor we "refresh" resultset eachtime(basically re-executing), and thus the (c/c)cursor is before 1st row at this point, and thux we need to restore the last position. For array fetch with not forward_only cursor, the (c/c)cursor is at 1st row of the last fetched rowset */ if (FetchOrientation != SQL_FETCH_NEXT || (RowsProcessed > 1 && Stmt->Options.CursorType != SQL_CURSOR_FORWARD_ONLY) || Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC) { if (Stmt->Cursor.Next != NULL) { mysql_stmt_row_seek(Stmt->stmt, Stmt->Cursor.Next); ret= SQL_SUCCESS; } else { ret= MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position); } } /* Assuming, that ret before previous "if" was SQL_SUCCESS */ if (ret == SQL_SUCCESS) { ret= Stmt->Methods->Fetch(Stmt); } if (ret == SQL_NO_DATA_FOUND && Stmt->LastRowFetched > 0) { ret= SQL_SUCCESS; } return ret; } struct st_ma_stmt_methods MADB_StmtMethods= { MADB_StmtPrepare, MADB_StmtExecute, MADB_StmtFetch, MADB_StmtBindCol, MADB_StmtBindParam, MADB_StmtExecDirect, MADB_StmtGetData, MADB_StmtRowCount, MADB_StmtParamCount, MADB_StmtColumnCount, MADB_StmtGetAttr, MADB_StmtSetAttr, MADB_StmtFree, MADB_StmtColAttr, MADB_StmtColumnPrivileges, MADB_StmtTablePrivileges, MADB_StmtTables, MADB_StmtStatistics, MADB_StmtColumns, MADB_StmtProcedureColumns, MADB_StmtPrimaryKeys, MADB_StmtSpecialColumns, MADB_StmtProcedures, MADB_StmtForeignKeys, MADB_StmtDescribeCol, MADB_SetCursorName, MADB_GetCursorName, MADB_StmtSetPos, MADB_StmtFetchScroll, MADB_StmtParamData, MADB_StmtPutData, MADB_StmtBulkOperations, MADB_RefreshDynamicCursor, MADB_RefreshRowPtrs, MADB_GetOutParams }; mariadb-connector-odbc-3.1.15/ma_statement.h000066400000000000000000000202351414431724000207310ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013, 2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_statement_h_ #define _ma_statement_h_ #include "ma_catalog.h" #define MADB_FETCH_TYPE_FETCH 1 #define MADB_FETCH_TYPE_BULK 2 #define MADB_FETCH_TYPE_SETPOS 3 struct st_ma_stmt_methods { SQLRETURN (*Prepare)(MADB_Stmt *Stmt, char *StatementText, SQLINTEGER TextLength, BOOL ExecDirect); SQLRETURN (*Execute)(MADB_Stmt *Stmt, BOOL ExecDirect); SQLRETURN (*Fetch)(MADB_Stmt *Stmt); SQLRETURN (*BindColumn)(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind); SQLRETURN (*BindParam)(MADB_Stmt *Stmt, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr); SQLRETURN (*ExecDirect)(MADB_Stmt *Stmt, char *StatementText, SQLINTEGER TextLength); SQLRETURN (*GetData)(SQLHSTMT StatementHandle, SQLUSMALLINT Col_or_Param_Num, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN * StrLen_or_IndPtr, BOOL InternalUse); SQLRETURN (*RowCount)(MADB_Stmt *Stmt, SQLLEN *RowCountPtr); SQLRETURN (*ParamCount)(MADB_Stmt *Stmt, SQLSMALLINT *ParamCountPtr); SQLRETURN (*ColumnCount)(MADB_Stmt *Stmt, SQLSMALLINT *ColumnCountPtr); SQLRETURN (*GetAttr)(MADB_Stmt *Stmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER *StringLength); SQLRETURN (*SetAttr)(MADB_Stmt *Stmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength); SQLRETURN (*StmtFree)(MADB_Stmt *Stmt, SQLUSMALLINT Option); SQLRETURN (*ColAttribute)(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLLEN *NumericAttributePtr, my_bool isWchar); SQLRETURN (*ColumnPrivileges)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN (*TablePrivileges)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3); SQLRETURN (*Tables)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *TableType, SQLSMALLINT NameLength4); SQLRETURN (*Statistics)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Unique, SQLUSMALLINT Reserved); SQLRETURN (*Columns)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN (*ProcedureColumns)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3, char *ColumnName, SQLSMALLINT NameLength4); SQLRETURN (*PrimaryKeys)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3); SQLRETURN (*SpecialColumns)(MADB_Stmt *Stmt, SQLUSMALLINT IdentifierType, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Scope, SQLUSMALLINT Nullable); SQLRETURN (*Procedures)(MADB_Stmt *Stmt, char *CatalogName, SQLSMALLINT NameLength1, char *SchemaName, SQLSMALLINT NameLength2, char *ProcName, SQLSMALLINT NameLength3); SQLRETURN (*ForeignKeys)(MADB_Stmt *Stmt, char *PKCatalogName, SQLSMALLINT NameLength1, char *PKSchemaName, SQLSMALLINT NameLength2, char *PKTableName, SQLSMALLINT NameLength3, char *FKCatalogName, SQLSMALLINT NameLength4, char *FKSchemaName, SQLSMALLINT NameLength5, char *FKTableName, SQLSMALLINT NameLength6); SQLRETURN (*DescribeCol)(MADB_Stmt *Stmt, SQLUSMALLINT ColumnNumber, void *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, SQLSMALLINT *DataTypePtr, SQLULEN *ColumnSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr, my_bool isWChar); SQLRETURN (*SetCursorName)(MADB_Stmt *Stmt, char *Buffer, SQLINTEGER BufferLength); SQLRETURN (*GetCursorName)(MADB_Stmt *Stmt, void *CursorName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, my_bool isWChar); SQLRETURN (*SetPos)(MADB_Stmt *Stmt, SQLSETPOSIROW Row, SQLUSMALLINT Operation, SQLUSMALLINT LockType, int ArrayOffset); SQLRETURN (*FetchScroll)(MADB_Stmt *Stmt, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset); SQLRETURN (*ParamData)(MADB_Stmt *Stmt, SQLPOINTER *ValuePtrPtr); SQLRETURN (*PutData)(MADB_Stmt *Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind); SQLRETURN (*BulkOperations)(MADB_Stmt *Stmt, SQLSMALLINT Operation); SQLRETURN (*RefreshDynamicCursor)(MADB_Stmt *Stmt); SQLRETURN (*RefreshRowPtrs)(MADB_Stmt *Stmt); SQLRETURN (*GetOutParams)(MADB_Stmt *Stmt, int CurrentOffset); }; SQLRETURN MADB_StmtInit (MADB_Dbc *Connection, SQLHANDLE *pHStmt); SQLUSMALLINT MapColAttributeDescType(SQLUSMALLINT FieldIdentifier); MYSQL_RES* FetchMetadata (MADB_Stmt *Stmt); SQLRETURN MADB_DoExecute(MADB_Stmt *Stmt, BOOL ExecDirect); #define MADB_MAX_CURSOR_NAME 64 * 4 + 1 #define MADB_CHECK_STMT_HANDLE(a,b)\ if (!(a) || !(a)->b)\ return SQL_INVALID_HANDLE #define MADB_STMT_COLUMN_COUNT(aStmt) (aStmt)->Ird->Header.Count #define MADB_RESET_COLUMT_COUNT(aStmt) (aStmt)->Ird->Header.Count= 0 #define MADB_STMT_PARAM_COUNT(aStmt) (aStmt)->ParamCount #define MADB_POSITIONED_COMMAND(aStmt) ((aStmt)->PositionedCommand && (aStmt)->PositionedCursor) /* So far we always use all fields for index. Once that is changed, this should be changed as well */ #define MADB_POS_COMM_IDX_FIELD_COUNT(aStmt) MADB_STMT_COLUMN_COUNT((aStmt)->PositionedCursor) #define MADB_STMT_FORGET_NEXT_POS(aStmt) (aStmt)->Cursor.Next= NULL #define MADB_STMT_RESET_CURSOR(aStmt) (aStmt)->Cursor.Position= -1; MADB_STMT_FORGET_NEXT_POS(aStmt) #define MADB_STMT_CLOSE_STMT(aStmt) mysql_stmt_close((aStmt)->stmt);(aStmt)->stmt= NULL #endif mariadb-connector-odbc-3.1.15/ma_string.c000066400000000000000000000367501414431724000202370ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include extern MARIADB_CHARSET_INFO* DmUnicodeCs; char *MADB_GetTableName(MADB_Stmt *Stmt) { char *TableName= NULL; unsigned int i= 0; if (Stmt->TableName && Stmt->TableName[0]) return Stmt->TableName; if (!mysql_stmt_field_count(Stmt->stmt)) return NULL; for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (Stmt->stmt->fields[i].org_table) { if (!TableName) TableName= Stmt->stmt->fields[i].org_table; if (strcmp(TableName, Stmt->stmt->fields[i].org_table)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "Couldn't identify unique table name", 0); return NULL; } } } if (TableName) Stmt->TableName= _strdup(TableName); return TableName; } char *MADB_GetCatalogName(MADB_Stmt *Stmt) { char *CatalogName= NULL; unsigned int i= 0; if (Stmt->CatalogName && Stmt->CatalogName[0]) return Stmt->CatalogName; if (!mysql_stmt_field_count(Stmt->stmt)) return NULL; for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (Stmt->stmt->fields[i].org_table) { if (!CatalogName) CatalogName= Stmt->stmt->fields[i].db; if (strcmp(CatalogName, Stmt->stmt->fields[i].db)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "Couldn't identify unique catalog name", 0); return NULL; } } } if (CatalogName) Stmt->CatalogName= _strdup(CatalogName); return CatalogName; } my_bool MADB_DynStrAppendQuoted(MADB_DynString *DynString, char *String) { if (MADB_DynstrAppendMem(DynString, "`", 1) || MADB_DynstrAppend(DynString, String) || MADB_DynstrAppendMem(DynString, "`", 1)) { return TRUE; } return FALSE; } my_bool MADB_DynStrUpdateSet(MADB_Stmt *Stmt, MADB_DynString *DynString) { int i, IgnoredColumns= 0; MADB_DescRecord *Record; if (MADB_DYNAPPENDCONST(DynString, " SET ")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } // ???? memcpy(&Stmt->Da->Apd->Header, &Stmt->Ard->Header, sizeof(MADB_Header)); for (i=0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++) { SQLLEN *IndicatorPtr= NULL; Record= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ); if (Record->IndicatorPtr) IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Ard, Record, Record->IndicatorPtr, Stmt->DaeRowNumber > 1 ? Stmt->DaeRowNumber-1 : 0, sizeof(SQLLEN)/*Record->OctetLength*/); if ((IndicatorPtr && *IndicatorPtr == SQL_COLUMN_IGNORE) || !Record->inUse) { IgnoredColumns++; continue; } if ((i - IgnoredColumns) && MADB_DYNAPPENDCONST(DynString, ",")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } if (MADB_DYNAPPENDCONST(DynString, "=?")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } } if (IgnoredColumns == mysql_stmt_field_count(Stmt->stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_21S02, NULL, 0); return TRUE; } return FALSE; } my_bool MADB_DynStrInsertSet(MADB_Stmt *Stmt, MADB_DynString *DynString) { MADB_DynString ColVals; int i, NeedComma= 0; MADB_DescRecord *Record; MADB_InitDynamicString(&ColVals, "VALUES (", 32, 32); if (MADB_DYNAPPENDCONST(DynString, " (")) { goto dynerror; return TRUE; } /* We use only columns, that have been bound, and are not IGNORED */ for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++) { Record= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ); if (!Record->inUse || MADB_ColumnIgnoredInAllRows(Stmt->Ard, Record) == TRUE) { continue; } if ((NeedComma) && (MADB_DYNAPPENDCONST(DynString, ",") || MADB_DYNAPPENDCONST(&ColVals, ","))) goto dynerror; if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name) || MADB_DYNAPPENDCONST(&ColVals, "?")) goto dynerror; NeedComma= 1; } if (MADB_DYNAPPENDCONST(DynString, ") " ) || MADB_DYNAPPENDCONST(&ColVals, ")") || MADB_DynstrAppend(DynString, ColVals.str)) goto dynerror; MADB_DynstrFree(&ColVals); return FALSE; dynerror: MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); MADB_DynstrFree(&ColVals); return TRUE; } my_bool MADB_DynStrGetColumns(MADB_Stmt *Stmt, MADB_DynString *DynString) { unsigned int i; if (MADB_DYNAPPENDCONST(DynString, " (")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (i && MADB_DYNAPPENDCONST(DynString, ", ")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } if (MADB_DynStrAppendQuoted(DynString, Stmt->stmt->fields[i].org_name)) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } } if (MADB_DYNAPPENDCONST(DynString, " )")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } return FALSE; } my_bool MADB_DynStrGetWhere(MADB_Stmt *Stmt, MADB_DynString *DynString, char *TableName, my_bool ParameterMarkers) { int UniqueCount=0, PrimaryCount= 0; int i, Flag= 0; char *Column= NULL, *Escaped= NULL; SQLLEN StrLength; unsigned long EscapedLength; for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++) { MYSQL_FIELD *field= mysql_fetch_field_direct(FetchMetadata(Stmt), i); if (field->flags & PRI_KEY_FLAG) PrimaryCount++; if (field->flags & UNIQUE_KEY_FLAG) UniqueCount++; } /* We need to use all columns, otherwise it will be difficult to map fields for Positioned Update */ if (PrimaryCount && PrimaryCount != MADB_KeyTypeCount(Stmt->Connection, TableName, PRI_KEY_FLAG)) PrimaryCount= 0; if (UniqueCount && UniqueCount != MADB_KeyTypeCount(Stmt->Connection, TableName, UNIQUE_KEY_FLAG)) UniqueCount= 0; /* if no primary or unique key is in the cursor, the cursor must contain all columns from table in TableName */ if (!PrimaryCount && !UniqueCount) { char StmtStr[256]; MADB_Stmt *CountStmt; int FieldCount= 0; MA_SQLAllocHandle(SQL_HANDLE_STMT, Stmt->Connection, (SQLHANDLE*)&CountStmt); _snprintf(StmtStr, 256, "SELECT * FROM `%s` LIMIT 0", TableName); CountStmt->Methods->ExecDirect(CountStmt, (char *)StmtStr, SQL_NTS); FieldCount= mysql_stmt_field_count(((MADB_Stmt *)CountStmt)->stmt); CountStmt->Methods->StmtFree(CountStmt, SQL_DROP); if (FieldCount != MADB_STMT_COLUMN_COUNT(Stmt)) { MADB_SetError(&Stmt->Error, MADB_ERR_S1000, "Can't build index for update/delete", 0); return TRUE; } } if (MADB_DYNAPPENDCONST(DynString, " WHERE 1")) goto memerror; for (i= 0; i < MADB_STMT_COLUMN_COUNT(Stmt); i++) { MYSQL_FIELD *field= mysql_fetch_field_direct(Stmt->metadata, i); if (field->flags & Flag || !Flag) { if (MADB_DYNAPPENDCONST(DynString, " AND ") || MADB_DynStrAppendQuoted(DynString, field->org_name)) goto memerror; if (ParameterMarkers) { if (MADB_DYNAPPENDCONST(DynString, "=?")) goto memerror; } else { if (!SQL_SUCCEEDED(Stmt->Methods->GetData(Stmt, i+1, SQL_C_CHAR, NULL, 0, &StrLength, TRUE))) { MADB_FREE(Column); return TRUE; } if (StrLength < 0) { if (MADB_DYNAPPENDCONST(DynString, " IS NULL")) goto memerror; } else { Column= MADB_CALLOC(StrLength + 1); Stmt->Methods->GetData(Stmt,i+1, SQL_C_CHAR, Column, StrLength + 1, &StrLength, TRUE); Escaped = MADB_CALLOC(2 * StrLength + 1); EscapedLength= mysql_real_escape_string(Stmt->Connection->mariadb, Escaped, Column, (unsigned long)StrLength); if (MADB_DYNAPPENDCONST(DynString, "= '") || MADB_DynstrAppend(DynString, Escaped) ||//, EscapedLength) || MADB_DYNAPPENDCONST(DynString, "'")) { goto memerror; } MADB_FREE(Column); MADB_FREE(Escaped); } } } } if (MADB_DYNAPPENDCONST(DynString, " LIMIT 1")) goto memerror; MADB_FREE(Column); return FALSE; memerror: MADB_FREE(Column); MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } my_bool MADB_DynStrGetValues(MADB_Stmt *Stmt, MADB_DynString *DynString) { unsigned int i; if (MADB_DYNAPPENDCONST(DynString, " VALUES(")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (MADB_DynstrAppend(DynString, (i) ? ",?" : "?")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } } if (MADB_DYNAPPENDCONST(DynString, ")")) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return TRUE; } return FALSE; } char *MADB_GetInsertStatement(MADB_Stmt *Stmt) { char *StmtStr; size_t Length= 1024; char *p; char *TableName; unsigned int i; if (!(StmtStr= MADB_CALLOC(1024))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0); return NULL; } if (!(TableName= MADB_GetTableName(Stmt))) goto error; p= StmtStr; p+= _snprintf(StmtStr, 1024, "INSERT INTO `%s` (", TableName); for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (strlen(StmtStr) > Length - NAME_LEN - 4/* comma + 2 ticks + terminating NULL */) { Length+= 1024; if (!(StmtStr= MADB_REALLOC(StmtStr, Length))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0); goto error; } } p+= _snprintf(p, Length - strlen(StmtStr), "%s`%s`", (i==0) ? "" : ",", Stmt->stmt->fields[i].org_name); } p+= _snprintf(p, Length - strlen(StmtStr), ") VALUES ("); if (strlen(StmtStr) > Length - mysql_stmt_field_count(Stmt->stmt)*2 - 1)/* , and ? for each column + (- 1 comma for 1st column + closing ')') + terminating NULL */ { Length= strlen(StmtStr) + mysql_stmt_field_count(Stmt->stmt)*2 + 1; if (!(StmtStr= MADB_REALLOC(StmtStr, Length))) { MADB_SetError(&Stmt->Error, MADB_ERR_HY013, NULL, 0); goto error; } } for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { p+= _snprintf(p, Length - strlen(StmtStr), "%s?", (i==0) ? "" : ","); } p+= _snprintf(p, Length - strlen(StmtStr), ")"); return StmtStr; error: if (StmtStr) MADB_FREE(StmtStr); return NULL; } my_bool MADB_ValidateStmt(MADB_QUERY *Query) { return Query->QueryType != MADB_QUERY_SET_NAMES; } char *MADB_ToLower(const char *src, char *buff, size_t buff_size) { size_t i= 0; if (buff_size > 0) { while (*src && i < buff_size) { buff[i++]= tolower(*src++); } buff[i == buff_size ? i - 1 : i]= '\0'; } return buff; } int InitClientCharset(Client_Charset *cc, const char * name) { /* There is no legal charset names longer than 31 chars */ char lowered[32]; cc->cs_info= mariadb_get_charset_by_name(MADB_ToLower(name, lowered, sizeof(lowered))); if (cc->cs_info == NULL) { return 1; } cc->CodePage= cc->cs_info->codepage; return 0; } void CopyClientCharset(Client_Charset * Src, Client_Charset * Dst) { Dst->CodePage= Src->CodePage; Dst->cs_info= Src->cs_info; } void CloseClientCharset(Client_Charset *cc) { } /* Hmmm... Length in characters is SQLLEN, octet length SQLINTEGER */ SQLLEN MbstrOctetLen(const char *str, SQLLEN *CharLen, MARIADB_CHARSET_INFO *cs) { SQLLEN result= 0, inChars= *CharLen; if (str) { if (cs->mb_charlen == NULL) { /* Charset uses no more than a byte per char. Result is strlen or umber of chars */ if (*CharLen < 0) { result= (SQLLEN)strlen(str); *CharLen= result; } else { result= *CharLen; } return result; } else { while (inChars > 0 || (inChars < 0 && *str)) { result+= cs->mb_charlen(0 + *str); --inChars; str+= cs->mb_charlen(*str); } } } if (*CharLen < 0) { *CharLen-= inChars; } return result; } /* Number of characters in given number of bytes */ SQLLEN MbstrCharLen(const char *str, SQLINTEGER OctetLen, MARIADB_CHARSET_INFO *cs) { SQLLEN result= 0; const char *ptr= str; unsigned int charlen; if (str) { if (cs->mb_charlen == NULL || cs->char_maxlen == 1) { return OctetLen; } while (ptr < str + OctetLen) { charlen= cs->mb_charlen((unsigned char)*ptr); if (charlen == 0) { /* Dirty hack to avoid dead loop - Has to be the error! */ charlen= 1; } /* Skipping thru 0 bytes */ while (charlen > 0 && *ptr == '\0') { --charlen; ++ptr; } /* Stopping if current character is terminating NULL - charlen == 0 means all bytes of current char was 0 */ if (charlen == 0) { return result; } /* else we increment ptr for number of left bytes */ ptr+= charlen; ++result; } } return result; } /* Length of NT SQLWCHAR string in characters */ SQLINTEGER SqlwcsCharLen(SQLWCHAR *str, SQLLEN octets) { SQLINTEGER result= 0; SQLWCHAR *end= octets != (SQLLEN)-1 ? str + octets/sizeof(SQLWCHAR) : (SQLWCHAR*)octets /*for simplicity - the address to be always bigger */; if (str) { while (str < end && *str) { str+= (DmUnicodeCs->mb_charlen(*str))/sizeof(SQLWCHAR); if (str > end) { break; } ++result; } } return result; } /* Length in SQLWCHAR units @buff_length[in] - size of the str buffer or negative number */ SQLLEN SqlwcsLen(SQLWCHAR *str, SQLLEN buff_length) { SQLINTEGER result= 0; if (str) { /* If buff_length is negative - we will never hit 1st condition, otherwise we hit it after last character of the buffer is processed */ while ((--buff_length) != -1 && *str) { ++result; ++str; } } return result; } /* Length of a string with respect to specified buffer size @buff_length[in] - size of the str buffer or negative number */ SQLLEN SafeStrlen(SQLCHAR *str, SQLLEN buff_length) { SQLINTEGER result= 0; if (str) { /* If buff_length is negative - we will never hit 1st condition, otherwise we hit it after last character of the buffer is processed */ while ((--buff_length) != -1 && *str) { ++result; ++str; } } return result; }mariadb-connector-odbc-3.1.15/ma_string.h000066400000000000000000000055401414431724000202350ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #ifndef _ma_string_h_ #define _ma_string_h_ char *MADB_ConvertFromWChar(const SQLWCHAR *Ptr, SQLINTEGER PtrLength, SQLULEN *Length, Client_Charset* cc, BOOL *DefaultCharUsed); int MADB_ConvertAnsi2Unicode(Client_Charset* cc, const char *AnsiString, SQLLEN AnsiLength, SQLWCHAR *UnicodeString, SQLLEN UnicodeLength, SQLLEN *LengthIndicator, BOOL IsNull, MADB_Error *Error); char* MADB_GetInsertStatement(MADB_Stmt *Stmt); char* MADB_GetTableName(MADB_Stmt *Stmt); char* MADB_GetCatalogName(MADB_Stmt *Stmt); my_bool MADB_DynStrUpdateSet(MADB_Stmt *Stmt, MADB_DynString *DynString); my_bool MADB_DynStrInsertSet(MADB_Stmt *Stmt, MADB_DynString *DynString); my_bool MADB_DynStrGetWhere(MADB_Stmt *Stmt, MADB_DynString *DynString, char *TableName, my_bool ParameterMarkers); my_bool MADB_DynStrAppendQuoted(MADB_DynString *DynString, char *String); my_bool MADB_DynStrGetColumns(MADB_Stmt *Stmt, MADB_DynString *DynString); my_bool MADB_DynStrGetValues(MADB_Stmt *Stmt, MADB_DynString *DynString); SQLWCHAR* MADB_ConvertToWchar(const char *Ptr, SQLLEN PtrLength, Client_Charset* cc); SQLLEN MADB_SetString(Client_Charset* cc, void *Dest, SQLULEN DestLength, const char *Src, SQLLEN SrcLength, MADB_Error *Error); my_bool MADB_ValidateStmt(MADB_QUERY *Query); SQLLEN MbstrOctetLen(const char *str, SQLLEN *CharLen, MARIADB_CHARSET_INFO *cs); SQLLEN MbstrCharLen(const char *str, SQLINTEGER OctetLen, MARIADB_CHARSET_INFO *cs); SQLINTEGER SqlwcsCharLen(SQLWCHAR *str, SQLLEN octets); SQLLEN SqlwcsLen(SQLWCHAR *str, SQLLEN buff_length); SQLLEN SafeStrlen(SQLCHAR *str, SQLLEN buff_length); #define ADJUST_LENGTH(ptr, len)\ if((ptr) && ((len) == SQL_NTS))\ len= sizeof(len) == 2 ? (SQLSMALLINT)strlen((ptr)) : (SQLINTEGER)strlen((ptr));\ else if (!(ptr))\ len= 0 #endif mariadb-connector-odbc-3.1.15/ma_typeconv.c000066400000000000000000000547511414431724000206010ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2017 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ /* ODBC C->SQL and SQL->C type conversion functions */ #include /* Borrowed from C/C and adapted */ SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, MYSQL_TIME *Tm, BOOL Interval, MADB_Error *Error, BOOL *isTime) { char *localCopy= MADB_ALLOC(Length + 1), *Start= localCopy, *Frac, *End= Start + Length; my_bool isDate= 0; if (Start == NULL) { return MADB_SetError(Error, MADB_ERR_HY001, NULL, 0); } memset(Tm, 0, sizeof(MYSQL_TIME)); memcpy(Start, Str, Length); Start[Length]= '\0'; while (Length && isspace(*Start)) ++Start, --Length; if (Length == 0) { goto end;//MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } /* Determine time type: MYSQL_TIMESTAMP_DATE: [-]YY[YY].MM.DD MYSQL_TIMESTAMP_DATETIME: [-]YY[YY].MM.DD hh:mm:ss.mmmmmm MYSQL_TIMESTAMP_TIME: [-]hh:mm:ss.mmmmmm */ if (strchr(Start, '-')) { if (sscanf(Start, "%d-%u-%u", &Tm->year, &Tm->month, &Tm->day) < 3) { return MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } isDate= 1; if (!(Start= strchr(Start, ' '))) { goto check; } } if (!strchr(Start, ':')) { goto check; } if (isDate == 0) { *isTime= 1; } if ((Frac= strchr(Start, '.')) != NULL) /* fractional seconds */ { size_t FracMulIdx= End - (Frac + 1) - 1/*to get index array index */; /* ODBC - nano-seconds */ if (sscanf(Start, "%d:%u:%u.%6lu", &Tm->hour, &Tm->minute, &Tm->second, &Tm->second_part) < 4) { return MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } /* 9 digits up to nano-seconds, and -1 since comparing with arr idx */ if (FracMulIdx < 6 - 1) { static unsigned long Mul[]= {100000, 10000, 1000, 100, 10}; Tm->second_part*= Mul[FracMulIdx]; } } else { if (sscanf(Start, "%d:%u:%u", &Tm->hour, &Tm->minute, &Tm->second) < 3) { return MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } } check: if (Interval == FALSE) { if (isDate) { if (Tm->year > 0) { if (Tm->year < 70) { Tm->year+= 2000; } else if (Tm->year < 100) { Tm->year+= 1900; } } } } end: MADB_FREE(localCopy); return SQL_SUCCESS; } /* {{{ MADB_ConversionSupported */ BOOL MADB_ConversionSupported(MADB_DescRecord *From, MADB_DescRecord *To) { switch (From->ConciseType) { case SQL_C_TIMESTAMP: case SQL_C_TYPE_TIMESTAMP: case SQL_C_TIME: case SQL_C_TYPE_TIME: case SQL_C_DATE: case SQL_C_TYPE_DATE: if (To->Type == SQL_INTERVAL) { return FALSE; } } return TRUE; } /* }}} */ /* {{{ MADB_ConvertCharToBit */ char MADB_ConvertCharToBit(MADB_Stmt *Stmt, char *src) { char *EndPtr= NULL; float asNumber= strtof(src, &EndPtr); if (asNumber < 0 || asNumber > 1) { /* 22003 */ } else if (asNumber != 0 && asNumber != 1) { /* 22001 */ } else if (EndPtr != NULL && *EndPtr != '\0') { /* 22018. TODO: check if condition is correct */ } return asNumber != 0 ? '\1' : '\0'; } /* }}} */ /* {{{ MADB_ConvertNumericToChar */ size_t MADB_ConvertNumericToChar(SQL_NUMERIC_STRUCT *Numeric, char *Buffer, int *ErrorCode) { const double DenominatorTable[]= {1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0/*9*/, 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0, 100000000000000.0, 1000000000000000.0/*15*/, 10000000000000000.0, 100000000000000000.0, 1000000000000000000.0, 10000000000000000000.0, 1e+20 /*20*/, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38 }; unsigned long long Numerator= 0; double Denominator; int Scale= 0; unsigned long long ByteDenominator= 1; int i; char* p; size_t Length; Buffer[0]= 0; *ErrorCode= 0; Scale+= (Numeric->scale < 0) ? -Numeric->scale : Numeric->scale; for (i= 0; i < SQL_MAX_NUMERIC_LEN; ++i) { if (i > 7 && Numeric->val[i] != '\0') { *ErrorCode = MADB_ERR_22003; return 0; } Numerator += Numeric->val[i] * ByteDenominator; ByteDenominator <<= 8; } if (Numeric->scale > 0) { Denominator = DenominatorTable[Scale];// pow(10, Scale); char tmp[10 /*1 sign + 1 % + 1 dot + 3 scale + 1f + 1\0 */]; _snprintf(tmp, sizeof(tmp), "%s%%.%df", Numeric->sign ? "" : "-", Numeric->scale); _snprintf(Buffer, MADB_CHARSIZE_FOR_NUMERIC, tmp, Numerator / Denominator); } else { _snprintf(Buffer, MADB_CHARSIZE_FOR_NUMERIC, "%s%llu", Numeric->sign ? "" : "-", Numerator); /* Checking Truncation for negative/zero scale before adding 0 */ Length= strlen(Buffer) - (Numeric->sign ? 0 : 1); if (Length > Numeric->precision) { *ErrorCode = MADB_ERR_22003; goto end; } for (i= 0; i < Scale; ++i) { strcat(Buffer, "0"); } } if (Buffer[0] == '-') { ++Buffer; } Length= strlen(Buffer); /* Truncation checks: 1st ensure, that the digits before decimal point will fit */ if ((p= strchr(Buffer, '.'))) { if (Numeric->precision != 0 && (p - Buffer) > Numeric->precision) { *ErrorCode= MADB_ERR_22003; Length= Numeric->precision; Buffer[Numeric->precision]= 0; goto end; } /* If scale >= precision, we still can have no truncation */ if (Length > (unsigned int)(Numeric->precision + 1)/*dot*/ && Scale < Numeric->precision) { *ErrorCode= MADB_ERR_01S07; Length = Numeric->precision + 1/*dot*/; Buffer[Length]= 0; goto end; } } end: /* check if last char is decimal point */ if (Length > 0 && Buffer[Length - 1] == '.') { Buffer[Length - 1]= 0; } if (Numeric->sign == 0) { ++Length; } return Length; } /* }}} */ /* {{{ MADB_ConvertNullValue */ SQLRETURN MADB_ConvertNullValue(MADB_Stmt *Stmt, MYSQL_BIND *MaBind) { MaBind->buffer_type= MYSQL_TYPE_NULL; MaBind->buffer_length= 0; return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_ProcessIndicator */ /* Returns TRUE if indicator contains some special value, and thus no further type conversion is needed */ BOOL MADB_ProcessIndicator(MADB_Stmt *Stmt, SQLLEN Indicator, char * DefaultValue, MYSQL_BIND *MaBind) { switch (Indicator) { case SQL_COLUMN_IGNORE: if (DefaultValue == NULL) { MADB_ConvertNullValue(Stmt, MaBind); } else { MaBind->buffer= DefaultValue; MaBind->buffer_length= (unsigned long)strlen(DefaultValue); MaBind->buffer_type= MYSQL_TYPE_STRING; } return TRUE; case SQL_NULL_DATA: MADB_ConvertNullValue(Stmt, MaBind); return TRUE; } return FALSE; } /* }}} */ /* {{{ MADB_CalculateLength */ SQLLEN MADB_CalculateLength(MADB_Stmt *Stmt, SQLLEN *OctetLengthPtr, MADB_DescRecord *CRec, void* DataPtr) { /* If no OctetLengthPtr was specified, or OctetLengthPtr is SQL_NTS character are considered to be NULL binary data are null terminated */ if (!OctetLengthPtr || *OctetLengthPtr == SQL_NTS) { /* Meaning of Buffer Length is not quite clear in specs. Thus we treat in the way, that does not break (old) testcases. i.e. we neglect its value if Length Ptr is specified */ SQLLEN BufferLen= OctetLengthPtr ? -1 : CRec->OctetLength; switch (CRec->ConciseType) { case SQL_C_WCHAR: /* CRec->OctetLength eq 0 means not 0-length buffer, but that this value is not specified. Thus -1, for SqlwcsLen and SafeStrlen that means buffer len is not specified */ return SqlwcsLen((SQLWCHAR *)DataPtr, BufferLen/sizeof(SQLWCHAR) - test(BufferLen == 0)) * sizeof(SQLWCHAR); break; case SQL_C_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: case SQL_C_CHAR: return SafeStrlen((SQLCHAR *)DataPtr, BufferLen != 0 ? BufferLen : -1); } } else { return *OctetLengthPtr; } return CRec->OctetLength; } /* }}} */ /* {{{ MADB_GetBufferForSqlValue */ void* MADB_GetBufferForSqlValue(MADB_Stmt *Stmt, MADB_DescRecord *CRec, size_t Size) { if (Stmt->RebindParams || CRec->InternalBuffer == NULL) { MADB_FREE(CRec->InternalBuffer); CRec->InternalBuffer= MADB_CALLOC(Size); if (CRec->InternalBuffer == NULL) { MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); return NULL; } } return (void *)CRec->InternalBuffer; } /* }}} */ /* {{{ MADB_Wchar2Sql */ SQLRETURN MADB_Wchar2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { SQLULEN mbLength=0; MADB_FREE(CRec->InternalBuffer); /* conn cs ? */ CRec->InternalBuffer= MADB_ConvertFromWChar((SQLWCHAR *)DataPtr, (SQLINTEGER)(Length / sizeof(SQLWCHAR)), &mbLength, &Stmt->Connection->Charset, NULL); if (CRec->InternalBuffer == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY001, NULL, 0); } *LengthPtr= (unsigned long)mbLength; *Buffer= CRec->InternalBuffer; MaBind->buffer_type= MYSQL_TYPE_STRING; return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_Char2Sql */ SQLRETURN MADB_Char2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { switch (SqlRec->Type) { case SQL_BIT: if (*Buffer == NULL) { CRec->InternalBuffer= (char *)MADB_GetBufferForSqlValue(Stmt, CRec, MaBind->buffer_length); if (CRec->InternalBuffer == NULL) { return Stmt->Error.ReturnValue; } *Buffer= CRec->InternalBuffer; } *LengthPtr= 1; **(char**)Buffer= MADB_ConvertCharToBit(Stmt, DataPtr); MaBind->buffer_type= MYSQL_TYPE_TINY; break; case SQL_DATETIME: { MYSQL_TIME Tm; SQL_TIMESTAMP_STRUCT Ts; BOOL isTime; /* Enforcing constraints on date/time values */ RETURN_ERROR_OR_CONTINUE(MADB_Str2Ts(DataPtr, Length, &Tm, FALSE, &Stmt->Error, &isTime)); MADB_CopyMadbTimeToOdbcTs(&Tm, &Ts); RETURN_ERROR_OR_CONTINUE(MADB_TsConversionIsPossible(&Ts, SqlRec->ConciseType, &Stmt->Error, MADB_ERR_22018, isTime)); /* To stay on the safe side - still sending as string in the default branch */ } default: /* Bulk shouldn't get here, thus logic for single paramset execution */ *LengthPtr= (unsigned long)Length; *Buffer= DataPtr; MaBind->buffer_type= MYSQL_TYPE_STRING; } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_Numeric2Sql */ SQLRETURN MADB_Numeric2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { SQL_NUMERIC_STRUCT *p; int ErrorCode= 0; /* We might need to preserve this pointer to be able to later release the memory */ CRec->InternalBuffer= (char *)MADB_GetBufferForSqlValue(Stmt, CRec, MADB_CHARSIZE_FOR_NUMERIC); if (CRec->InternalBuffer == NULL) { return Stmt->Error.ReturnValue; } p= (SQL_NUMERIC_STRUCT *)DataPtr; p->scale= (SQLSCHAR)SqlRec->Scale; p->precision= (SQLSCHAR)SqlRec->Precision; *LengthPtr= (unsigned long)MADB_ConvertNumericToChar((SQL_NUMERIC_STRUCT *)p, CRec->InternalBuffer, &ErrorCode);; *Buffer= CRec->InternalBuffer; MaBind->buffer_type= MYSQL_TYPE_STRING; if (ErrorCode) { /*TODO: I guess this parameters row should be skipped */ return MADB_SetError(&Stmt->Error, ErrorCode, NULL, 0); } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_TsConversionIsPossible */ SQLRETURN MADB_TsConversionIsPossible(SQL_TIMESTAMP_STRUCT *ts, SQLSMALLINT SqlType, MADB_Error *Error, enum enum_madb_error SqlState, int isTime) { /* I think instead of MADB_ERR_22008 there should be also SqlState */ switch (SqlType) { case SQL_TIME: case SQL_TYPE_TIME: if (ts->fraction) { return MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } break; case SQL_DATE: case SQL_TYPE_DATE: if (ts->hour + ts->minute + ts->second + ts->fraction) { return MADB_SetError(Error, MADB_ERR_22008, NULL, 0); } default: /* This only would be good for SQL_TYPE_TIME. If C type is time(isTime!=0), and SQL type is timestamp, date fields may be NULL - driver should set them to current date */ if ((isTime == 0 && ts->year == 0) || ts->month == 0 || ts->day == 0) { return MADB_SetError(Error, SqlState, NULL, 0); } } return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_Timestamp2Sql */ SQLRETURN MADB_Timestamp2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { MYSQL_TIME *tm= NULL; SQL_TIMESTAMP_STRUCT *ts= (SQL_TIMESTAMP_STRUCT *)DataPtr; RETURN_ERROR_OR_CONTINUE(MADB_TsConversionIsPossible(ts, SqlRec->ConciseType, &Stmt->Error, MADB_ERR_22007, 0)); if (*Buffer == NULL) { tm= (MYSQL_TIME*)MADB_GetBufferForSqlValue(Stmt, CRec, sizeof(MYSQL_TIME)); if (tm == NULL) { /* Error is set in function responsible for allocation */ return Stmt->Error.ReturnValue; } *Buffer= tm; } else { tm= *Buffer; } /* Default types. Not quite clear if time_type has any effect */ tm->time_type= MYSQL_TIMESTAMP_DATETIME; MaBind->buffer_type= MYSQL_TYPE_DATETIME;//MYSQL_TYPE_TIMESTAMP; switch (SqlRec->ConciseType) { case SQL_TYPE_DATE: if (ts->hour + ts->minute + ts->second + ts->fraction != 0) { return MADB_SetError(&Stmt->Error, MADB_ERR_22008, "Time fields are nonzero", 0); } MaBind->buffer_type= MYSQL_TYPE_DATE; tm->time_type= MYSQL_TIMESTAMP_DATE; tm->year= ts->year; tm->month= ts->month; tm->day= ts->day; break; case SQL_TYPE_TIME: if (ts->fraction != 0) { return MADB_SetError(&Stmt->Error, MADB_ERR_22008, "Fractional seconds fields are nonzero", 0); } if (!VALID_TIME(ts)) { return MADB_SetError(&Stmt->Error, MADB_ERR_22007, "Invalid time", 0); } MaBind->buffer_type= MYSQL_TYPE_TIME; tm->time_type= MYSQL_TIMESTAMP_TIME; tm->hour= ts->hour; tm->minute= ts->minute; tm->second= ts->second; break; default: MADB_CopyOdbcTsToMadbTime(ts, tm); } *LengthPtr= sizeof(MYSQL_TIME); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_Time2Sql */ SQLRETURN MADB_Time2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { MYSQL_TIME *tm= NULL; SQL_TIME_STRUCT *ts= (SQL_TIME_STRUCT *)DataPtr; if ((SqlRec->ConciseType == SQL_TYPE_TIME || SqlRec->ConciseType == SQL_TYPE_TIMESTAMP || SqlRec->ConciseType == SQL_TIME || SqlRec->ConciseType == SQL_TIMESTAMP || SqlRec->ConciseType == SQL_DATETIME) && !VALID_TIME(ts)) { return MADB_SetError(&Stmt->Error, MADB_ERR_22007, NULL, 0); } if (*Buffer == NULL) { tm= (MYSQL_TIME*)MADB_GetBufferForSqlValue(Stmt, CRec, sizeof(MYSQL_TIME)); if (tm == NULL) { /* Error is set in function responsible for allocation */ return Stmt->Error.ReturnValue; } *Buffer= tm; } else { tm= *Buffer; } if(SqlRec->ConciseType == SQL_TYPE_TIMESTAMP || SqlRec->ConciseType == SQL_TIMESTAMP || SqlRec->ConciseType == SQL_DATETIME) { time_t sec_time; struct tm * cur_tm; sec_time= time(NULL); cur_tm= localtime(&sec_time); tm->year= 1900 + cur_tm->tm_year; tm->month= cur_tm->tm_mon + 1; tm->day= cur_tm->tm_mday; tm->second_part= 0; tm->time_type= MYSQL_TIMESTAMP_DATETIME; MaBind->buffer_type= MYSQL_TYPE_TIMESTAMP; } else { tm->year= 0; tm->month= 0; tm->day= 0; tm->time_type = MYSQL_TIMESTAMP_TIME; MaBind->buffer_type= MYSQL_TYPE_TIME; } tm->hour= ts->hour; tm->minute= ts->minute; tm->second= ts->second; tm->second_part= 0; *LengthPtr= sizeof(MYSQL_TIME); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_IntervalHtoMS2Sql */ SQLRETURN MADB_IntervalHtoMS2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { MYSQL_TIME *tm= NULL; SQL_INTERVAL_STRUCT *is= (SQL_INTERVAL_STRUCT *)DataPtr; if (*Buffer == NULL) { tm= (MYSQL_TIME*)MADB_GetBufferForSqlValue(Stmt, CRec, sizeof(MYSQL_TIME)); if (tm == NULL) { /* Error is set in function responsible for allocation */ return Stmt->Error.ReturnValue; } *Buffer= tm; } else { tm= *Buffer; } tm->hour= is->intval.day_second.hour; tm->minute= is->intval.day_second.minute; tm->second= CRec->ConciseType == SQL_C_INTERVAL_HOUR_TO_SECOND ? is->intval.day_second.second : 0; tm->second_part= 0; tm->time_type= MYSQL_TIMESTAMP_TIME; MaBind->buffer_type= MYSQL_TYPE_TIME; *LengthPtr= sizeof(MYSQL_TIME); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_Date2Sql */ SQLRETURN MADB_Date2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { MYSQL_TIME *tm= NULL, **BuffPtr= (MYSQL_TIME**)Buffer; SQL_DATE_STRUCT *ts= (SQL_DATE_STRUCT *)DataPtr; if (*BuffPtr == NULL) { tm= (MYSQL_TIME*)MADB_GetBufferForSqlValue(Stmt, CRec, sizeof(MYSQL_TIME)); if (tm == NULL) { /* Error is set in function responsible for allocation */ return Stmt->Error.ReturnValue; } *BuffPtr= tm; } else { tm= *BuffPtr; } tm->year= ts->year; tm->month= ts->month; tm->day= ts->day; tm->hour= tm->minute= tm->second= tm->second_part= 0; tm->time_type= MYSQL_TIMESTAMP_DATE; MaBind->buffer_type= MYSQL_TYPE_DATE; *LengthPtr= sizeof(MYSQL_TIME); return SQL_SUCCESS; } /* }}} */ /* {{{ MADB_ConvertC2Sql */ SQLRETURN MADB_ConvertC2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr) { if (Buffer == NULL) { MaBind->buffer= NULL; Buffer= &MaBind->buffer; } if (LengthPtr == NULL) { LengthPtr= &MaBind->buffer_length; } /* Switch to fill BIND structures based on C and SQL type */ switch (CRec->ConciseType) { case WCHAR_TYPES: RETURN_ERROR_OR_CONTINUE(MADB_Wchar2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case CHAR_BINARY_TYPES: RETURN_ERROR_OR_CONTINUE(MADB_Char2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case SQL_C_NUMERIC: RETURN_ERROR_OR_CONTINUE(MADB_Numeric2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case SQL_C_TIMESTAMP: case SQL_TYPE_TIMESTAMP: RETURN_ERROR_OR_CONTINUE(MADB_Timestamp2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case SQL_C_TIME: case SQL_C_TYPE_TIME: RETURN_ERROR_OR_CONTINUE(MADB_Time2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case SQL_C_INTERVAL_HOUR_TO_MINUTE: case SQL_C_INTERVAL_HOUR_TO_SECOND: RETURN_ERROR_OR_CONTINUE(MADB_IntervalHtoMS2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; case SQL_C_DATE: case SQL_TYPE_DATE: RETURN_ERROR_OR_CONTINUE(MADB_Date2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, Buffer, LengthPtr)); break; default: /* memset(MaBind, 0, sizeof(MYSQL_BIND)); MaBind->buffer_length= 0; */ MaBind->buffer_type= 0; MaBind->is_unsigned= 0; *LengthPtr= (unsigned long)Length; MaBind->buffer_type= MADB_GetMaDBTypeAndLength(CRec->ConciseType, &MaBind->is_unsigned, &MaBind->buffer_length); if (!CRec->OctetLength) { CRec->OctetLength= MaBind->buffer_length; } *Buffer= DataPtr; } /* End of switch (CRec->ConsiseType) */ /* We need it in case SQL_SUCCESS_WITH_INFO was set, we can't just return SQL_SUCCESS */ return Stmt->Error.ReturnValue; } /* }}} */ /* {{{ MADB_C2SQL */ /* Main entrance function for C type to SQL type conversion*/ SQLRETURN MADB_C2SQL(MADB_Stmt* Stmt, MADB_DescRecord *CRec, MADB_DescRecord *SqlRec, SQLULEN ParamSetIdx, MYSQL_BIND *MaBind) { SQLLEN *IndicatorPtr= NULL; SQLLEN *OctetLengthPtr= NULL; void *DataPtr= NULL; SQLLEN Length= 0; IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->IndicatorPtr, ParamSetIdx, sizeof(SQLLEN)); OctetLengthPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->OctetLengthPtr, ParamSetIdx, sizeof(SQLLEN)); if (PARAM_IS_DAE(OctetLengthPtr)) { if (!DAE_DONE(Stmt)) { return SQL_NEED_DATA; } else { MaBind->buffer_type= MADB_GetMaDBTypeAndLength(CRec->ConciseType, &MaBind->is_unsigned, &MaBind->buffer_length); /* I guess we can leave w/out this. Keeping it so far for safety */ MaBind->long_data_used= '\1'; return SQL_SUCCESS; } } /* -- End of DAE parameter processing -- */ if (IndicatorPtr && MADB_ProcessIndicator(Stmt, *IndicatorPtr, CRec->DefaultValue, MaBind)) { return SQL_SUCCESS; } /* -- Special cases are done, i.e. not a DAE etc, general case -- */ DataPtr= GetBindOffset(Stmt->Apd, CRec, CRec->DataPtr, ParamSetIdx, CRec->OctetLength); /* If indicator wasn't NULL_DATA, but data pointer is still NULL, we convert NULL value */ if (!DataPtr) { return MADB_ConvertNullValue(Stmt, MaBind); } Length= MADB_CalculateLength(Stmt, OctetLengthPtr, CRec, DataPtr); RETURN_ERROR_OR_CONTINUE(MADB_ConvertC2Sql(Stmt, CRec, DataPtr, Length, SqlRec, MaBind, NULL, NULL)); /* We need it in case SUCCESS_WITH_INFO was set */ return Stmt->Error.ReturnValue; } /* }}} */ mariadb-connector-odbc-3.1.15/ma_typeconv.h000066400000000000000000000072241414431724000205770ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2017 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #pragma once #ifndef _ma_typeconv_h #define _ma_typeconv_h /* Argument should be pointer to SQL_TIMESTAMP_STRUCT or MYSQL_TIME */ #define VALID_TIME(PTR2TM_OR_TS) (PTR2TM_OR_TS->hour < 24 && PTR2TM_OR_TS->minute < 60 && PTR2TM_OR_TS->second < 60) /* 39 - max 16byte number lenght + 1 for sign + 1 for dot + 1 for + scale up to 38 \0 */ #define MADB_CHARSIZE_FOR_NUMERIC 80 BOOL MADB_ConversionSupported(MADB_DescRecord *From, MADB_DescRecord *To); size_t MADB_ConvertNumericToChar(SQL_NUMERIC_STRUCT *Numeric, char *Buffer, int *ErrorCode); SQLLEN MADB_CalculateLength(MADB_Stmt *Stmt, SQLLEN *OctetLengthPtr, MADB_DescRecord *CRec, void* DataPtr); SQLRETURN MADB_C2SQL(MADB_Stmt* Stmt, MADB_DescRecord *CRec, MADB_DescRecord *SqlRec, SQLULEN ParamSetIdx, MYSQL_BIND *bind); SQLRETURN MADB_Wchar2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_Char2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_Numeric2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_Timestamp2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_Time2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_IntervalHtoMS2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_Date2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_ConvertC2Sql(MADB_Stmt *Stmt, MADB_DescRecord *CRec, void* DataPtr, SQLLEN Length, MADB_DescRecord *SqlRec, MYSQL_BIND *MaBind, void **Buffer, unsigned long *LengthPtr); SQLRETURN MADB_TsConversionIsPossible(SQL_TIMESTAMP_STRUCT *ts, SQLSMALLINT SqlType, MADB_Error *Error, enum enum_madb_error SqlState, int isTime); SQLRETURN MADB_Str2Ts(const char *Str, size_t Length, MYSQL_TIME *Tm, BOOL Interval, MADB_Error *Error, BOOL *isTime); #endif mariadb-connector-odbc-3.1.15/maodbc.def000066400000000000000000000034071414431724000200060ustar00rootroot00000000000000{ global: SQLAllocConnect; SQLAllocEnv; SQLAllocHandle; SQLAllocStmt; SQLBindCol; SQLBindParameter; SQLBrowseConnect; SQLBrowseConnectW; SQLBulkOperations; SQLCancel; SQLCloseCursor; SQLColAttribute; SQLColAttributeW; SQLColAttributes; SQLColAttributesW; SQLColumnPrivileges; SQLColumnPrivilegesW; SQLColumns; SQLColumnsW; SQLConnect; SQLConnectW; SQLCopyDesc; SQLDescribeCol; SQLDescribeColW; SQLDescribeParam; SQLDisconnect; SQLDriverConnect; SQLDriverConnectW; SQLEndTran; SQLError; SQLErrorW; SQLExecDirect; SQLExecDirectW; SQLExecute; SQLExtendedFetch; SQLFetch; SQLFetchScroll; SQLForeignKeys; SQLForeignKeysW; SQLFreeConnect; SQLFreeEnv; SQLFreeHandle; SQLFreeStmt; SQLGetConnectAttr; SQLGetConnectAttrW; SQLGetConnectOption; SQLGetConnectOptionW; SQLGetCursorName; SQLGetCursorNameW; SQLGetData; SQLGetDescField; SQLGetDescFieldW; SQLGetDescRec; SQLGetDescRecW; SQLGetDiagField; SQLGetDiagFieldW; SQLGetDiagRec; SQLGetDiagRecW; SQLGetEnvAttr; SQLGetFunctions; SQLGetInfo; SQLGetInfoW; SQLGetStmtAttr; SQLGetStmtAttrW; SQLGetStmtOption; SQLGetTypeInfo; SQLGetTypeInfoW; SQLMoreResults; SQLNativeSql; SQLNativeSqlW; SQLNumParams; SQLNumResultCols; SQLParamData; SQLParamOptions; SQLPrepare; SQLPrepareW; SQLPrimaryKeys; SQLPrimaryKeysW; SQLProcedureColumns; SQLProcedureColumnsW; SQLProcedures; SQLProceduresW; SQLPutData; SQLRowCount; SQLSetConnectAttr; SQLSetConnectAttrW; SQLSetConnectOption; SQLSetConnectOptionW; SQLSetCursorName; SQLSetCursorNameW; SQLSetDescField; SQLSetDescFieldW; SQLSetDescRec; SQLSetDescRecW; SQLSetEnvAttr; SQLSetParam; SQLSetPos; SQLSetScrollOptions; SQLSetStmtAttr; SQLSetStmtAttrW; SQLSetStmtOption; SQLSpecialColumns; SQLSpecialColumnsW; SQLStatistics; SQLStatisticsW; SQLTablePrivileges; SQLTablePrivilegesW; SQLTables; SQLTablesW; SQLTransact; local: *; }; mariadb-connector-odbc-3.1.15/maodbcu.rc.in000066400000000000000000000047631414431724000204540ustar00rootroot00000000000000// Microsoft Visual C++ generated resource script. // #include "stringinfo.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "WinResrc.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // Deutsch (Deutschland) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) LANGUAGE LANG_GERMAN, SUBLANG_GERMAN #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource3.h\0" END 2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION @MARIADB_ODBC_VERSION_MAJOR@,@MARIADB_ODBC_VERSION_MINOR@,@MARIADB_ODBC_VERSION_PATCH@,0 PRODUCTVERSION @MARIADB_ODBC_VERSION_MAJOR@,@MARIADB_ODBC_VERSION_MINOR@,@MARIADB_ODBC_VERSION_PATCH@,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040704b0" BEGIN VALUE "CompanyName", "MariaDB Corporation AB" VALUE "FileDescription", "MariaDB ODBC Unicode Driver" VALUE "FileVersion", "@MARIADB_ODBC_VERSION_MAJOR@.@MARIADB_ODBC_VERSION_MINOR@.@MARIADB_ODBC_VERSION_PATCH@.0" VALUE "InternalName", "ma_odbc_u" VALUE "LegalCopyright", "Copyright (C) 2013, 2019" VALUE "OriginalFilename", "maodbc.dll" VALUE "ProductName", "MariaDB ODBC Unicode Driver" VALUE "ProductVersion", "@MARIADB_ODBC_VERSION_MAJOR@.@MARIADB_ODBC_VERSION_MINOR@.@MARIADB_ODBC_VERSION_PATCH@.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x407, 1200 END END #endif // Deutsch (Deutschland) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED mariadb-connector-odbc-3.1.15/mariadb-odbc-driver.def.in000066400000000000000000000037141414431724000227640ustar00rootroot00000000000000LIBRARY @LIBRARY_NAME@.dll EXPORTS DllMain SQLAllocConnect SQLAllocEnv SQLAllocHandle SQLAllocStmt SQLBindCol SQLBindParameter SQLBrowseConnect SQLBrowseConnect@UNICODE@ SQLBulkOperations SQLCancel SQLCloseCursor SQLColAttribute SQLColAttribute@UNICODE@ SQLColAttributes SQLColAttributes@UNICODE@ SQLColumnPrivileges SQLColumnPrivileges@UNICODE@ SQLColumns SQLColumns@UNICODE@ SQLConnect SQLConnect@UNICODE@ SQLCopyDesc SQLDescribeCol SQLDescribeCol@UNICODE@ SQLDescribeParam SQLDisconnect SQLDriverConnect SQLDriverConnect@UNICODE@ SQLEndTran SQLError SQLError@UNICODE@ SQLExecDirect SQLExecDirect@UNICODE@ SQLExecute SQLExtendedFetch SQLFetch SQLFetchScroll SQLForeignKeys SQLForeignKeys@UNICODE@ SQLFreeConnect SQLFreeEnv SQLFreeHandle SQLFreeStmt SQLGetConnectAttr SQLGetConnectAttr@UNICODE@ SQLGetConnectOption SQLGetConnectOption@UNICODE@ SQLGetCursorName SQLGetCursorName@UNICODE@ SQLGetData SQLGetDescField SQLGetDescField@UNICODE@ SQLGetDescRec SQLGetDescRec@UNICODE@ SQLGetDiagField SQLGetDiagField@UNICODE@ SQLGetDiagRec SQLGetDiagRec@UNICODE@ SQLGetEnvAttr SQLGetFunctions SQLGetInfo SQLGetInfo@UNICODE@ SQLGetStmtAttr SQLGetStmtAttr@UNICODE@ SQLGetStmtOption SQLGetTypeInfo SQLGetTypeInfo@UNICODE@ SQLMoreResults SQLNativeSql SQLNativeSql@UNICODE@ SQLNumParams SQLNumResultCols SQLParamData SQLParamOptions SQLPrepare SQLPrepare@UNICODE@ SQLPrimaryKeys SQLPrimaryKeys@UNICODE@ SQLProcedureColumns SQLProcedureColumns@UNICODE@ SQLProcedures SQLProcedures@UNICODE@ SQLPutData SQLRowCount SQLSetConnectAttr SQLSetConnectAttr@UNICODE@ SQLSetConnectOption SQLSetConnectOption@UNICODE@ SQLSetCursorName SQLSetCursorName@UNICODE@ SQLSetDescField SQLSetDescField@UNICODE@ SQLSetDescRec SQLSetDescRec@UNICODE@ SQLSetEnvAttr SQLSetParam SQLSetPos SQLSetScrollOptions SQLSetStmtAttr SQLSetStmtAttr@UNICODE@ SQLSetStmtOption SQLSpecialColumns SQLSpecialColumns@UNICODE@ SQLStatistics SQLStatistics@UNICODE@ SQLTablePrivileges SQLTablePrivileges@UNICODE@ SQLTables SQLTables@UNICODE@ SQLTransact mariadb-connector-odbc-3.1.15/odbc_3_api.c000066400000000000000000002426611414431724000202360ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2013,2016 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #undef MA_ODBC_DEBUG_ALL #include extern Client_Charset utf8; /* {{{ SQLAllocHandle */ SQLRETURN SQL_API SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr) { SQLRETURN ret; MDBUG_ENTER("SQLAllocHandle"); MDBUG_DUMP(HandleType, d); MDBUG_DUMP(InputHandle, 0x); MDBUG_DUMP(OutputHandlePtr, 0x); ret= MA_SQLAllocHandle(HandleType, InputHandle, OutputHandlePtr); MDBUG_DUMP(ret,d); MDBUG_RETURN(ret); } /* }}} */ SQLRETURN SQL_API SQLAllocConnect(SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr) { return MA_SQLAllocHandle(SQL_HANDLE_DBC, InputHandle, OutputHandlePtr);; } /* }}} */ /* {{{ SQLAllocStmt */ SQLRETURN SQL_API SQLAllocStmt(SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr) { MDBUG_C_ENTER(InputHandle, "SQLAllocStmt"); MDBUG_C_DUMP(InputHandle, InputHandle, 0x); MDBUG_C_DUMP(InputHandle, OutputHandlePtr, 0x); return MA_SQLAllocHandle(SQL_HANDLE_STMT, InputHandle, OutputHandlePtr); } /* }}} */ /* {{{ SQLAllocEnv */ SQLRETURN SQL_API SQLAllocEnv(SQLHANDLE *OutputHandlePtr) { return MA_SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, OutputHandlePtr); } /* }}} */ /* {{{ SQLBindCol */ SQLRETURN SQL_API SQLBindCol(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_Ind) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; MADB_CLEAR_ERROR(&Stmt->Error); MADB_CHECK_STMT_HANDLE(Stmt,stmt); MDBUG_C_ENTER(Stmt->Connection, "SQLBindCol"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, ColumnNumber, u); MDBUG_C_DUMP(Stmt->Connection, TargetType, d); MDBUG_C_DUMP(Stmt->Connection, BufferLength, d); MDBUG_C_DUMP(Stmt->Connection, StrLen_or_Ind, 0x); ret= Stmt->Methods->BindColumn(Stmt, ColumnNumber, TargetType, TargetValuePtr, BufferLength, StrLen_or_Ind); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLBindParameter */ SQLRETURN SQL_API SQLBindParameter(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT InputOutputType, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN ColumnSize, SQLSMALLINT DecimalDigits, SQLPOINTER ParameterValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) { if (!StatementHandle) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLBindParameter(StatementHandle, ParameterNumber, InputOutputType, ValueType, ParameterType, ColumnSize, DecimalDigits, ParameterValuePtr, BufferLength, StrLen_or_IndPtr); } /* }}} */ /* {{{ SQLBrowseConnect */ SQLRETURN SQL_API SQLBrowseConnect(SQLHDBC ConnectionHandle, SQLCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; MDBUG_C_ENTER(Dbc, "SQLBrowseConnect"); MADB_SetError(&Dbc->Error, MADB_ERR_IM001, NULL, 0); ret= Dbc->Error.ReturnValue; MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLBrowseConnectW */ SQLRETURN SQL_API SQLBrowseConnectW(SQLHDBC ConnectionHandle, SQLWCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLWCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; MDBUG_C_ENTER(Dbc, SQLBrowseConnectW); MADB_SetError(&Dbc->Error, MADB_ERR_IM001, NULL, 0); ret= Dbc->Error.ReturnValue; MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLBulkOperations */ SQLRETURN SQL_API SQLBulkOperations(SQLHSTMT StatementHandle, SQLSMALLINT Operation) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLBulkOperations"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, Operation, d); ret= Stmt->Methods->BulkOperations(Stmt, Operation); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLCancel */ SQLRETURN SQL_API SQLCancel(SQLHSTMT StatementHandle) { return MA_SQLCancel(StatementHandle); } /* }}} */ /* {{{ SQLCancelHandle */ /* ODBC version 3.8 */ SQLRETURN SQL_API SQLCancelHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { if (Handle) return SQL_INVALID_HANDLE; switch(HandleType) { case SQL_HANDLE_DBC: { MADB_Stmt Stmt; Stmt.Connection= (MADB_Dbc *)Handle; return MA_SQLCancel((SQLHSTMT)&Stmt); } break; case SQL_HANDLE_STMT: return MA_SQLCancel((SQLHSTMT)Handle); break; } return SQL_INVALID_HANDLE; } /* }}} */ /* {{{ SQLCloseCursor */ SQLRETURN SQL_API SQLCloseCursor(SQLHSTMT StatementHandle) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLCloseCursor"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); if (!Stmt->stmt || (!mysql_stmt_field_count(Stmt->stmt) && Stmt->Connection->Environment->OdbcVersion >= SQL_OV_ODBC3)) { MADB_SetError(&Stmt->Error, MADB_ERR_24000, NULL, 0); ret= Stmt->Error.ReturnValue; } else { ret = Stmt->Methods->StmtFree(Stmt, SQL_CLOSE); } MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColAttribute */ SQLRETURN SQL_API SQLColAttribute (SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, #ifdef SQLCOLATTRIB_SQLPOINTER SQLPOINTER NumericAttributePtr #else SQLLEN *NumericAttributePtr #endif ) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColAttribute"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); MDBUG_C_DUMP(Stmt->Connection, ColumnNumber, u); MDBUG_C_DUMP(Stmt->Connection, FieldIdentifier, u); MDBUG_C_DUMP(Stmt->Connection, CharacterAttributePtr, 0x); MDBUG_C_DUMP(Stmt->Connection, BufferLength, d); MDBUG_C_DUMP(Stmt->Connection, StringLengthPtr, 0x); MDBUG_C_DUMP(Stmt->Connection, NumericAttributePtr, 0x); ret= Stmt->Methods->ColAttribute(Stmt, ColumnNumber, FieldIdentifier, CharacterAttributePtr, BufferLength, StringLengthPtr, NumericAttributePtr, FALSE); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColAttributeW */ SQLRETURN SQL_API SQLColAttributeW (SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLUSMALLINT FieldIdentifier, SQLPOINTER CharacterAttributePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, #ifdef SQLCOLATTRIB_SQLPOINTER SQLPOINTER NumericAttributePtr #else SQLLEN *NumericAttributePtr #endif ) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColAttributeW"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); MDBUG_C_DUMP(Stmt->Connection, ColumnNumber, u); MDBUG_C_DUMP(Stmt->Connection, FieldIdentifier, u); MDBUG_C_DUMP(Stmt->Connection, CharacterAttributePtr, 0x); MDBUG_C_DUMP(Stmt->Connection, BufferLength, d); MDBUG_C_DUMP(Stmt->Connection, StringLengthPtr, 0x); MDBUG_C_DUMP(Stmt->Connection, NumericAttributePtr, 0x); ret= Stmt->Methods->ColAttribute(Stmt, ColumnNumber, FieldIdentifier, CharacterAttributePtr, BufferLength, StringLengthPtr, NumericAttributePtr, TRUE); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColAttributes */ SQLRETURN SQL_API SQLColAttributes(SQLHSTMT hstmt, SQLUSMALLINT icol, SQLUSMALLINT fDescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT * pcbDesc, SQLLEN * pfDesc) { MADB_Stmt *Stmt= (MADB_Stmt *)hstmt; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->ColAttribute(Stmt, icol, MapColAttributeDescType(fDescType), rgbDesc, cbDescMax, pcbDesc, pfDesc, FALSE); } /* }}} */ /* {{{ SQLColAttributesW */ SQLRETURN SQL_API SQLColAttributesW(SQLHSTMT hstmt, SQLUSMALLINT icol, SQLUSMALLINT fDescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT * pcbDesc, SQLLEN * pfDesc) { /* TODO: use internal function, not api */ return SQLColAttributeW(hstmt, icol, MapColAttributeDescType(fDescType), rgbDesc, cbDescMax, pcbDesc, pfDesc); } /* }}} */ /* {{{ SQLColumnPrivileges */ SQLRETURN SQL_API SQLColumnPrivileges(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, SQLCHAR *ColumnName, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColumnPrivileges"); ret= Stmt->Methods->ColumnPrivileges(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3, (char *)ColumnName, NameLength4); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColumnPrivilegesW */ SQLRETURN SQL_API SQLColumnPrivilegesW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3, SQLWCHAR *ColumnName, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0, CpLength4= 0; char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL, *CpColumn= NULL; SQLRETURN ret; if (!StatementHandle) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColumnPrivilegesW"); if (CatalogName != NULL) { CpCatalog = MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable = MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (ColumnName != NULL) { CpColumn = MADB_ConvertFromWChar(ColumnName, NameLength4, &CpLength4, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->ColumnPrivileges(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3, CpColumn, (SQLSMALLINT)CpLength4); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); MADB_FREE(CpColumn); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColumns */ SQLRETURN SQL_API SQLColumns(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, SQLCHAR *ColumnName, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColumns"); ret= Stmt->Methods->Columns(Stmt, (char *)CatalogName,NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3, (char *)ColumnName, NameLength4); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLColumnsW */ SQLRETURN SQL_API SQLColumnsW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3, SQLWCHAR *ColumnName, SQLSMALLINT NameLength4) { char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL, *CpColumn= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0, CpLength4= 0; SQLRETURN ret; MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLColumns"); if (CatalogName != NULL) { CpCatalog= MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema= MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable= MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (ColumnName != NULL) { CpColumn= MADB_ConvertFromWChar(ColumnName, NameLength4, &CpLength4, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->Columns(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3, CpColumn, (SQLSMALLINT)CpLength4); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); MADB_FREE(CpColumn); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLCompleteAsync */ /* ODBC 3.8 */ SQLRETURN SQL_API SQLCompleteAsync(SQLSMALLINT HandleType, SQLHANDLE Handle, RETCODE *AsyncRetCodePtr) { SQLRETURN ret= SQL_ERROR; return ret; } /* }}} */ /* {{{ SQLConnectCommon */ SQLRETURN SQLConnectCommon(SQLHDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, SQLCHAR *UserName, SQLSMALLINT NameLength2, SQLCHAR *Authentication, SQLSMALLINT NameLength3) { MADB_Dbc *Connection= (MADB_Dbc *)ConnectionHandle; MADB_Dsn *Dsn; SQLRETURN ret; my_bool DsnFound; if (!Connection) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Connection->Error); MDBUG_C_ENTER(Connection, "SQLConnect"); MDBUG_C_DUMP(Connection, Connection, 0x); MDBUG_C_DUMP(Connection, ServerName, s); MDBUG_C_DUMP(Connection, NameLength1, d); MDBUG_C_DUMP(Connection, UserName, s); MDBUG_C_DUMP(Connection, NameLength2, d); MDBUG_C_DUMP(Connection, Authentication, s); MDBUG_C_DUMP(Connection, NameLength3, d); if (CheckConnection(Connection)) { MADB_SetError(&Connection->Error, MADB_ERR_08002, NULL, 0); return SQL_ERROR; } if (!(Dsn= MADB_DSN_Init())) { MADB_SetError(&Connection->Error, MADB_ERR_HY001, NULL, 0); return SQL_ERROR; } if (ServerName && !ServerName[0]) { MADB_SetError(&Connection->Error, MADB_ERR_HY000, "Invalid DSN", 0); MADB_DSN_Free(Dsn); return Connection->Error.ReturnValue; } MADB_DSN_SET_STR(Dsn, DSNName, (char *)ServerName, NameLength1); DsnFound= MADB_ReadDSN(Dsn, NULL, TRUE); MADB_DSN_SET_STR(Dsn, UserName, (char *)UserName, NameLength2); MADB_DSN_SET_STR(Dsn, Password, (char *)Authentication, NameLength3); ret= Connection->Methods->ConnectDB(Connection, Dsn); if (SQL_SUCCEEDED(ret)) { MADB_DSN_Free(Connection->Dsn); Connection->Dsn= Dsn; } else { MADB_DSN_Free(Dsn); } MDBUG_C_RETURN(Connection, ret, &Connection->Error); } /* }}} */ /* {{{ SQLConnect */ SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle, SQLCHAR *ServerName, SQLSMALLINT NameLength1, SQLCHAR *UserName, SQLSMALLINT NameLength2, SQLCHAR *Authentication, SQLSMALLINT NameLength3) { return SQLConnectCommon(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3); } /* }}} */ /* {{{ SQLConnectW */ SQLRETURN SQL_API SQLConnectW(SQLHDBC ConnectionHandle, SQLWCHAR *ServerName, SQLSMALLINT NameLength1, SQLWCHAR *UserName, SQLSMALLINT NameLength2, SQLWCHAR *Authentication, SQLSMALLINT NameLength3) { char *MBServerName= NULL, *MBUserName= NULL, *MBAuthentication= NULL; SQLRETURN ret; MADB_Dbc *Dbc= (MADB_Dbc*)ConnectionHandle; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); /* Convert parameters to Cp */ if (ServerName) MBServerName= MADB_ConvertFromWChar(ServerName, NameLength1, 0, Dbc->IsAnsi ? Dbc->ConnOrSrcCharset : &utf8, NULL); if (UserName) MBUserName= MADB_ConvertFromWChar(UserName, NameLength2, 0, Dbc->IsAnsi ? Dbc->ConnOrSrcCharset : &utf8, NULL); if (Authentication) MBAuthentication= MADB_ConvertFromWChar(Authentication, NameLength3, 0, Dbc->IsAnsi ? Dbc->ConnOrSrcCharset : &utf8, NULL); ret= SQLConnectCommon(ConnectionHandle, (SQLCHAR *)MBServerName, SQL_NTS, (SQLCHAR *)MBUserName, SQL_NTS, (SQLCHAR *)MBAuthentication, SQL_NTS); MADB_FREE(MBServerName); MADB_FREE(MBUserName); MADB_FREE(MBAuthentication); return ret; } /* }}} */ /* {{{ SQLCopyDesc */ SQLRETURN SQL_API SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle) { /*TODO: clear error */ return MADB_DescCopyDesc((MADB_Desc *)SourceDescHandle, (MADB_Desc *)TargetDescHandle); } /* }}} */ /* {{{ SQLDataSources */ SQLRETURN SQL_API SQLDataSources(SQLHENV EnvironmentHandle, SQLUSMALLINT Direction, SQLCHAR *ServerName, SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1Ptr, SQLCHAR *Description, SQLSMALLINT BufferLength2, SQLSMALLINT *NameLength2Ptr) { SQLRETURN ret= SQL_ERROR; return ret; } /* }}} */ /* {{{ SQLDataSourcesW */ SQLRETURN SQL_API SQLDataSourcesW(SQLHENV EnvironmentHandle, SQLUSMALLINT Direction, SQLWCHAR *ServerName, SQLSMALLINT BufferLength1, SQLSMALLINT *NameLength1Ptr, SQLWCHAR *Description, SQLSMALLINT BufferLength2, SQLSMALLINT *NameLength2Ptr) { SQLRETURN ret= SQL_ERROR; return ret; } /* }}} */ /* {{{ SQLDescribeCol */ SQLRETURN SQL_API SQLDescribeCol(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLCHAR *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, SQLSMALLINT *DataTypePtr, SQLULEN *ColumnSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLDescribeCol"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, ColumnNumber, u); ret= Stmt->Methods->DescribeCol(Stmt, ColumnNumber, (void *)ColumnName, BufferLength, NameLengthPtr, DataTypePtr, ColumnSizePtr, DecimalDigitsPtr, NullablePtr, FALSE); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLDescribeColW */ SQLRETURN SQL_API SQLDescribeColW(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLWCHAR *ColumnName, SQLSMALLINT BufferLength, SQLSMALLINT *NameLengthPtr, SQLSMALLINT *DataTypePtr, SQLULEN *ColumnSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLDescribeColW"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, ColumnNumber, u); ret= Stmt->Methods->DescribeCol(Stmt, ColumnNumber, (void *)ColumnName, BufferLength, NameLengthPtr, DataTypePtr, ColumnSizePtr, DecimalDigitsPtr, NullablePtr, TRUE); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLDescribeParam */ SQLRETURN SQL_API SQLDescribeParam(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT *DataTypePtr, SQLULEN *ParameterSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); /* MariaDB doesn't support metadata for parameters, so we return default values */ if (DataTypePtr) *DataTypePtr= SQL_VARCHAR; if (ParameterSizePtr) *ParameterSizePtr= 1024 * 1024 * 24; if (NullablePtr) *NullablePtr= SQL_NULLABLE_UNKNOWN; return SQL_SUCCESS; } /* }}} */ /* {{{ SQLDisconnect */ SQLRETURN SQL_API SQLDisconnect(SQLHDBC ConnectionHandle) { SQLRETURN ret= SQL_ERROR; MADB_Dbc *Connection = (MADB_Dbc *)ConnectionHandle; MADB_List *Element, *NextElement; if (!Connection) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Connection->Error); MDBUG_C_ENTER(Connection, "SQLDisconnect"); MDBUG_C_DUMP(Connection, ConnectionHandle, 0x); /* Close all statements */ for (Element= Connection->Stmts; Element; Element= NextElement) { MADB_Stmt *Stmt = (MADB_Stmt*)Element->data; NextElement= Element->next; Stmt->Methods->StmtFree(Stmt, SQL_DROP); } /* Close all explicitly allocated descriptors */ for (Element= Connection->Descrs; Element; Element= NextElement) { NextElement= Element->next; MADB_DescFree((MADB_Desc*)Element->data, FALSE); } if (Connection->mariadb) { mysql_close(Connection->mariadb); Connection->mariadb= NULL; ret= SQL_SUCCESS; } else { MADB_SetError(&Connection->Error, MADB_ERR_08003, NULL, 0); ret= Connection->Error.ReturnValue; } Connection->ConnOrSrcCharset= NULL; MDBUG_C_RETURN(Connection, ret, &Connection->Error); } /* }}} */ /* {{{ SQLDriverConnect */ SQLRETURN SQL_API SQLDriverConnect(SQLHDBC ConnectionHandle, SQLHWND WindowHandle, SQLCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SQLDriverConnect"); MDBUG_C_DUMP(Dbc, Dbc, 0x); MDBUG_C_DUMP(Dbc, InConnectionString, s); MDBUG_C_DUMP(Dbc, StringLength1, d); MDBUG_C_DUMP(Dbc, OutConnectionString, 0x); MDBUG_C_DUMP(Dbc, BufferLength, d); MDBUG_C_DUMP(Dbc, StringLength2Ptr, 0x); MDBUG_C_DUMP(Dbc, DriverCompletion, d); ret= Dbc->Methods->DriverConnect(Dbc, WindowHandle, InConnectionString, StringLength1, OutConnectionString, BufferLength, StringLength2Ptr, DriverCompletion); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLDriverConnectW */ SQLRETURN SQL_API SQLDriverConnectW(SQLHDBC ConnectionHandle, SQLHWND WindowHandle, SQLWCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLWCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion) { SQLRETURN ret= SQL_ERROR; SQLULEN Length= 0; /* Since we need bigger(in bytes) buffer for utf8 string, the length may be > max SQLSMALLINT */ char *InConnStrA= NULL; SQLULEN InStrAOctLen= 0; char *OutConnStrA= NULL; MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; if (!ConnectionHandle) { return SQL_INVALID_HANDLE; } MDBUG_C_ENTER(Dbc, "SQLDriverConnectW"); MADB_CLEAR_ERROR(&Dbc->Error); InConnStrA= MADB_ConvertFromWChar(InConnectionString, StringLength1, &InStrAOctLen, Dbc->IsAnsi ? Dbc->ConnOrSrcCharset : &utf8, NULL); MDBUG_C_DUMP(Dbc, Dbc, 0x); MDBUG_C_DUMP(Dbc, InConnStrA, s); MDBUG_C_DUMP(Dbc, StringLength1, d); MDBUG_C_DUMP(Dbc, OutConnectionString, 0x); MDBUG_C_DUMP(Dbc, BufferLength, d); MDBUG_C_DUMP(Dbc, StringLength2Ptr, 0x); MDBUG_C_DUMP(Dbc, DriverCompletion, d); /* Allocate buffer for Asc OutConnectionString */ if (OutConnectionString && BufferLength) { Length= BufferLength*4 /*Max bytes per utf8 character */; OutConnStrA= (char *)MADB_CALLOC(Length); if (OutConnStrA == NULL) { ret= MADB_SetError(&Dbc->Error, MADB_ERR_HY001, NULL, 0); goto end; } } ret= Dbc->Methods->DriverConnect(Dbc, WindowHandle, (SQLCHAR *)InConnStrA, InStrAOctLen, (SQLCHAR *)OutConnStrA, Length, StringLength2Ptr, DriverCompletion); MDBUG_C_DUMP(Dbc, ret, d); if (!SQL_SUCCEEDED(ret)) goto end; if (OutConnectionString) { Length= MADB_SetString(&utf8, OutConnectionString, BufferLength, OutConnStrA, SQL_NTS, &((MADB_Dbc *)ConnectionHandle)->Error); if (StringLength2Ptr) *StringLength2Ptr= (SQLSMALLINT)Length; } end: MADB_FREE(OutConnStrA); MADB_FREE(InConnStrA); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLDrivers */ SQLRETURN SQL_API SQLDrivers(SQLHENV EnvironmentHandle, SQLUSMALLINT Direction, SQLCHAR *DriverDescription, SQLSMALLINT BufferLength1, SQLSMALLINT *DescriptionLengthPtr, SQLCHAR *DriverAttributes, SQLSMALLINT BufferLength2, SQLSMALLINT *AttributesLengthPtr) { SQLRETURN ret= SQL_ERROR; return ret; } /* }}} */ /* {{{ SQLDriversW */ SQLRETURN SQL_API SQLDriversW(SQLHENV EnvironmentHandle, SQLUSMALLINT Direction, SQLWCHAR *DriverDescription, SQLSMALLINT BufferLength1, SQLSMALLINT *DescriptionLengthPtr, SQLWCHAR *DriverAttributes, SQLSMALLINT BufferLength2, SQLSMALLINT *AttributesLengthPtr) { SQLRETURN ret= SQL_ERROR; return ret; } /* }}} */ /* {{{ SQLEndTran */ SQLRETURN SQL_API SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT CompletionType) { MADB_CHECK_HANDLE_CLEAR_ERROR(HandleType, Handle); return MA_SQLEndTran(HandleType, Handle, CompletionType); } /* }}} */ /* {{{ SQLError */ SQLRETURN SQL_API SQLError(SQLHENV Env, SQLHDBC Dbc, SQLHSTMT Stmt, SQLCHAR *Sqlstate, SQLINTEGER *NativeError, SQLCHAR *Message, SQLSMALLINT MessageMax, SQLSMALLINT *MessageLen) { SQLSMALLINT HandleType= 0; SQLHANDLE Handle= NULL; MADB_Error *error; if (Stmt) { MDBUG_C_ENTER(((MADB_Stmt*)Stmt)->Connection, "SQLError->SQLGetDiagRec"); MDBUG_C_DUMP(((MADB_Stmt*)Stmt)->Connection, Env, 0x); MDBUG_C_DUMP(((MADB_Stmt*)Stmt)->Connection, Dbc, 0x); MDBUG_C_DUMP(((MADB_Stmt*)Stmt)->Connection, Stmt, 0x); Handle= Stmt; HandleType= SQL_HANDLE_STMT; error= &((MADB_Stmt*)Stmt)->Error; } else if (Dbc) { MDBUG_C_ENTER((MADB_Dbc*)Dbc, "SQLError->SQLGetDiagRec"); MDBUG_C_DUMP((MADB_Dbc*)Dbc, Env, 0x); MDBUG_C_DUMP((MADB_Dbc*)Dbc, Dbc, 0x); MDBUG_C_DUMP((MADB_Dbc*)Dbc, Stmt, 0x); Handle= Dbc; HandleType= SQL_HANDLE_DBC; error= &((MADB_Dbc*)Dbc)->Error; } else { MDBUG_ENTER("SQLError->SQLGetDiagRec"); MDBUG_DUMP(Env, 0x); MDBUG_DUMP(Dbc, 0x); MDBUG_DUMP(Stmt, 0x); Handle= Env; HandleType= SQL_HANDLE_ENV; error= &((MADB_Env*)Env)->Error; } return MA_SQLGetDiagRec(HandleType, Handle, ++error->ErrorNum, Sqlstate, NativeError, Message, MessageMax, MessageLen); } /* }}} */ /*{{{ SQLErrorW */ SQLRETURN SQL_API SQLErrorW(SQLHENV Env, SQLHDBC Dbc, SQLHSTMT Stmt, SQLWCHAR *Sqlstate, SQLINTEGER *NativeError, SQLWCHAR *Message, SQLSMALLINT MessageMax, SQLSMALLINT *MessageLen) { SQLSMALLINT HandleType= 0; SQLHANDLE Handle= NULL; MADB_Error *error; if (Stmt) { Handle= Stmt; HandleType= SQL_HANDLE_STMT; error= &((MADB_Stmt*)Stmt)->Error; } else if (Dbc) { Handle= Dbc; HandleType= SQL_HANDLE_DBC; error= &((MADB_Dbc*)Dbc)->Error; } else { Handle= Env; HandleType= SQL_HANDLE_ENV; error= &((MADB_Env*)Env)->Error; } return MA_SQLGetDiagRecW(HandleType, Handle, ++error->ErrorNum, Sqlstate, NativeError, Message, MessageMax, MessageLen); } /* }}} */ /* {{{ SQLTransact */ SQLRETURN SQL_API SQLTransact(SQLHENV Env, SQLHDBC Dbc, SQLUSMALLINT CompletionType) { if (Env != SQL_NULL_HENV) { MADB_CLEAR_ERROR(&((MADB_Env*)Env)->Error); return MA_SQLEndTran(SQL_HANDLE_ENV, Env, CompletionType); } else if (Dbc != SQL_NULL_HDBC) { MADB_CLEAR_ERROR(&((MADB_Dbc*)Dbc)->Error); return MA_SQLEndTran(SQL_HANDLE_DBC, Dbc, CompletionType); } else return SQL_INVALID_HANDLE; } /* }}} */ /* {{{ SQLExecDirect */ SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) ret= SQL_INVALID_HANDLE; else ret= Stmt->Methods->ExecDirect(Stmt, (char *)StatementText, TextLength); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLExecDirectW */ SQLRETURN SQL_API SQLExecDirectW(SQLHSTMT StatementHandle, SQLWCHAR *StatementText, SQLINTEGER TextLength) { char *CpStmt; SQLULEN StmtLength; SQLRETURN ret; BOOL ConversionError; MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLExecDirectW"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); CpStmt= MADB_ConvertFromWChar(StatementText, TextLength, &StmtLength, Stmt->Connection->ConnOrSrcCharset, &ConversionError); MDBUG_C_DUMP(Stmt->Connection, CpStmt, s); if (ConversionError) { MADB_SetError(&Stmt->Error, MADB_ERR_22018, NULL, 0); ret= Stmt->Error.ReturnValue; } else ret= Stmt->Methods->ExecDirect(Stmt, CpStmt, (SQLINTEGER)StmtLength); MADB_FREE(CpStmt); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ SQLRETURN SQL_API SQLExecute(SQLHSTMT StatementHandle) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (StatementHandle == SQL_NULL_HSTMT) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLExecute"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); return Stmt->Methods->Execute(Stmt, FALSE); } /* }}} */ /* {{{ SQLExtendedFetch */ SQLRETURN SQL_API SQLExtendedFetch(SQLHSTMT StatementHandle, SQLUSMALLINT FetchOrientation, SQLLEN FetchOffset, SQLULEN *RowCountPtr, SQLUSMALLINT *RowStatusArray) { SQLRETURN ret; MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLULEN *SaveRowsProcessedPtr= Stmt->Ird->Header.RowsProcessedPtr; SQLUSMALLINT *SaveArrayStatusPtr= Stmt->Ird->Header.ArrayStatusPtr; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLExtendedFetch"); MDBUG_C_DUMP(Stmt->Connection, FetchOrientation, u); MDBUG_C_DUMP(Stmt->Connection, FetchOffset, d); MDBUG_C_DUMP(Stmt->Connection, RowCountPtr, 0x); MDBUG_C_DUMP(Stmt->Connection, RowStatusArray, 0x); Stmt->Ird->Header.RowsProcessedPtr= RowCountPtr; Stmt->Ird->Header.ArrayStatusPtr= RowStatusArray; ret= Stmt->Methods->FetchScroll(Stmt, FetchOrientation, FetchOffset); if (RowStatusArray && SaveArrayStatusPtr) { SQLUINTEGER i; for (i=0; i < Stmt->Ard->Header.ArraySize; i++) SaveArrayStatusPtr[i]= RowStatusArray[i]; } Stmt->Ird->Header.RowsProcessedPtr= SaveRowsProcessedPtr; Stmt->Ird->Header.ArrayStatusPtr= SaveArrayStatusPtr; if (ret == SQL_NO_DATA) { if (RowCountPtr) *RowCountPtr= 0; } if (ret == SQL_ERROR) if (strcmp(Stmt->Error.SqlState, "22002") == 0) ret= SQL_SUCCESS_WITH_INFO; MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLFetch */ SQLRETURN SQL_API SQLFetch(SQLHSTMT StatementHandle) { MADB_Stmt *Stmt; if (StatementHandle == SQL_NULL_HSTMT) return SQL_INVALID_HANDLE; Stmt= (MADB_Stmt *)StatementHandle; MDBUG_C_ENTER(Stmt->Connection, "SQLFetch"); MADB_CLEAR_ERROR(&Stmt->Error); /* SQLFetch is equivalent of SQLFetchScroll(SQL_FETCH_NEXT), 3rd parameter is ignored for SQL_FETCH_NEXT */ MDBUG_C_RETURN(Stmt->Connection, Stmt->Methods->FetchScroll(Stmt, SQL_FETCH_NEXT, 1), &Stmt->Error); } /* }}} */ /* {{{ SQLFetchScroll */ SQLRETURN SQL_API SQLFetchScroll(SQLHSTMT StatementHandle, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset) { MADB_Stmt *Stmt; if (StatementHandle == SQL_NULL_HSTMT) return SQL_INVALID_HANDLE; Stmt= (MADB_Stmt *)StatementHandle; MDBUG_C_ENTER(Stmt->Connection, "SQLFetchScroll"); MDBUG_C_DUMP(Stmt->Connection, FetchOrientation, d); MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_RETURN(Stmt->Connection, Stmt->Methods->FetchScroll(Stmt, FetchOrientation, FetchOffset), &Stmt->Error); } /* }}} */ /* {{{ SQLFreeHandle */ SQLRETURN SQL_API SQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle) { SQLRETURN ret= SQL_INVALID_HANDLE; MADB_CHECK_HANDLE_CLEAR_ERROR(HandleType, Handle); switch (HandleType) { case SQL_HANDLE_ENV: MDBUG_ENTER("SQLFreeHandle"); MDBUG_DUMP(HandleType, d); MDBUG_DUMP(Handle, 0x); ret= MADB_EnvFree((MADB_Env *)Handle); break; case SQL_HANDLE_DBC: { MADB_Dbc *Dbc= (MADB_Dbc *)Handle; MDBUG_C_ENTER(Dbc, "SQLFreeHandle"); MDBUG_C_DUMP(Dbc, HandleType, d); MDBUG_C_DUMP(Dbc, Handle, 0x); ret= MADB_DbcFree(Dbc); return ret; /*MDBUG_C_RETURN(Dbc, ret, &Dbc->Error);*/ } case SQL_HANDLE_DESC: { MADB_Desc *Desc= (MADB_Desc *)Handle; MADB_Dbc *Dbc= Desc->Dbc; MDBUG_C_ENTER(Dbc, "SQLFreeHandle"); MDBUG_C_DUMP(Dbc, HandleType, d); MDBUG_C_DUMP(Dbc, Handle, 0x); /* Error if the descriptor does not belong to application(was automatically alliocated by the driver) Basically DM is supposed to take care of this. Keeping in mind direct linking */ if (!Desc->AppType) { MADB_SetError(&Desc->Error, MADB_ERR_HY017, NULL, 0); MDBUG_C_RETURN(Dbc, Desc->Error.ReturnValue, &Desc->Error); } ret= MADB_DescFree(Desc, FALSE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } case SQL_HANDLE_STMT: { MADB_Stmt *Stmt= (MADB_Stmt *)Handle; MADB_Dbc *Dbc= Stmt->Connection; MDBUG_C_ENTER(Dbc, "SQLFreeHandle"); MDBUG_C_DUMP(Dbc, HandleType, d); MDBUG_C_DUMP(Dbc, Handle, 0x); ret= Stmt->Methods->StmtFree(Stmt, SQL_DROP); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } } MDBUG_RETURN(ret); } /* }}} */ /* {{{ SQLFreeEnv */ SQLRETURN SQL_API SQLFreeEnv(SQLHANDLE henv) { if (henv == SQL_NULL_HENV) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Env*)henv)->Error); return MADB_EnvFree((MADB_Env *)henv); } /* }}} */ /* {{{ SQLFreeConnect */ SQLRETURN SQL_API SQLFreeConnect(SQLHANDLE hdbc) { if (hdbc == SQL_NULL_HDBC) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Dbc*)hdbc)->Error); return MADB_DbcFree((MADB_Dbc*)hdbc); } /* }}} */ /* {{{ SQLFreeStmt */ SQLRETURN SQL_API SQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option) { MADB_Stmt* Stmt = (MADB_Stmt*)StatementHandle; if (StatementHandle== SQL_NULL_HSTMT) return SQL_INVALID_HANDLE; MDBUG_C_ENTER(((MADB_Stmt*)StatementHandle)->Connection, "SQLFreeStmt"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); MDBUG_C_DUMP(Stmt->Connection, Option, d); MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->StmtFree(Stmt, Option); } /* }}} */ /* {{{ SQLForeignKeys */ SQLRETURN SQL_API SQLForeignKeys(SQLHSTMT StatementHandle, SQLCHAR *PKCatalogName, SQLSMALLINT NameLength1, SQLCHAR *PKSchemaName, SQLSMALLINT NameLength2, SQLCHAR *PKTableName, SQLSMALLINT NameLength3, SQLCHAR *FKCatalogName, SQLSMALLINT NameLength4, SQLCHAR *FKSchemaName, SQLSMALLINT NameLength5, SQLCHAR *FKTableName, SQLSMALLINT NameLength6) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if(!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLForeignKeys"); ret= Stmt->Methods->ForeignKeys(Stmt, (char *)PKCatalogName, NameLength1, (char *)PKSchemaName, NameLength2, (char *)PKTableName, NameLength3, (char *)FKCatalogName, NameLength4, (char *)FKSchemaName, NameLength4, (char *)FKTableName, NameLength6); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLForeignKeysW */ SQLRETURN SQL_API SQLForeignKeysW(SQLHSTMT StatementHandle, SQLWCHAR *PKCatalogName, SQLSMALLINT NameLength1, SQLWCHAR *PKSchemaName, SQLSMALLINT NameLength2, SQLWCHAR *PKTableName, SQLSMALLINT NameLength3, SQLWCHAR *FKCatalogName, SQLSMALLINT NameLength4, SQLWCHAR *FKSchemaName, SQLSMALLINT NameLength5, SQLWCHAR *FKTableName, SQLSMALLINT NameLength6) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; char *CpPkCatalog= NULL, *CpPkSchema= NULL, *CpPkTable= NULL, *CpFkCatalog= NULL, *CpFkSchema= NULL, *CpFkTable= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0, CpLength4= 0, CpLength5= 0 , CpLength6= 0; SQLRETURN ret; if(!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLForeignKeysW"); if (PKCatalogName != NULL) { CpPkCatalog = MADB_ConvertFromWChar(PKCatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (PKSchemaName != NULL) { CpPkSchema = MADB_ConvertFromWChar(PKSchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (PKTableName != NULL) { CpPkTable = MADB_ConvertFromWChar(PKTableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (FKCatalogName != NULL) { CpFkCatalog = MADB_ConvertFromWChar(FKCatalogName, NameLength4, &CpLength4, Stmt->Connection->ConnOrSrcCharset, NULL); } if (FKSchemaName != NULL) { CpFkSchema = MADB_ConvertFromWChar(FKSchemaName, NameLength5, &CpLength5, Stmt->Connection->ConnOrSrcCharset, NULL); } if (FKTableName != NULL) { CpFkTable = MADB_ConvertFromWChar(FKTableName, NameLength6, &CpLength6, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->ForeignKeys(Stmt, CpPkCatalog, (SQLSMALLINT)CpLength1, CpPkSchema, (SQLSMALLINT)CpLength2, CpPkTable, (SQLSMALLINT)CpLength3, CpFkCatalog, (SQLSMALLINT)CpLength4, CpFkSchema, (SQLSMALLINT)CpLength5, CpFkTable, (SQLSMALLINT)CpLength6); MADB_FREE(CpPkCatalog); MADB_FREE(CpPkSchema); MADB_FREE(CpPkTable); MADB_FREE(CpFkCatalog); MADB_FREE(CpFkSchema); MADB_FREE(CpFkTable); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLGetConnectAttr */ SQLRETURN SQL_API SQLGetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { if (ConnectionHandle == SQL_NULL_HDBC) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Dbc *)ConnectionHandle)->Error); return MA_SQLGetConnectAttr(ConnectionHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); } /* }}} */ /* {{{ SQLGetConnectAttrW */ SQLRETURN SQL_API SQLGetConnectAttrW(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SQLGetConnectAttr"); MDBUG_C_DUMP(Dbc, Attribute, d); MDBUG_C_DUMP(Dbc, ValuePtr, 0x); MDBUG_C_DUMP(Dbc, BufferLength, d); MDBUG_C_DUMP(Dbc, StringLengthPtr, 0x); ret= Dbc->Methods->GetAttr(Dbc, Attribute, ValuePtr, BufferLength, StringLengthPtr, TRUE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLGetConnectOption */ SQLRETURN SQL_API SQLGetConnectOption(SQLHDBC ConnectionHandle, SQLUSMALLINT Option, SQLPOINTER ValuePtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); return MA_SQLGetConnectAttr(ConnectionHandle, Option, ValuePtr, Option == SQL_ATTR_CURRENT_CATALOG ? SQL_MAX_OPTION_STRING_LENGTH : 0, NULL); } /* }}} */ /* {{{ SQLGetConnectOptionW */ SQLRETURN SQL_API SQLGetConnectOptionW(SQLHDBC ConnectionHandle, SQLUSMALLINT Option, SQLPOINTER ValuePtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); return SQLGetConnectAttrW(ConnectionHandle, Option, ValuePtr, Option == SQL_ATTR_CURRENT_CATALOG ? SQL_MAX_OPTION_STRING_LENGTH : 0, NULL); } /* }}} */ /* {{{ SQLGetCursorName */ SQLRETURN SQL_API SQLGetCursorName( SQLHSTMT StatementHandle, SQLCHAR * CursorName, SQLSMALLINT BufferLength, SQLSMALLINT * NameLengthPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->GetCursorName(Stmt, CursorName, BufferLength, NameLengthPtr, FALSE); } /* }}} */ /* {{{ SQLGetCursorNameW */ SQLRETURN SQL_API SQLGetCursorNameW( SQLHSTMT StatementHandle, SQLWCHAR * CursorName, SQLSMALLINT BufferLength, SQLSMALLINT * NameLengthPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->GetCursorName(Stmt, CursorName, BufferLength, NameLengthPtr, TRUE); } /* }}} */ /* {{{ SQLGetData */ SQLRETURN SQL_API SQLGetData(SQLHSTMT StatementHandle, SQLUSMALLINT Col_or_Param_Num, SQLSMALLINT TargetType, SQLPOINTER TargetValuePtr, SQLLEN BufferLength, SQLLEN *StrLen_or_IndPtr) { MADB_Stmt *Stmt= (MADB_Stmt*)StatementHandle; unsigned int i; MADB_DescRecord *IrdRec; if (StatementHandle== SQL_NULL_HSTMT) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); /* In case we don't have DM(it check for that) */ if (TargetValuePtr == NULL) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY009, NULL, 0); } /* Bookmark */ if (Col_or_Param_Num == 0) { return MADB_GetBookmark(Stmt, TargetType, TargetValuePtr, BufferLength, StrLen_or_IndPtr); } /* We don't need this to be checked in case of "internal" use of the GetData, i.e. for internal needs we should always get the data */ if ( Stmt->CharOffset[Col_or_Param_Num - 1] > 0 && Stmt->CharOffset[Col_or_Param_Num - 1] >= Stmt->Lengths[Col_or_Param_Num - 1]) { return SQL_NO_DATA; } if (BufferLength < 0) { return MADB_SetError(&Stmt->Error, MADB_ERR_HY090, NULL, 0); } /* reset offsets for other columns. Doing that here since "internal" calls should not do that */ for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { if (i != Col_or_Param_Num - 1) { IrdRec= MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_READ); if (IrdRec) { MADB_FREE(IrdRec->InternalBuffer); } Stmt->CharOffset[i]= 0; } } return Stmt->Methods->GetData(StatementHandle, Col_or_Param_Num, TargetType, TargetValuePtr, BufferLength, StrLen_or_IndPtr, FALSE); } /* }}} */ /* {{{ SQLGetDescField */ SQLRETURN SQL_API SQLGetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { if (!DescriptorHandle) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Desc*)DescriptorHandle)->Error); return MADB_DescGetField(DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr, FALSE); } /* }}} */ /* {{{ SQLGetDescFieldW */ SQLRETURN SQL_API SQLGetDescFieldW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { if (!DescriptorHandle) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Desc*)DescriptorHandle)->Error); return MADB_DescGetField(DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, StringLengthPtr, TRUE); } /* }}} */ /* {{{ SQLGetDescRec */ SQLRETURN SQL_API SQLGetDescRec(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLCHAR *Name, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; if (!Desc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Desc->Error); return MADB_DescGetRec(Desc, RecNumber, Name, BufferLength, StringLengthPtr, TypePtr, SubTypePtr, LengthPtr, PrecisionPtr, ScalePtr, NullablePtr, FALSE); } /* }}} */ /* {{{ SQLGetDescRecW */ SQLRETURN SQL_API SQLGetDescRecW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLWCHAR *Name, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; if (!Desc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Desc->Error); return MADB_DescGetRec(Desc, RecNumber, (SQLCHAR *)Name, BufferLength, StringLengthPtr, TypePtr, SubTypePtr, LengthPtr, PrecisionPtr, ScalePtr, NullablePtr, TRUE); } /* }}} */ /* {{{ SQLGetDiagField */ SQLRETURN SQL_API SQLGetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) { if (!Handle) return SQL_INVALID_HANDLE; return MADB_GetDiagField(HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr, FALSE); } /* }}} */ /* {{{ SQLGetDiagFieldW */ SQLRETURN SQL_API SQLGetDiagFieldW(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) { if (!Handle) return SQL_INVALID_HANDLE; return MADB_GetDiagField(HandleType, Handle, RecNumber, DiagIdentifier, DiagInfoPtr, BufferLength, StringLengthPtr, TRUE); } /* }}} */ /* {{{ SQLGetDiagRec */ SQLRETURN SQL_API SQLGetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr) { return MA_SQLGetDiagRec(HandleType, Handle, RecNumber, SQLState, NativeErrorPtr, MessageText, BufferLength, TextLengthPtr); } /* }}} */ /* {{{ SQLGetDiagRecW */ SQLRETURN SQL_API SQLGetDiagRecW(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLWCHAR *SQLState, SQLINTEGER *NativeErrorPtr, SQLWCHAR *MessageText, SQLSMALLINT BufferLength, SQLSMALLINT *TextLengthPtr) { return MA_SQLGetDiagRecW(HandleType, Handle, RecNumber, SQLState, NativeErrorPtr, MessageText, BufferLength, TextLengthPtr); } /* }}} */ /* {{{ SQLGetEnvAttr */ SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_Env *Env= (MADB_Env *)EnvironmentHandle; SQLRETURN ret; MDBUG_ENTER("SQLGetEnvAttr"); MDBUG_DUMP(Attribute, d); MDBUG_DUMP(ValuePtr, 0x); MDBUG_DUMP(BufferLength, d); MDBUG_DUMP(StringLengthPtr, 0x); if (!Env) ret= SQL_INVALID_HANDLE; else { MADB_CLEAR_ERROR(&Env->Error); ret= MADB_EnvGetAttr(Env, Attribute, ValuePtr, BufferLength, StringLengthPtr); } MDBUG_DUMP(ret, d); MDBUG_RETURN(ret); } /* {{{ SQLGetFunctions */ SQLRETURN SQL_API SQLGetFunctions(SQLHDBC ConnectionHandle, SQLUSMALLINT FunctionId, SQLUSMALLINT *SupportedPtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SQLGetFunctions"); MDBUG_C_DUMP(Dbc, FunctionId, d); MDBUG_C_DUMP(Dbc, SupportedPtr, 0x); ret= Dbc->Methods->GetFunctions(Dbc, FunctionId, SupportedPtr); MDBUG_C_RETURN(Dbc,ret, &Dbc->Error); } /* }}} */ /* {{{ SQLGetInfo */ SQLRETURN SQL_API SQLGetInfo(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValuePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SQLGetInfo"); MDBUG_C_DUMP(Dbc, InfoType, d); ret= Dbc->Methods->GetInfo(Dbc, InfoType, InfoValuePtr, BufferLength, StringLengthPtr, FALSE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLGetInfoW */ SQLRETURN SQL_API SQLGetInfoW(SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, SQLPOINTER InfoValuePtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SQLGetInfo"); MDBUG_C_DUMP(Dbc, InfoType, d); MDBUG_C_DUMP(Dbc, InfoValuePtr, 0x); MDBUG_C_DUMP(Dbc, StringLengthPtr, 0x); ret= Dbc->Methods->GetInfo(Dbc, InfoType, InfoValuePtr, BufferLength, StringLengthPtr, TRUE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLGetStmtAttr */ SQLRETURN SQL_API SQLGetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLGetStmtAttr(StatementHandle, Attribute, ValuePtr, BufferLength, StringLengthPtr); } /* }}} */ /* {{{ SQLGetStmtAttrW */ SQLRETURN SQL_API SQLGetStmtAttrW(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->GetAttr(Stmt, Attribute, ValuePtr, BufferLength, StringLengthPtr); } /* }}} */ /* {{{ SQLGetStmtOption */ SQLRETURN SQL_API SQLGetStmtOption(SQLHSTMT StatementHandle, SQLUSMALLINT Option, SQLPOINTER Value) { if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLGetStmtAttr(StatementHandle, Option, Value, SQL_NTS, (SQLINTEGER *)NULL); } /* }}} */ /* {{{ SQLGetTypeInfo */ SQLRETURN SQL_API SQLGetTypeInfo(SQLHSTMT StatementHandle, SQLSMALLINT DataType) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return MADB_GetTypeInfo(Stmt, DataType); } /* }}} */ /* {{{ SQLGetTypeInfoW */ SQLRETURN SQL_API SQLGetTypeInfoW(SQLHSTMT StatementHandle, SQLSMALLINT DataType) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return MADB_GetTypeInfo(Stmt, DataType);} /* }}} */ /* {{{ SQLMoreResults */ SQLRETURN SQL_API SQLMoreResults(SQLHSTMT StatementHandle) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return MADB_StmtMoreResults(Stmt); } /* }}} */ /* {{{ SQLNativeSql */ SQLRETURN SQL_API SQLNativeSql(SQLHDBC ConnectionHandle, SQLCHAR *InStatementText, SQLINTEGER TextLength1, SQLCHAR *OutStatementText, SQLINTEGER BufferLength, SQLINTEGER *TextLength2Ptr) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLINTEGER Length; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); if (!TextLength2Ptr && (!OutStatementText || !BufferLength)) { MADB_SetError(&Dbc->Error, MADB_ERR_01004, NULL, 0); return Dbc->Error.ReturnValue; } Length= (SQLINTEGER)MADB_SetString(0, OutStatementText, BufferLength, (char *)InStatementText, TextLength1, &Dbc->Error); if (TextLength2Ptr) *TextLength2Ptr= Length; return Dbc->Error.ReturnValue; } /* }}} */ /* {{{ SQLNativeSqlW */ SQLRETURN SQL_API SQLNativeSqlW(SQLHDBC ConnectionHandle, SQLWCHAR *InStatementText, SQLINTEGER TextLength1, SQLWCHAR *OutStatementText, SQLINTEGER BufferLength, SQLINTEGER *TextLength2Ptr) { MADB_Dbc *Conn= (MADB_Dbc *)ConnectionHandle; SQLINTEGER Length= (TextLength1 == SQL_NTS) ? SqlwcsCharLen(InStatementText, (SQLLEN)-1) : TextLength1; if (!Conn) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Conn->Error); if (TextLength2Ptr) *TextLength2Ptr= Length; if(OutStatementText && BufferLength < Length) MADB_SetError(&Conn->Error, MADB_ERR_01004, NULL, 0); if(OutStatementText && BufferLength < Length) MADB_SetError(&Conn->Error, MADB_ERR_01004, NULL, 0); Length= MIN(Length, BufferLength - 1); if (OutStatementText && BufferLength) { memcpy(OutStatementText, InStatementText, Length * sizeof(SQLWCHAR)); OutStatementText[Length]= 0; } return Conn->Error.ReturnValue; } /* }}} */ /* {{{ SQLNumParams */ SQLRETURN SQL_API SQLNumParams(SQLHSTMT StatementHandle, SQLSMALLINT *ParameterCountPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; MADB_CHECK_STMT_HANDLE(Stmt, stmt); MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->ParamCount(Stmt, ParameterCountPtr); } /* }}} */ /* {{{ SQLNumResultCols */ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT StatementHandle, SQLSMALLINT *ColumnCountPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; MADB_CHECK_STMT_HANDLE(Stmt, stmt); MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->ColumnCount(Stmt, ColumnCountPtr); } /* }}} */ /* {{{ SQLParamData */ SQLRETURN SQL_API SQLParamData(SQLHSTMT StatementHandle, SQLPOINTER *ValuePtrPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->ParamData(Stmt, ValuePtrPtr); } /* }}} */ SQLRETURN SQL_API SQLPrepare(SQLHSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MDBUG_C_ENTER(Stmt->Connection, "SQLPrepare"); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, StatementText, s); MDBUG_C_DUMP(Stmt->Connection, TextLength, d); /* Prepare method clears error */ return Stmt->Methods->Prepare(Stmt, (char *)StatementText, TextLength, FALSE); } /* }}} */ /* {{{ SQLPrepareW */ SQLRETURN SQL_API SQLPrepareW(SQLHSTMT StatementHandle, SQLWCHAR *StatementText, SQLINTEGER TextLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; char *StmtStr; SQLULEN StmtLength; SQLRETURN ret; BOOL ConversionError; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLPrepareW"); StmtStr= MADB_ConvertFromWChar(StatementText, TextLength, &StmtLength, Stmt->Connection->ConnOrSrcCharset, &ConversionError); MDBUG_C_DUMP(Stmt->Connection, Stmt, 0x); MDBUG_C_DUMP(Stmt->Connection, StmtStr, s); MDBUG_C_DUMP(Stmt->Connection, TextLength, d); if (ConversionError) { MADB_SetError(&Stmt->Error, MADB_ERR_22018, NULL, 0); ret= Stmt->Error.ReturnValue; } else ret= Stmt->Methods->Prepare(Stmt, StmtStr, (SQLINTEGER)StmtLength, FALSE); MADB_FREE(StmtStr); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLPrimaryKeys */ SQLRETURN SQL_API SQLPrimaryKeys(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; MDBUG_C_ENTER(Stmt->Connection, "SQLPrimaryKeys"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); MDBUG_C_DUMP(Stmt->Connection, CatalogName, s); MDBUG_C_DUMP(Stmt->Connection, NameLength1, d); MDBUG_C_DUMP(Stmt->Connection, SchemaName, s); MDBUG_C_DUMP(Stmt->Connection, NameLength2, d); MDBUG_C_DUMP(Stmt->Connection, TableName, s); MDBUG_C_DUMP(Stmt->Connection, NameLength3, d); if (!Stmt) ret= SQL_INVALID_HANDLE; else { MADB_CLEAR_ERROR(&Stmt->Error); ret= Stmt->Methods->PrimaryKeys(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3); } MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLPrimaryKeysW */ SQLRETURN SQL_API SQLPrimaryKeysW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName != NULL) { CpCatalog = MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable = MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } MDBUG_C_ENTER(Stmt->Connection, "SQLPrimaryKeysW"); MDBUG_C_DUMP(Stmt->Connection, StatementHandle, 0x); MDBUG_C_DUMP(Stmt->Connection, CpCatalog, s); MDBUG_C_DUMP(Stmt->Connection, CpLength1, d); MDBUG_C_DUMP(Stmt->Connection, CpSchema, s); MDBUG_C_DUMP(Stmt->Connection, CpLength2, d); MDBUG_C_DUMP(Stmt->Connection, CpTable, s); MDBUG_C_DUMP(Stmt->Connection, CpLength3, d); ret= Stmt->Methods->PrimaryKeys(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLProcedureColumns */ SQLRETURN SQL_API SQLProcedureColumns(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *ProcName, SQLSMALLINT NameLength3, SQLCHAR *ColumnName, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->ProcedureColumns(Stmt, (char *)CatalogName,NameLength1, (char *)SchemaName, NameLength2, (char *)ProcName, NameLength3, (char *)ColumnName, NameLength4); } /* }}} */ /* {{{ SQLProcedureColumnsW */ SQLRETURN SQL_API SQLProcedureColumnsW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *ProcName, SQLSMALLINT NameLength3, SQLWCHAR *ColumnName, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; char *CpCatalog= NULL, *CpSchema= NULL, *CpProc= NULL, *CpColumn= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0, CpLength4= 0; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName != NULL) { CpCatalog= MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema= MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (ProcName != NULL) { CpProc= MADB_ConvertFromWChar(ProcName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (ColumnName != NULL) { CpColumn= MADB_ConvertFromWChar(ColumnName, NameLength4, &CpLength4, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->ProcedureColumns(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpProc, (SQLSMALLINT)CpLength3, CpColumn, (SQLSMALLINT)CpLength4); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpProc); MADB_FREE(CpColumn); return ret; } /* }}} */ /* {{{ SQLProcedures */ SQLRETURN SQL_API SQLProcedures(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *ProcName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->Procedures(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)ProcName, NameLength3); } /* }}} */ /* {{{ SQLProceduresW */ SQLRETURN SQL_API SQLProceduresW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *ProcName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; char *CpCatalog= NULL, *CpSchema= NULL, *CpProc= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName != NULL) { CpCatalog = MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (ProcName != NULL) { CpProc = MADB_ConvertFromWChar(ProcName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->Procedures(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpProc, (SQLSMALLINT)CpLength3); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpProc); return ret; } /* }}} */ /* {{{ SQLPutData */ SQLRETURN SQL_API SQLPutData(SQLHSTMT StatementHandle, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind) { MADB_Stmt *Stmt=(MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLPutData"); MDBUG_C_DUMP(Stmt->Connection, DataPtr, 0x); MDBUG_C_DUMP(Stmt->Connection, StrLen_or_Ind, d); ret= Stmt->Methods->PutData(Stmt, DataPtr, StrLen_or_Ind); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLRowCount */ SQLRETURN SQL_API SQLRowCount(SQLHSTMT StatementHandle, SQLLEN *RowCountPtr) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; MADB_CHECK_STMT_HANDLE(Stmt, stmt); MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->RowCount(Stmt, RowCountPtr); } /* }}} */ /* {{{ SQLSetConnectAttr */ SQLRETURN SQL_API SQLSetConnectAttr(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { if (ConnectionHandle == SQL_NULL_HDBC) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Dbc *)ConnectionHandle)->Error); return MA_SQLSetConnectAttr(ConnectionHandle, Attribute, ValuePtr, StringLength); } /* {{{ SQLSetConnectAttrW */ SQLRETURN SQL_API SQLSetConnectAttrW(SQLHDBC ConnectionHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { MADB_Dbc *Dbc= (MADB_Dbc *)ConnectionHandle; SQLRETURN ret; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SetConnectAttrW"); MDBUG_C_DUMP(Dbc, Dbc, 0x); MDBUG_C_DUMP(Dbc, Attribute, d); MDBUG_C_DUMP(Dbc, ValuePtr, 0x); MDBUG_C_DUMP(Dbc, StringLength, d); ret= Dbc->Methods->SetAttr(Dbc, Attribute, ValuePtr, StringLength, TRUE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLSetConnectOption */ SQLRETURN SQL_API SQLSetConnectOption(SQLHDBC Hdbc, SQLUSMALLINT Option, SQLULEN Param) { SQLINTEGER StringLength= 0; SQLRETURN ret; if (!Hdbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Dbc*)Hdbc)->Error); /* todo: do we have more string options ? */ if (Option == SQL_ATTR_CURRENT_CATALOG) StringLength= SQL_NTS; ret= MA_SQLSetConnectAttr(Hdbc, Option, (SQLPOINTER)Param, StringLength); return ret; } /* }}} */ /* {{{ SQLSetConnectOptionW */ SQLRETURN SQL_API SQLSetConnectOptionW(SQLHDBC Hdbc, SQLUSMALLINT Option, SQLULEN Param) { SQLINTEGER StringLength= 0; SQLRETURN ret; MADB_Dbc *Dbc= (MADB_Dbc *)Hdbc; if (!Dbc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Dbc->Error); MDBUG_C_ENTER(Dbc, "SetSetConnectOptionW"); MDBUG_C_DUMP(Dbc, Option, d); MDBUG_C_DUMP(Dbc, Param, u); /* todo: do we have more string options ? */ if (Option == SQL_ATTR_CURRENT_CATALOG) StringLength= SQL_NTS; ret= Dbc->Methods->SetAttr(Dbc, Option, (SQLPOINTER)Param, StringLength, TRUE); MDBUG_C_RETURN(Dbc, ret, &Dbc->Error); } /* }}} */ /* {{{ SQLSetCursorName */ SQLRETURN SQL_API SQLSetCursorName(SQLHSTMT StatementHandle, SQLCHAR *CursorName, SQLSMALLINT NameLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->SetCursorName(Stmt, (char *)CursorName, NameLength); } /* }}} */ /* {{{ SQLSetCursorNameW */ SQLRETURN SQL_API SQLSetCursorNameW(SQLHSTMT StatementHandle, SQLWCHAR *CursorName, SQLSMALLINT NameLength) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; char *CpName= NULL; SQLULEN Length; SQLRETURN rc; if (!Stmt) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&Stmt->Error); CpName= MADB_ConvertFromWChar(CursorName, NameLength, &Length, Stmt->Connection->ConnOrSrcCharset, NULL); rc= Stmt->Methods->SetCursorName(Stmt, (char *)CpName, (SQLINTEGER)Length); MADB_FREE(CpName); return rc; } /* }}} */ /* {{{ SQLSetDescField */ SQLRETURN SQL_API SQLSetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; if (!Desc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Desc->Error); return MADB_DescSetField(DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, FALSE); } /* }}} */ /* {{{ SQLSetDescFieldW */ SQLRETURN SQL_API SQLSetDescFieldW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; if (!Desc) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Desc->Error); return MADB_DescSetField(DescriptorHandle, RecNumber, FieldIdentifier, ValuePtr, BufferLength, TRUE); } /* }}} */ /* {{{ SQLSetDescRec */ SQLRETURN SQL_API SQLSetDescRec(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT Type, SQLSMALLINT SubType, SQLLEN Length, SQLSMALLINT Precision, SQLSMALLINT Scale, SQLPOINTER DataPtr, SQLLEN *StringLengthPtr, SQLLEN *IndicatorPtr) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_NOT_IMPLEMENTED(Desc); } /* }}} */ /* {{{ SQLSetDescRecW */ SQLRETURN SQL_API SQLSetDescRecW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT Type, SQLSMALLINT SubType, SQLLEN Length, SQLSMALLINT Precision, SQLSMALLINT Scale, SQLPOINTER DataPtr, SQLLEN *StringLengthPtr, SQLLEN *IndicatorPtr) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_NOT_IMPLEMENTED(Desc); } /* }}} */ /* {{{ SQLSetEnvAttr */ SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { MADB_Env *Env= (MADB_Env *)EnvironmentHandle; SQLRETURN ret; MDBUG_ENTER("SQLSetEnvAttr"); MDBUG_DUMP(Attribute, d); MDBUG_DUMP(ValuePtr, 0x); if (!Env) ret= SQL_INVALID_HANDLE; else { MADB_CLEAR_ERROR(&Env->Error); ret= MADB_EnvSetAttr(Env, Attribute, ValuePtr, StringLength); } MDBUG_DUMP(ret, d); MDBUG_RETURN(ret); } /* }}} */ /* {{{ SQLSetPos */ SQLRETURN SQL_API SQLSetPos(SQLHSTMT StatementHandle, SQLSETPOSIROW RowNumber, SQLUSMALLINT Operation, SQLUSMALLINT LockType) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); MDBUG_C_ENTER(Stmt->Connection, "SQLSetPos"); MDBUG_C_DUMP(Stmt->Connection, RowNumber, d); MDBUG_C_DUMP(Stmt->Connection, Operation, u); MDBUG_C_DUMP(Stmt->Connection, LockType, d); ret= Stmt->Methods->SetPos(Stmt, RowNumber, Operation, LockType, 0); MDBUG_C_RETURN(Stmt->Connection, ret, &Stmt->Error); } /* }}} */ /* {{{ SQLSetParam */ SQLRETURN SQL_API SQLSetParam(SQLHSTMT stmt, SQLUSMALLINT par, SQLSMALLINT type, SQLSMALLINT sqltype, SQLULEN coldef, SQLSMALLINT scale, SQLPOINTER val, SQLLEN *nval) { if (!stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Stmt*)stmt)->Error); return MA_SQLBindParameter(stmt, par, SQL_PARAM_INPUT_OUTPUT, type, sqltype, coldef, scale, val, SQL_SETPARAM_VALUE_MAX, nval); } /* }}} */ /* {{{ SQLBindParam - we need it for direct linking mainly */ SQLRETURN SQL_API SQLBindParam(SQLHSTMT StatementHandle, SQLUSMALLINT ParameterNumber, SQLSMALLINT ValueType, SQLSMALLINT ParameterType, SQLULEN LengthPrecision, SQLSMALLINT ParameterScale, SQLPOINTER ParameterValue, SQLLEN *StrLen_or_Ind) { if (!StatementHandle) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLBindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, SQL_SETPARAM_VALUE_MAX, StrLen_or_Ind); } /* }}} */ /* {{{ SQLSetStmtAttr */ SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLSetStmtAttr(StatementHandle, Attribute, ValuePtr, StringLength); } /* }}} */ /* {{{ SQLSetStmtAttrW */ SQLRETURN SQL_API SQLSetStmtAttrW(SQLHSTMT StatementHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength) { if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLSetStmtAttr(StatementHandle, Attribute, ValuePtr, StringLength); } /* }}} */ /* {{{ SQLSetStmtOption */ SQLRETURN SQL_API SQLSetStmtOption(SQLHSTMT StatementHandle, SQLUSMALLINT Option, SQLULEN Value) { if (StatementHandle == SQL_NULL_HSTMT) { return SQL_INVALID_HANDLE; } MADB_CLEAR_ERROR(&((MADB_Stmt*)StatementHandle)->Error); return MA_SQLSetStmtAttr(StatementHandle, Option, (SQLPOINTER)Value, SQL_NTS); } /* }}} */ /* {{{ SQLSpecialColumns */ SQLRETURN SQL_API SQLSpecialColumns(SQLHSTMT StatementHandle, SQLUSMALLINT IdentifierType, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Scope, SQLUSMALLINT Nullable) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->SpecialColumns(Stmt,IdentifierType, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3, Scope, Nullable); } /* }}} */ /* {{{ SQLSpecialColumnsW */ SQLRETURN SQL_API SQLSpecialColumnsW(SQLHSTMT StatementHandle, SQLUSMALLINT IdentifierType, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Scope, SQLUSMALLINT Nullable) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName!=NULL) { CpCatalog = MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable = MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->SpecialColumns(Stmt,IdentifierType, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3, Scope, Nullable); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); return ret; } /* }}} */ /* {{{ SQLStatistics */ SQLRETURN SQL_API SQLStatistics(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Unique, SQLUSMALLINT Reserved) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->Statistics(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3, Unique, Reserved); } /* }}} */ /* {{{ SQLStatisticsW */ SQLRETURN SQL_API SQLStatisticsW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3, SQLUSMALLINT Unique, SQLUSMALLINT Reserved) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName != NULL) { CpCatalog = MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable = MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (!Stmt) return SQL_INVALID_HANDLE; ret= Stmt->Methods->Statistics(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3, Unique, Reserved); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); return ret; } /* }}} */ /* {{{ SQLTablePrivileges */ SQLRETURN SQL_API SQLTablePrivileges(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->TablePrivileges(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName, NameLength3); } /* }}} */ /* {{{ SQLTablePrivilegesW */ SQLRETURN SQL_API SQLTablePrivilegesW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; SQLRETURN ret; char *CpCatalog= NULL, *CpTable= NULL, *CpSchema= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName != NULL) { CpCatalog= MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName != NULL) { CpSchema = MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName != NULL) { CpTable= MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->TablePrivileges(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3); MADB_FREE(CpCatalog); MADB_FREE(CpTable); return ret; } /* }}} */ /* {{{ SQLTables */ SQLRETURN SQL_API SQLTables(SQLHSTMT StatementHandle, SQLCHAR *CatalogName, SQLSMALLINT NameLength1, SQLCHAR *SchemaName, SQLSMALLINT NameLength2, SQLCHAR *TableName, SQLSMALLINT NameLength3, SQLCHAR *TableType, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return Stmt->Methods->Tables(Stmt, (char *)CatalogName, NameLength1, (char *)SchemaName, NameLength2, (char *)TableName,NameLength3, (char *)TableType, NameLength4); } /* }}} */ /* {{{ SQLTablesW */ SQLRETURN SQL_API SQLTablesW(SQLHSTMT StatementHandle, SQLWCHAR *CatalogName, SQLSMALLINT NameLength1, SQLWCHAR *SchemaName, SQLSMALLINT NameLength2, SQLWCHAR *TableName, SQLSMALLINT NameLength3, SQLWCHAR *TableType, SQLSMALLINT NameLength4) { MADB_Stmt *Stmt= (MADB_Stmt *)StatementHandle; char *CpCatalog= NULL, *CpSchema= NULL, *CpTable= NULL, *CpType= NULL; SQLULEN CpLength1= 0, CpLength2= 0, CpLength3= 0, CpLength4= 0; SQLRETURN ret; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); if (CatalogName) { CpCatalog= MADB_ConvertFromWChar(CatalogName, NameLength1, &CpLength1, Stmt->Connection->ConnOrSrcCharset, NULL); } if (SchemaName) { CpSchema= MADB_ConvertFromWChar(SchemaName, NameLength2, &CpLength2, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableName) { CpTable= MADB_ConvertFromWChar(TableName, NameLength3, &CpLength3, Stmt->Connection->ConnOrSrcCharset, NULL); } if (TableType) { CpType= MADB_ConvertFromWChar(TableType, NameLength4, &CpLength4, Stmt->Connection->ConnOrSrcCharset, NULL); } ret= Stmt->Methods->Tables(Stmt, CpCatalog, (SQLSMALLINT)CpLength1, CpSchema, (SQLSMALLINT)CpLength2, CpTable, (SQLSMALLINT)CpLength3, CpType, (SQLSMALLINT)CpLength4); MADB_FREE(CpCatalog); MADB_FREE(CpSchema); MADB_FREE(CpTable); MADB_FREE(CpType); return ret; } /* }}} */ /* {{{ SQLSetScrollOptions */ SQLRETURN SQL_API SQLSetScrollOptions(SQLHSTMT hstmt, SQLUSMALLINT Concurrency, SQLLEN crowKeySet, SQLUSMALLINT crowRowSet) { MADB_Stmt *Stmt= (MADB_Stmt *)hstmt; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); return MADB_DescSetField(Stmt->Ard, 0, SQL_DESC_ARRAY_SIZE, (SQLPOINTER)(SQLULEN)crowKeySet, SQL_IS_USMALLINT, 0); } /* }}} */ /* {{{ SQLParamOptions */ SQLRETURN SQL_API SQLParamOptions( SQLHSTMT hstmt, SQLULEN crow, SQLULEN *pirow) { MADB_Stmt *Stmt= (MADB_Stmt *)hstmt; SQLRETURN result; if (!Stmt) return SQL_INVALID_HANDLE; MADB_CLEAR_ERROR(&Stmt->Error); result= MADB_DescSetField(Stmt->Apd, 0, SQL_DESC_ARRAY_SIZE, (SQLPOINTER)crow, SQL_IS_UINTEGER, 0); if (SQL_SUCCEEDED(result)) { result= MADB_DescSetField(Stmt->Ipd, 0, SQL_DESC_ROWS_PROCESSED_PTR, (SQLPOINTER)pirow, SQL_IS_POINTER, 0); } return result; } /* }}} */ mariadb-connector-odbc-3.1.15/osxinstall/000077500000000000000000000000001414431724000202755ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/osxinstall/CMakeLists.txt000066400000000000000000000124301414431724000230350ustar00rootroot00000000000000# ************************************************************************************ # Copyright (C) 2019,2020 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # *************************************************************************************/ SET(ODBC_SOURCE_PKG_DIR ${CMAKE_SOURCE_DIR}/osxinstall) # Get revision number IF(WITH_REVNO) EXECUTE_PROCESS(COMMAND git log HEAD^^..HEAD COMMAND FINDSTR commit OUTPUT_VARIABLE revno) STRING(REPLACE "commit " "" revno ${revno}) STRING(REPLACE "\n" "" revno ${revno}) ENDIF() IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(PLATFORM "x86_64") SET(PRODUCT_ARCH_BITS "64") ELSE() SET(PLATFORM "32bit") SET(PRODUCT_ARCH_BITS "32") endif() SET(PRODUCT_NAME "MariaDB ODBC Driver") SET(PRODUCT_MANUFACTURER "MariaDB") SET(PRODUCT_VERSION "${MARIADB_ODBC_VERSION_MAJOR}.${MARIADB_ODBC_VERSION_MINOR}.${MARIADB_ODBC_VERSION_PATCH}") SET(PRODUCT_SERIES "${MARIADB_ODBC_VERSION_MAJOR}.${MARIADB_ODBC_VERSION_MINOR}") SET(PRODUCT_IDENTIFIER "com.mariadb.connector.odbc") IF(${revno}) SET(PKG_PACKAGE "mariadb-connector-odbc-${PRODUCT_VERSION}-r${revno}-osx-${CMAKE_SYSTEM_PROCESSOR}.pkg") ELSE() IF(PACKAGE_PLATFORM_SUFFIX) SET(PKG_PACKAGE "mariadb-connector-odbc-${PRODUCT_VERSION}-osx-${CMAKE_SYSTEM_PROCESSOR}-${PACKAGE_PLATFORM_SUFFIX}.pkg") ELSE() SET(PKG_PACKAGE "mariadb-connector-odbc-${PRODUCT_VERSION}-osx-${CMAKE_SYSTEM_PROCESSOR}.pkg") ENDIF() ENDIF() MESSAGE(STATUS "PKG package file name ${PKG_PACKAGE}") IF(WITH_SIGNCODE) SET(SIGN_WITH_DEVID "--sign \"Developer ID Installer: ${DEVELOPER_ID}\"") ELSE() SET(SIGN_WITH_DEVID "") ENDIF() CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/WELCOME.html.in ${CMAKE_CURRENT_BINARY_DIR}/WELCOME.html @ONLY) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/scripts/postinstall.in ${CMAKE_CURRENT_BINARY_DIR}/scripts/postinstall @ONLY) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/distribution.plist.in ${CMAKE_CURRENT_BINARY_DIR}/distribution.plist @ONLY) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build_package.sh.in ${CMAKE_CURRENT_BINARY_DIR}/build_package.sh @ONLY) ADD_EXECUTABLE(install_driver install_driver.c) TARGET_LINK_LIBRARIES(install_driver ${ODBC_INSTLIBS})#${PLATFORM_DEPENDENCIES}) # On Windows should look like #TARGET_LINK_LIBRARIES(install_driver ${ODBC_INSTLIBS} legacy_stdio_definitions Shlwapi) IF(APPLE) SET_TARGET_PROPERTIES(install_driver PROPERTIES XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "--timestamp -f" ) IF(WITH_SIGNCODE) SET_TARGET_PROPERTIES(install_driver PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Developer ID Application: ${DEVELOPER_ID}" XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS "NO") ELSE() SET_TARGET_PROPERTIES(install_driver PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") ENDIF() ENDIF() IF(USE_SYSTEM_INSTALLED_LIB) ADD_CUSTOM_TARGET(copypkgfiles COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/copy_package_files.sh $ $ DEPENDS maodbc install_driver WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ELSE() SET(GNUTLS_LIB "") IF("${WITH_SSL}" STREQUAL "GNUTLS") SET(GNUTLS_LIB "${GNUTLS_LIBRARY}") MESSAGE(STATUS "Configuring to include gnutls library(${GNUTLS_LIB}) into the package") ENDIF() ADD_CUSTOM_TARGET(copypkgfiles COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/copy_package_files.sh $ $ $ ${GNUTLS_LIB} DEPENDS maodbc install_driver WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) ENDIF() ADD_CUSTOM_TARGET(maodbcpkg COMMAND ${CMAKE_CURRENT_BINARY_DIR}/build_package.sh ${PRODUCT_IDENTIFIER} ${PRODUCT_VERSION} ${PKG_PACKAGE} DEPENDS copypkgfiles ${CMAKE_CURRENT_BINARY_DIR}/scripts/postinstall ${CMAKE_CURRENT_BINARY_DIR}/distribution.plist README.html ${CMAKE_CURRENT_BINARY_DIR}/WELCOME.html LICENSE.html WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) SET_TARGET_PROPERTIES(maodbcpkg PROPERTIES EXCLUDE_FROM_ALL OFF) ADD_DEPENDENCIES(maodbcpkg copypkgfiles) ADD_DEPENDENCIES(copypkgfiles maodbc) IF(NOT USE_SYSTEM_INSTALLED_LIB) ADD_DEPENDENCIES(copypkgfiles maodbc dialog caching_sha2_password auth_gssapi_client sha256_password mysql_clear_password client_ed25519) ENDIF() mariadb-connector-odbc-3.1.15/osxinstall/LICENSE.html000066400000000000000000000634461414431724000222620ustar00rootroot00000000000000 MariaDB Connector/ODBC license
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]

Preamble

  The licenses for most software are designed to take away your freedom to share and change it.  By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it.  You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use, not price.  Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.

  To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights.  These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.  
  For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you.  You must make sure that they, too, receive or can get the source code.  If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.  To protect each distributor, we want to make it very clear that there is no warranty for the free library.  Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.

  Finally, software patents pose a constant threat to the existence of any free program.  We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder.  Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the ordinary GNU General Public License.  This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License.  We use this license for certain libraries in order to permit linking those libraries into non-free programs.

  When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library.  The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom.  The Lesser General Public License permits more lax criteria for linking other code with the library.

  We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License.  It also provides other free software developers Less of an advantage over competing non-free programs.  These disadvantages are the reason we use the ordinary General Public License for many libraries.  However, the Lesser license provides advantages in certain special circumstances.

  For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard.  To achieve this, non-free programs must be allowed to use the library.  A more frequent case is that a free library does the same job as widely used non-free libraries.  In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software.  For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.

  Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and modification follow.  Pay close attention to the difference between a "work based on the library" and a "work that uses the library".  The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
 

GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License").  Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work which has been distributed under these terms.  A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language.  (Hereinafter, translation is included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for making modifications to it.  For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.

  Activities other than copying, distribution and modification are not covered by this License; they are outside its scope.  The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it).  Whether that is true depends on what the Library does and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.

  You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
 

  2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.

    (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application.  Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works.  But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.

In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library.  To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License.  (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library".  Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library".  The executable is therefore covered by this License.  Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not.  Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library.  The threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work.  (Executables containing this object code plus portions of the Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6.  Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License.  You must supply a copy of this License.  If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License.  Also, you must do one of these things:

    a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library.  (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the Library.  A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.

    e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it.  However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

  It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system.  Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.

  7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities.  This must be distributed under the terms of the Sections above.

    b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License.  Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License.  However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not signed it.  However, nothing else grants you permission to modify or distribute the Library or its derivative works.  These actions are prohibited by law if you do not accept this License.  Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions.  You may not impose any further restrictions on the recipients' exercise of the rights granted herein.  You are not responsible for enforcing compliance by third parties with this License.

  11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License.  If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all.  For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices.  Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded.  In such case, this License incorporates the limitation as if written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time.  Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation.  If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission.  For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this.  Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change.  You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

     Copyright (C)   

    This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  , 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!


mariadb-connector-odbc-3.1.15/osxinstall/README.html000066400000000000000000000017421414431724000221240ustar00rootroot00000000000000 Readme for MariaDB Connector/ODBC

MariaDB Connector/ODBC files will be installed in /Library/MariaDB/MariaDB-Connector-ODBC.

Warning:The driver library requires latest versions of openssl libraries, that are not available natively, and can be obtained with Homebrew

Homebrew installation instructions can be found here. The short version at the moment is to run the following command in the terminal:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

The command to install openssl is:

brew install openssl@1.1

  • brew install openssl
mariadb-connector-odbc-3.1.15/osxinstall/WELCOME.html.in000066400000000000000000000020771414431724000226710ustar00rootroot00000000000000 Welcome to MariaDB Connector/ODBC Installer

This is going to install MariaDB Connector/ODBC(Unicode) @PRODUCT_ARCH_BITS@bit version @PRODUCT_VERSION@ - a database driver that uses the industry standard Open Database Connectivity (ODBC) API.

Warning: The driver library requires the latest version of the openssl libraries.These are not natively available in macOS, but can be obtained using Homebrew

Homebrew installation instructions can be found here. The short version at the moment is to run the following command in the terminal:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

The command to install openssl is:

brew install openssl@1.1

mariadb-connector-odbc-3.1.15/osxinstall/build_package.sh.in000077500000000000000000000023431414431724000240150ustar00rootroot00000000000000#!/bin/bash # ************************************************************************************ # Copyright (C) 2019 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # *************************************************************************************/ pkgbuild --root ROOT --scripts scripts --identifier $1 --version $2 libmaodbc.pkg productbuild --distribution @CMAKE_CURRENT_BINARY_DIR@/distribution.plist --resources . --package-path libmaodbc.pkg @SIGN_WITH_DEVID@ $3 mariadb-connector-odbc-3.1.15/osxinstall/copy_package_files.sh000077500000000000000000000033651414431724000244520ustar00rootroot00000000000000#!/bin/bash # ************************************************************************************ # Copyright (C) 2019 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # *************************************************************************************/ LibPath="Library/MariaDB/MariaDB-Connector-ODBC" set -e rm -rf ./ROOT mkdir -p ./ROOT/${LibPath}/bin cp $1/libmaodbc.dylib ./ROOT/${LibPath}/ cp $2//install_driver ./ROOT/${LibPath}/bin if [ $3 ]; then mkdir ./ROOT/${LibPath}/plugin cp $3/dialog.so ./ROOT/${LibPath}/plugin/ cp $3/auth_gssapi_client.so ./ROOT/${LibPath}/plugin/ cp $3/caching_sha2_password.so ./ROOT/${LibPath}/plugin/ cp $3/mysql_clear_password.so ./ROOT/${LibPath}/plugin/ cp $3/client_ed25519.so ./ROOT/${LibPath}/plugin/ cp $3/sha256_password.so ./ROOT/${LibPath}/plugin/ fi if [ $4 ]; then if [ -L $4 ]; then LibRealName=`readlink $4` cp $4 ./ROOT/${LibPath}/${LibRealName} cp -a $4 ./ROOT/${LibPath}/ else cp $4 ./ROOT/${LibPath}/ fi fi mariadb-connector-odbc-3.1.15/osxinstall/distribution.plist.in000066400000000000000000000014101414431724000244720ustar00rootroot00000000000000 MariaDB Connector/ODBC libmaodbc.pkg mariadb-connector-odbc-3.1.15/osxinstall/install_driver.c000066400000000000000000000066471414431724000234770ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #define SQL_NOUNICODEMAP #include #include #include #ifdef _WIN32 # include # include #else int _snprintf(char *buffer, size_t count, const char *format, ...) { va_list list; va_start(list, format); int result= vsnprintf(buffer, count, format, list); va_end(list); /* _snprintf returns negative number if buffer is not big enough */ if (result > count) { return count - result - 1; } return result; } #endif #include int Usage() { printf("Usage: install_driver []\n"); return 1; } int main(int argc, char** argv) { unsigned int UsageCount, ErrorCode; char DriverDescr[1024], ErrorText[128], OutLocation[512], DriverDir[512]; const char *DriverLocation, *DriverName, *DriverFileName, *DriverDescription= "MariaDB Connector/ODBC"; if (argc < 2 || strcmp(argv[1], "--help") == 0) { return Usage(); } DriverLocation= argv[1]; #ifdef _WIN32 strcpy(DriverDir, DriverLocation); PathRemoveFileSpec(DriverDir); #else { char *SlashLocation= strrchr(DriverLocation, '/'); if (SlashLocation != NULL) { strncpy(DriverDir, DriverLocation, SlashLocation - DriverLocation); DriverDir[SlashLocation - DriverLocation]= '\0'; } else { strcpy(DriverDir, "."); } } #endif if (argc > 2) { DriverName= argv[2]; } else { DriverName= DriverLocation; } if (argc > 3) { DriverDescription= argv[3]; } if (strlen(DriverLocation) > strlen(DriverDir)) { DriverFileName= DriverLocation + strlen(DriverDir) + 1; } else { DriverFileName= DriverName; } /* Our setup library does not have ConfigDriver. Thus there is no sense */ /*SQLConfigDriver(NULL, ODBC_REMOVE_DRIVER, DriverName, NULL, NULL, 0, NULL);*/ SQLRemoveDriver(DriverName, FALSE, &UsageCount); printf("Installing driver %s in %s as %s\n", DriverFileName, DriverDir, DriverName); _snprintf(DriverDescr, sizeof(DriverDescr), "%s%cDriver=%s%cDescription=%s%cThreading=0%c", DriverName, '\0', DriverLocation, '\0', DriverDescription, '\0', '\0'); if (SQLInstallDriverEx(DriverDescr, DriverDir, OutLocation, sizeof(OutLocation), NULL, ODBC_INSTALL_COMPLETE, NULL) == FALSE) { SQLInstallerError(1, &ErrorCode, ErrorText, (unsigned short)sizeof(ErrorText), NULL); printf("An error occured while registering driver: [%u] %s\n", ErrorCode, ErrorText); return 1; } return 0; } mariadb-connector-odbc-3.1.15/osxinstall/scripts/000077500000000000000000000000001414431724000217645ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/osxinstall/scripts/postinstall.in000077500000000000000000000066241414431724000247030ustar00rootroot00000000000000#!/bin/bash # ************************************************************************************ # Copyright (C) 2019 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # *************************************************************************************/ touch /tmp/maodbc_postinstall.log echo "Registering MariaDB ODBC driver" > /tmp/maodbc_postinstall.log if [ $2 ]; then target=$2 case "$target" in */) ;; *) target="${target}/" ;; esac echo "Driver installation target - $target" >> /tmp/maodbc_postinstall.log fi set -a ODBCINSTINI="${target}Library/ODBC/odbcinst.ini" instinidir="$(dirname -- "$ODBCINSTINI")" echo "Using $ODBCINSTINI, dirname - ${instinidir}" >> /tmp/maodbc_postinstall.log if [ ! -f $ODBCINSTINI ] ; then echo "$ODBCINSTINI does not exist" >> /tmp/maodbc_postinstall.log mkdir -p "$instinidir" && touch "$ODBCINSTINI" if [ ! -f $ODBCINSTINI ] ; then echo "$ODBCINSTINI still does not exist" >> /tmp/maodbc_postinstall.log echo "$ODBCINSTINI still does not exist" # mkdir -p "$instinidir" # touch "$ODBCINSTINI" exit 1 fi chmod 664 $ODBCINSTINI if [ ! -f ${target}Library/ODBC/odbc.ini ] ; then echo "${target}Library/ODBC/odbc.ini does not exist - creating it" >> /tmp/maodbc_postinstall.log touch "${target}Library/ODBC/odbc.ini" chgrp admin ${target}Library/ODBC/odbc.ini chmod 664 ${target}Library/ODBC/odbc.ini echo "[ODBC Data Sources]" > ${target}Library/ODBC/odbc.ini fi echo "Writing common options and driver entry" >> /tmp/maodbc_postinstall.log echo "[ODBC]" > $ODBCINSTINI echo "Trace = no" >> $ODBCINSTINI echo "TraceFile = /tmp/iodbc_trace.log" >> $ODBCINSTINI echo "" >> $ODBCINSTINI echo "[ODBC Drivers]" >> $ODBCINSTINI echo "MariaDB ODBC @PRODUCT_SERIES@ Unicode Driver = Installed" >> $ODBCINSTINI echo "" >> $ODBCINSTINI echo "[MariaDB ODBC @PRODUCT_SERIES@ Unicode Driver]" >> $ODBCINSTINI echo "Driver = ${target}Library/MariaDB/MariaDB-Connector-ODBC/libmaodbc.dylib" >> $ODBCINSTINI echo "Description = MariaDB Connector/ODBC(Unicode) @PRODUCT_VERSION@ @PRODUCT_ARCH_BITS@bit" >> $ODBCINSTINI echo "Threading = 0" >> $ODBCINSTINI echo "" >> $ODBCINSTINI echo "Registration has been successfully finished" >> /tmp/maodbc_postinstall.log exit 0 fi ${target}Library/MariaDB/MariaDB-Connector-ODBC/bin/install_driver "${target}Library/MariaDB/MariaDB-Connector-ODBC/libmaodbc.dylib" "MariaDB ODBC @PRODUCT_SERIES@ Unicode Driver" "MariaDB Connector/ODBC(Unicode) @PRODUCT_VERSION@ @PRODUCT_ARCH_BITS@bit" >> /tmp/maodbc_postinstall.log echo "Registration has been successfully finished" >> /tmp/maodbc_postinstall.log exit 0 mariadb-connector-odbc-3.1.15/osxpostbuild.sh000077500000000000000000000031011414431724000211660ustar00rootroot00000000000000#!/bin/bash # ************************************************************************************ # Copyright (C) 2020 MariaDB Corporation AB # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # *************************************************************************************/ set -x echo $1 LinkedLibName=`otool -L $1 | grep -i iodbcinst | sed 's/ //' | sed 's/ [(].*$//'` install_name_tool -change $LinkedLibName @rpath/libiodbcinst.dylib $1 LinkedLibName=`otool -L $1 | grep -i libgnutls | sed 's/ //' | sed 's/ [(].*$//'` if [ $LinkedLibName ] ; then LinkedLibFileName=`echo $LinkedLibName | sed 's/.*[/]//'` install_name_tool -change $LinkedLibName @rpath/$LinkedLibFileName $1 fi #LinkedLibName=`otool -L $1 | grep -i libcrypto | sed 's/ //' | sed 's/ [(].*$//'` #install_name_tool -change $LinkedLibName @rpath/libcrypto.1.1.dylib $1 mariadb-connector-odbc-3.1.15/stringinfo.h000066400000000000000000000006061414431724000204320ustar00rootroot00000000000000//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by maodbca.rc // Nchste Standardwerte fr neue Objekte // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif mariadb-connector-odbc-3.1.15/wininstall/000077500000000000000000000000001414431724000202615ustar00rootroot00000000000000mariadb-connector-odbc-3.1.15/wininstall/CMakeLists.txt000066400000000000000000000113521414431724000230230ustar00rootroot00000000000000SET(ODBC_SOURCE_WIX_DIR ${CMAKE_SOURCE_DIR}/wininstall) # Get revision number IF(WITH_REVNO) EXECUTE_PROCESS(COMMAND git log HEAD^^..HEAD COMMAND FINDSTR commit OUTPUT_VARIABLE revno) STRING(REPLACE "commit " "" revno ${revno}) STRING(REPLACE "\n" "" revno ${revno}) ENDIF() SET(WIXNOTFOUND_ERROR ON) IF(NOT WIX_DIR) SET(WIX_DIR "$ENV{WIX}/bin") MESSAGE(STATUS "WiX directory: ${WIX_DIR}") # Return error only if WIX_DIR was specified by user, but wix binaries are not found there SET(WIXNOTFOUND_ERROR OFF) ENDIF() ADD_EXECUTABLE(change_dsns_driver change_dsns_driver.c ${CMAKE_SOURCE_DIR}/ma_dsn.c ${CMAKE_SOURCE_DIR}/ma_platform_win32.c ${CMAKE_SOURCE_DIR}/ma_common.c) TARGET_LINK_LIBRARIES(change_dsns_driver ${ODBC_LIBS} ${ODBC_INSTLIBS} legacy_stdio_definitions Shlwapi Pathcch) FIND_PROGRAM(WIXBIN "${WIX_DIR}/candle.exe") IF(NOT WIXBIN) IF(WIXNOTFOUND_ERROR) MESSAGE(FATAL_ERROR "WiX binaries have not been found in given location. Don't define WIX_DIR, if you don't need msi package generation") ELSE() MESSAGE(STATUS "WiX binaries are not found, skipping generating of msi building projects") RETURN() ENDIF() ELSE() MESSAGE(STATUS "WiX binaries are found in given location ${WIXBIN}") ENDIF() SET(PRODUCT_NAME "MariaDB ODBC Driver") SET(PRODUCT_MANUFACTURER "MariaDB") SET(PRODUCT_VERSION "${MARIADB_ODBC_VERSION_MAJOR}.${MARIADB_ODBC_VERSION_MINOR}.${MARIADB_ODBC_VERSION_PATCH}") SET(TLS_LIB_BEGIN "!-- ") SET(TLS_LIB_END " --") IF(ALL_PLUGINS_STATIC) SET(PLUGINS_BEGIN "!-- ") SET(PLUGINS_END " --") ELSE() SET(PLUGINS_BEGIN "") SET(PLUGINS_END "") ENDIF() IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(PRODUCT_NAME "${PRODUCT_NAME} 64-bit") SET(PLATFORM "win64") SET(IS_WIN64 "yes") SET(IS64 "64") SET(WIXPLATFORM "x64") SET(FOLDER "ProgramFiles64Folder") SET(GUID_REGISTRY "E35BF41F-89A1-4691-8F62-09922C04C13B") SET(GUID_SETUP "BF255F46-18CA-4244-9A17-6B33BDAAFBF4") SET(GUID_DRIVER "3535FD95-1F44-454E-A6E4-81F865E8C57F") SET(GUID_DEBUG "4FA6E79A-4630-4CB8-A4E1-00A4740E9280") SET(GUID_PLUGINS "9D1B41AA-CE86-4c6d-93C8-FDCD40D5D2E9") SET(GUID_PLUGINS_DEBUG "63541EC9-9C2B-4763-8C18-03313DAE6F8E") SET(GUID_INSTALLER_TOOLS "9FF07852-2A99-4699-A6E8-889B9745C5B9") IF ("${WITH_SSL}" STREQUAL "GNUTLS" AND NOT "${GNUTLS_LIBRARY}" STREQUAL "") SET(TLS_LIB_BEGIN "") SET(TLS_LIB_END "") SET(GUID_TLS_LIB "C8D5976A-4F30-411f-88E5-D77AFF09E444") GET_FILENAME_COMPONENT(LIB_GNUTLS_LOCATION "${GNUTLS_LIBRARY}" DIRECTORY) MESSAGE(STATUS "Configuring to package gnutls library from ${LIB_GNUTLS_LOCATION}") ENDIF() ELSE() SET(PLATFORM "win32") SET(IS_WIN64 "no") SET(IS64 "") SET(WIXPLATFORM "x86") SET(FOLDER "ProgramFilesFolder") SET(GUID_REGISTRY "ACFC9B33-5D1F-4EA2-A4DB-1E37A2BAF86B") SET(GUID_SETUP "16E13D0B-7BFE-4BC4-A524-940716EE749F") SET(GUID_DRIVER "8BD16D93-30E0-4DF0-8B40-9A5A3D967DD6") SET(GUID_DEBUG "2EA8B4DD-F470-4362-8D87-59090D255981") SET(GUID_PLUGINS "B6355F5E-FA0B-427a-AC77-BC145887D11B") SET(GUID_PLUGINS_DEBUG "B2CB2291-249C-4258-83CB-A3E9C4DC9CFE") SET(GUID_INSTALLER_TOOLS "786BD2C3-20B0-4b8f-8D9B-374C736E3A1B") ENDIF() CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/wininstall/mariadb_odbc.xml.in ${CMAKE_BINARY_DIR}/wininstall/mariadb_odbc.xml) IF(${revno}) SET(MSI_PACKAGE "mariadb-connector-odbc-${PRODUCT_VERSION}-r${revno}-${PLATFORM}.msi") ELSE() SET(MSI_PACKAGE "mariadb-connector-odbc-${PRODUCT_VERSION}-${PLATFORM}.msi") ENDIF() SET(ENV{MARIADB_ODBC_MSI_PACKAGE} "${MSI_PACKAGE}") IF(WITH_SIGNCODE) IF(EXISTS "/tools/sign.bat") ADD_CUSTOM_TARGET(SIGNMSI DEPENDS ${MSI_PACKAGE} COMMAND /tools/sign.bat ${MSI_PACKAGE}) ELSE() ADD_CUSTOM_TARGET(SIGNMSI DEPENDS ${MSI_PACKAGE} COMMAND signtool sign ${SIGN_OPTIONS} ${MSI_PACKAGE}) ENDIF() ADD_DEPENDENCIES(SIGNMSI ${MSI_PACKAGE}) SET_TARGET_PROPERTIES(SIGNMSI PROPERTIES EXCLUDE_FROM_ALL OFF) ENDIF() MESSAGE(STATUS "MSI package name ${MSI_PACKAGE}") ADD_CUSTOM_TARGET( ${MSI_PACKAGE} COMMAND "${WIX_DIR}/light.exe" -ext WixUIExtension -ext WixUtilExtension mariadb_odbc.wixobj -o ${MSI_PACKAGE}) SET_TARGET_PROPERTIES(${MSI_PACKAGE} PROPERTIES EXCLUDE_FROM_ALL OFF) ADD_CUSTOM_TARGET( ODBC_WIX DEPENDS mariadb_odbc.xml binaries_dir.xml COMMAND "${WIX_DIR}/candle.exe" -ext WixUIExtension -ext WixUtilExtension mariadb_odbc.xml -o mariadb_odbc.wixobj) ADD_DEPENDENCIES(${MSI_PACKAGE} ODBC_WIX) ADD_DEPENDENCIES(ODBC_WIX maodbc maodbcs change_dsns_driver) IF(NOT USE_SYSTEM_INSTALLED_LIB) IF(ALL_PLUGINS_STATIC) ADD_DEPENDENCIES(ODBC_WIX maodbc maodbcs) ELSE() ADD_DEPENDENCIES(ODBC_WIX maodbc maodbcs dialog caching_sha2_password auth_gssapi_client sha256_password mysql_clear_password) ENDIF() ENDIF() mariadb-connector-odbc-3.1.15/wininstall/binaries_dir.xml.in000066400000000000000000000003541414431724000240440ustar00rootroot00000000000000 mariadb-connector-odbc-3.1.15/wininstall/change_dsns_driver.c000066400000000000000000000251221414431724000242560ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2019 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ const char OldVersionsDriverName[][32]= {"MariaDB ODBC 1.0 Driver", "MariaDB ODBC 2.0 Driver", "MariaDB ODBC 3.0 Driver"}; const char *DriverVersionBeingInstalled= "MariaDB ODBC 3.1 Driver"; const char *OldVersionsString= "MariaDB ODBC 1.0 Driver, MariaDB ODBC 2.0 Driver, MariaDB ODBC 3.0 Driver"; #include int Usage() { printf("Usage: change_dsns_driver [--help][--reg-direct][--also-sys][]\n" " - driver to re-register DSN's, that currently use old driver versions.\n" " By default that is \"%s\"\n" "Options:\n" " --help - shows this message.\n" " --also-sys - process also System DSN's in addition to User ones\n" " --reg-direct - change values directly in the Registry instead of using ODBC API\n" "Driver versions, that will be substituted:\n" " %s" "\n", DriverVersionBeingInstalled, OldVersionsString); return 1; } #define SQL_NOUNICODEMAP #include #ifdef _WIN32 # include "ma_platform_win32.h" #else int _snprintf(char *buffer, size_t count, const char *format, ...) { va_list list; va_start(list, format); int result= vsnprintf(buffer, count, format, list); va_end(list); /* _snprintf returns negative number if buffer is not big enough */ if (result > count) { return count - result - 1; } return result; } #endif /* Globals */ BOOL Interactive= TRUE; #include #include typedef char my_bool; #include "ma_dsn.h" /*----------------------------------------------------------------------------------------------------*/ /* Stub to shut up the linker. That is easier than to add source with the real function, as that would pull other problems in. SHould be safe since we don't use function calling it anyway */ typedef struct st_ma_odbc_error { char SqlState[SQL_SQLSTATE_SIZE + 1]; char SqlStateV2[SQL_SQLSTATE_SIZE + 1]; char SqlErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1]; SQLRETURN ReturnValue; } MADB_ERROR; typedef struct { char SqlState[SQL_SQLSTATE_SIZE + 1]; SQLINTEGER NativeError; char SqlErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1]; size_t PrefixLen; SQLRETURN ReturnValue; MADB_ERROR *ErrRecord; /* Order number of last requested error record */ unsigned int ErrorNum; } MADB_Error; SQLRETURN MADB_SetError(MADB_Error *Error, unsigned int SqlErrorCode, const char *SqlErrorMsg, unsigned int NativeError) { return SQL_SUCCESS; } MADB_ERROR MADB_ErrorList[] = { { "00000", "", "", SQL_ERROR } }; /*---------- The end of shutting up the linker ------------*/ void Message(const char* Text) { if (Interactive != FALSE) { fprintf(stdout, "-- %s\n", Text); } } void Error(const char * ErrorText) { if (Interactive != FALSE) { fprintf(stderr, "An Error occurred: %s\n", ErrorText); } } void Die(const char * ErrorText) { Error(ErrorText); exit(1); } unsigned int Ask(const char *Question, const char *Options, unsigned int Default) { unsigned int i= 0; int UserInput; if (Interactive != FALSE) { printf(Question); printf("("); } while (Options[i] != 0) { if (i > 0) { printf("/"); } printf("%c", i == Default ? toupper(Options[i]) : Options[i]); ++i; } printf(")"); UserInput= getc(stdin); if (UserInput != 0) { i= 0; while (Options[i] != 0) { if (toupper(Options[i]) == toupper(UserInput)) { return i; } ++i; } } return Default; } int DriverToChange(const char *DriverName) { unsigned int i; for (i= 0; i < sizeof(OldVersionsDriverName)/sizeof(OldVersionsDriverName[0]); ++i) { if (strcmp(OldVersionsDriverName[i], DriverName) == 0) { return 1; } } return 0; } void FreeDsnsList(char** Dsn, DWORD Count) { DWORD i; for (i= 0; i < Count; ++i) free(Dsn[i]); free(Dsn); } const char * RegOpError() { static char err[1024]; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, 0, 0, err, sizeof(err), NULL); return err; } HKEY MA_OpenRegKey(HKEY Key, const char * Path, BOOL Fatal) { HKEY Handle= NULL; LSTATUS ls= RegOpenKeyExA(HKEY_CURRENT_USER, Path, 0, KEY_ALL_ACCESS, &Handle); if (ls != ERROR_SUCCESS) { const char *err= RegOpError(); if (Fatal != FALSE) { Die(err); } else { Error(err); } } return Handle; } int DoSingleRegBranch(HKEY Branch, const char *NewDriverName) { char DsName[SQL_MAX_DSN_LENGTH], DriverName[256]; char DsnSubkey[64 /*> sizeof("Software\\ODBC\\ODBC.INI\\ODBC Data Sources") */ + SQL_MAX_DSN_LENGTH], **DsnToChange= (char**)NULL; HKEY OdbcIni, OdbcDataSources; DWORD DriverLen, NewDriverNameLen= (DWORD)(strlen(NewDriverName) + 1)/*Should be with TN*/; DWORD DsNameLen= sizeof(DsName), DsnCount= 0, i, TargetDsnCount= 0; OdbcIni= MA_OpenRegKey(Branch, "Software\\ODBC\\ODBC.INI", TRUE); RegQueryInfoKeyA(OdbcIni, NULL, NULL, NULL, &DsnCount, NULL, NULL, NULL, NULL, NULL, NULL, NULL); for (i= 0; i < DsnCount; ++i) { RegEnumKeyExA(OdbcIni, i, DsName, &DsNameLen, NULL, NULL, NULL, NULL); DsNameLen= sizeof(DsName); DriverLen= sizeof(DriverName); _snprintf(DsnSubkey, sizeof(DsnSubkey), "Software\\ODBC\\ODBC.INI\\%s", DsName); if (RegGetValueA(OdbcIni, DsName, "Driver", RRF_RT_REG_SZ, NULL, DriverName, &DriverLen) == ERROR_SUCCESS) { if (DriverToChange(DriverName)) { if (DsnToChange == NULL) { DsnToChange= calloc(DsnCount - i, sizeof(char*)); if (DsnToChange == NULL) { Die("Could not allocate memory"); } } DsnToChange[TargetDsnCount]= _strdup(DsName); ++TargetDsnCount; Message(DsName); } } } RegCloseKey(OdbcIni); /* Changing driver values in the separate cycle. Since some people promise all kinds of misery if change registry while navigating thru subkeys. It is simpler to do this separately, than read if this case may cause such issues */ OdbcDataSources= MA_OpenRegKey(Branch, "Software\\ODBC\\ODBC.INI\\ODBC Data Sources", TRUE); for (i= 0; i < TargetDsnCount; ++i) { _snprintf(DsnSubkey, sizeof(DsnSubkey), "Software\\ODBC\\ODBC.INI\\%s", DsnToChange[i]); OdbcIni= MA_OpenRegKey(Branch, DsnSubkey, FALSE); if (OdbcIni != NULL) { LSTATUS s= RegSetValueExA(OdbcIni, "Driver", 0, REG_SZ, NewDriverName, NewDriverNameLen); if (s != ERROR_SUCCESS) { Error(RegOpError()); } RegCloseKey(OdbcIni); RegSetValueExA(OdbcDataSources, DsnToChange[i], 0, REG_SZ, NewDriverName, NewDriverNameLen); } } RegCloseKey(OdbcDataSources); FreeDsnsList(DsnToChange, TargetDsnCount); return 0; } void DoRegistryDirectly(const char *NewDriverName, BOOL AlsoSystem) { DoSingleRegBranch(HKEY_CURRENT_USER, NewDriverName); if (AlsoSystem) { DoSingleRegBranch(HKEY_LOCAL_MACHINE, NewDriverName); } } void DoOdbcWay(const char *NewDriverName, BOOL AlsoSystem) { char DsName[SQL_MAX_DSN_LENGTH], DriverName[256]; SQLSMALLINT DriverLen; SQLHANDLE Env; SQLUSMALLINT Direction= AlsoSystem != FALSE ? SQL_FETCH_FIRST : SQL_FETCH_FIRST_USER; SQLRETURN rc; SQLSMALLINT DsnLen; SQLAllocHandle(SQL_HANDLE_ENV, NULL, &Env); SQLSetEnvAttr(Env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)(SQLLEN)SQL_OV_ODBC2, 0); rc= SQLDataSources(Env, Direction, DsName, sizeof(DsName), &DsnLen, DriverName, sizeof(DriverName), &DriverLen); while (rc != SQL_ERROR && rc != SQL_NO_DATA) { if (DriverToChange(DriverName)) { MADB_Dsn *Dsn= MADB_DSN_Init(); if (Dsn == NULL) { Die("Could not allocate memory"); } Dsn->DSNName= _strdup(DsName); if (!MADB_ReadDSN(Dsn, NULL, FALSE)) { Error("Could not read DSN"); MADB_DSN_Free(Dsn); continue; } free(Dsn->Driver); Dsn->Driver= _strdup(NewDriverName); Message(DsName); MADB_SaveDSN(Dsn); MADB_DSN_Free(Dsn); } Direction= SQL_FETCH_NEXT; rc= SQLDataSources(Env, Direction, DsName, sizeof(DsName), &DsnLen, DriverName, sizeof(DriverName), &DriverLen); } if (rc != SQL_NO_DATA) /* i.e. There was an error */ { SQLCHAR SQLState[6]; SQLINTEGER NativeError; SQLCHAR SQLMessage[SQL_MAX_MESSAGE_LENGTH]; SQLSMALLINT TextLengthPtr; SQLGetDiagRec(SQL_HANDLE_ENV, Env, 1, SQLState, &NativeError, SQLMessage, SQL_MAX_MESSAGE_LENGTH, &TextLengthPtr); fprintf(stderr, "[%s] (%d) %s\n", SQLState, NativeError, SQLMessage); } } int main(int argc, char** argv) { const char *NewDriverName= DriverVersionBeingInstalled; BOOL RegistryDirect= FALSE, SystemDsns= FALSE; if (argc > 1) { if (strcmp(argv[1], "--help") == 0) { Usage(); return 0; } else { int i; for (i= 1; i < argc; ++i) { if (strcmp(argv[i], "--reg-direct") == 0) { RegistryDirect= TRUE; } else if (strcmp(argv[i], "--also-sys") == 0) { SystemDsns= TRUE; } else if (strcmp(argv[i], "--quiet") == 0) { Interactive= FALSE; } else if (strncmp(argv[i], "--", 2) == 0) { Usage(); return 1; } else { NewDriverName= argv[i]; } } /* for on argv */ } /* argv[1] == "--help" */ } /* argc > 1*/ else { Usage(); if (Ask("Are you sure you want to proceed with default paremeter values?", "yn", 0)) { return 0; } } if (RegistryDirect != FALSE) { DoRegistryDirectly(NewDriverName, SystemDsns); } else { DoOdbcWay(NewDriverName, SystemDsns); } return 0; } mariadb-connector-odbc-3.1.15/wininstall/license.rtf000066400000000000000000002127161414431724000224310ustar00rootroot00000000000000{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1031\deflangfe1031\themelang1031\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} {\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f322\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f323\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f325\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f326\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f327\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f328\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\f329\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f330\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f342\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f343\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;} {\f345\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f346\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f347\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f348\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);} {\f349\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f350\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f662\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f663\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} {\f665\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f666\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f669\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f670\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} {\f692\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f693\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\f695\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f696\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\f699\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f700\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;}{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} {\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;} {\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} {\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0; \red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa200\sl276\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 \snext0 \sqformat \spriority0 Normal;} {\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 \snext11 \ssemihidden \sunhideused Normal Table;}{\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 \snext15 \sqformat \spriority1 \styrsid7013135 No Spacing;}}{\*\rsidtbl \rsid1254708\rsid7013135}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Windows User} {\creatim\yr2013\mo10\dy26\hr9\min47}{\revtim\yr2013\mo10\dy26\hr9\min55}{\version2}{\edmins0}{\nofpages9}{\nofwords3590}{\nofchars22619}{\nofcharsws26157}{\vern49167}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} \paperw12240\paperh15840\margl1417\margr1417\margt1417\margb1134\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\hyphhotz425\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120 \dghorigin1701\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot7013135 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}} {\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} {\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9 \pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af2\afs28 \ltrch\fcs0 \b\fs28\lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 GNU LESSER GENERAL PUBLIC LICENSE \par }{\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 Version 2.1, February 1999 \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par }\pard \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 Copyright (C) 1991, 1999 Free Software Foundation, Inc. \par \hich\af31506\dbch\af31505\loch\f31506 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \par \hich\af31506\dbch\af31505\loch\f31506 Everyone is permitted to copy and distribute verbatim copies \par \hich\af31506\dbch\af31505\loch\f31506 of this license document, but changing it is not allowed. \par \par \hich\af31506\dbch\af31505\loch\f31506 [This is the first released version of the Lesser GPL. \hich\af31506\dbch\af31505\loch\f31506 It also counts as the successor of the GNU Library Public License, version 2, \hich\af31506\dbch\af31505\loch\f31506 hence the version number 2.1.] \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par }\pard \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 {\rtlch\fcs1 \af2 \ltrch\fcs0 \b\lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 Preamble \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7013135 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to sh\hich\af31506\dbch\af31505\loch\f31506 are and change free software--to make sure the software is free for all its users. \par }\pard\plain \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1031\langfe1031\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1031\langfenp1031 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par \hich\af31506\dbch\af31505\loch\f31506 This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other au\hich\af31506\dbch\af31505\loch\f31506 thors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. \par \par \hich\af31506\dbch\af31505\loch\f31506 When we sp\hich\af31506\dbch\af31505\loch\f31506 eak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source \hich\af31506\dbch\af31505\loch\f31506 c\hich\af31506\dbch\af31505\loch\f31506 ode or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. \par \par \hich\af31506\dbch\af31505\loch\f31506 To protect your rights, we need to make restrictions that forbid distributors to deny y\hich\af31506\dbch\af31505\loch\f31506 ou these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. \par \hich\af31506\dbch\af31505\loch\f31506 For example, if you distribute copies of the library, whether gratis\hich\af31506\dbch\af31505\loch\f31506 or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so tha \hich\af31506\dbch\af31505\loch\f31506 t\hich\af31506\dbch\af31505\loch\f31506 they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. \par \par \hich\af31506\dbch\af31505\loch\f31506 We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer y\hich\af31506\dbch\af31505\loch\f31506 ou this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else \hich\af31506\dbch\af31505\loch\f31506 a\hich\af31506\dbch\af31505\loch\f31506 nd passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. \par \par \hich\af31506\dbch\af31505\loch\f31506 Finally, software patents pose a constant threat to t\hich\af31506\dbch\af31505\loch\f31506 he existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 the library must be consistent with the full freedom of use specified in this license. \par \par \hich\af31506\dbch\af31505\loch\f31506 Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to c\hich\af31506\dbch\af31505\loch\f31506 ertain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. \par \par \hich\af31506\dbch\af31505\loch\f31506 When a program is linked with a library, whether \hich\af31506\dbch\af31505\loch\f31506 statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteri \hich\af31506\dbch\af31505\loch\f31506 a\hich\af31506\dbch\af31505\loch\f31506 of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. \par \par \hich\af31506\dbch\af31505\loch\f31506 We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. \par \par \hich\af31506\dbch\af31505\loch\f31506 For example, on rare occasions, there may be a special need to encourag\hich\af31506\dbch\af31505\loch\f31506 e the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free librarie \hich\af31506\dbch\af31505\loch\f31506 s\hich\af31506\dbch\af31505\loch\f31506 . In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. \par \par \hich\af31506\dbch\af31505\loch\f31506 In other cases, permission to use a particular library in non-free programs enables a greater number of people to u\hich\af31506\dbch\af31505\loch\f31506 se a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. \par \par \hich\af31506\dbch\af31505\loch\f31506 Although the Lesser General \hich\af31506\dbch\af31505\loch\f31506 Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. \par \par \hich\af31506\dbch\af31505\loch\f31506 The precise terms and con\hich\af31506\dbch\af31505\loch\f31506 ditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be comb \hich\af31506\dbch\af31505\loch\f31506 i\hich\af31506\dbch\af31505\loch\f31506 ned with the library in order to run. \par \page \par }\pard \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 {\rtlch\fcs1 \af2\afs24 \ltrch\fcs0 \b\fs24\lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 GNU LESSER GENERAL PUBLIC LICENSE \par \hich\af31506\dbch\af31505\loch\f31506 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par \hich\af31506\dbch\af31505\loch\f31506 0. This License Agreement applies to any software library or other program which contains a no\hich\af31506\dbch\af31505\loch\f31506 tice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". \par \par \hich\af31506\dbch\af31505\loch\f31506 A "library" means a collection of softwa\hich\af31506\dbch\af31505\loch\f31506 re functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. \par \par \hich\af31506\dbch\af31505\loch\f31506 The "Library", below, refers to any such software library or work which has been distributed un\hich\af31506\dbch\af31505\loch\f31506 der these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardl \hich\af31506\dbch\af31505\loch\f31506 y\hich\af31506\dbch\af31505\loch\f31506 into another language. (Hereinafter, translation is included without limitation in the term "modification".) \par \par \hich\af31506\dbch\af31505\loch\f31506 "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all t\hich\af31506\dbch\af31505\loch\f31506 he source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. \par \par \hich\af31506\dbch\af31505\loch\f31506 Activities other than copying, distribution and modification are not covered by thi\hich\af31506\dbch\af31505\loch\f31506 s License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a too \hich\af31506\dbch\af31505\loch\f31506 l\hich\af31506\dbch\af31505\loch\f31506 for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. \par \par \hich\af31506\dbch\af31505\loch\f31506 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided tha\hich\af31506\dbch\af31505\loch\f31506 t you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along w \hich\af31506\dbch\af31505\loch\f31506 i\hich\af31506\dbch\af31505\loch\f31506 th the Library. \par \par \hich\af31506\dbch\af31505\loch\f31506 You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. \par \page \par \hich\af31506\dbch\af31505\loch\f31506 2. You may modify your copy or copies of the Library or any portion of it, thus forming a w\hich\af31506\dbch\af31505\loch\f31506 ork based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: \par \par \hich\af31506\dbch\af31505\loch\f31506 a) The modified work must itself be a software library. \par \par \hich\af31506\dbch\af31505\loch\f31506 b) You mus\hich\af31506\dbch\af31505\loch\f31506 t cause the files modified to carry prominent notices stating that you changed the files and the date of any change. \par \par \hich\af31506\dbch\af31505\loch\f31506 c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 d) If a\hich\af31506\dbch\af31505\loch\f31506 facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that ,\hich\af31506\dbch\af31505\loch\f31506 in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. \par \par \hich\af31506\dbch\af31505\loch\f31506 (For example, a function in a library to compute square roots has a purpose that is entirel\hich\af31506\dbch\af31505\loch\f31506 y well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute sq \hich\af31506\dbch\af31505\loch\f31506 u\hich\af31506\dbch\af31505\loch\f31506 are roots.) \par \par \hich\af31506\dbch\af31505\loch\f31506 These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, \hich\af31506\dbch\af31505\loch\f31506 do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permi \hich\af31506\dbch\af31505\loch\f31506 s\hich\af31506\dbch\af31505\loch\f31506 sions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. \par \par \hich\af31506\dbch\af31505\loch\f31506 Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to e\hich\af31506\dbch\af31505\loch\f31506 xercise the right to control the distribution of derivative or collective works based on the Library. \par \par \hich\af31506\dbch\af31505\loch\f31506 In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or\hich\af31506\dbch\af31505\loch\f31506 distribution medium does not bring the other work under the scope of this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the \hich\af31506\dbch\af31505\loch\f31506 notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify tha \hich\af31506\dbch\af31505\loch\f31506 t\hich\af31506\dbch\af31505\loch\f31506 version instead if you wish.) Do not make any other change in \par \hich\af31506\dbch\af31505\loch\f31506 these notices. \par \par \hich\af31506\dbch\af31505\loch\f31506 Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works m\hich\af31506\dbch\af31505\loch\f31506 ade from that copy. \par \par \hich\af31506\dbch\af31505\loch\f31506 This option is useful when you wish to copy part of the code of the Library into a program that is not a library. \par \par \hich\af31506\dbch\af31505\loch\f31506 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or exe\hich\af31506\dbch\af31505\loch\f31506 cutable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for softwa \hich\af31506\dbch\af31505\loch\f31506 r\hich\af31506\dbch\af31505\loch\f31506 e interchange. \par \par \hich\af31506\dbch\af31505\loch\f31506 If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though th \hich\af31506\dbch\af31505\loch\f31506 ird parties are not compelled to copy the source along with the object code. \par \par \hich\af31506\dbch\af31505\loch\f31506 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the L \hich\af31506\dbch\af31505\loch\f31506 i\hich\af31506\dbch\af31505\loch\f31506 brary, and therefore falls outside the scope of this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work t \hich\af31506\dbch\af31505\loch\f31506 hat uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. \par \par \hich\af31506\dbch\af31505\loch\f31506 When a "work that uses the Library" uses material from a header file that is part of the Library, the object code \hich\af31506\dbch\af31505\loch\f31506 for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true \hich\af31506\dbch\af31505\loch\f31506 i\hich\af31506\dbch\af31505\loch\f31506 s not precisely defined by law. \par \par \hich\af31506\dbch\af31505\loch\f31506 If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, rega \hich\af31506\dbch\af31505\loch\f31506 rdless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) \par \par \hich\af31506\dbch\af31505\loch\f31506 Otherwise, if the work is a derivative of the Library, you may distribute the object code for the\hich\af31506\dbch\af31505\loch\f31506 work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. \par \par \hich\af31506\dbch\af31505\loch\f31506 6. As an exception to the Sections above, you may also combine or link a "work that use\hich\af31506\dbch\af31505\loch\f31506 s the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debug \hich\af31506\dbch\af31505\loch\f31506 g\hich\af31506\dbch\af31505\loch\f31506 ing such modifications. \par \par \hich\af31506\dbch\af31505\loch\f31506 You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution display \hich\af31506\dbch\af31505\loch\f31506 s copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: \par \par \hich\af31506\dbch\af31505\loch\f31506 a) Accompany the work with the complete corresponding\hich\af31506\dbch\af31505\loch\f31506 machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work th \hich\af31506\dbch\af31505\loch\f31506 a\hich\af31506\dbch\af31505\loch\f31506 t uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions file \hich\af31506\dbch\af31505\loch\f31506 s\hich\af31506\dbch\af31505\loch\f31506 in the Library will not necessarily be able to recompile the application to use the modified definitions.) \par \par \hich\af31506\dbch\af31505\loch\f31506 b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the l\hich\af31506\dbch\af31505\loch\f31506 ibrary already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-c \hich\af31506\dbch\af31505\loch\f31506 o\hich\af31506\dbch\af31505\loch\f31506 mpatible with the version that the work was made with. \par \par \hich\af31506\dbch\af31505\loch\f31506 c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing \hich\af31506\dbch\af31505\loch\f31506 this distribution. \par \par \hich\af31506\dbch\af31505\loch\f31506 d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. \par \par \hich\af31506\dbch\af31505\loch\f31506 e) Verify that the user has already received a copy o\hich\af31506\dbch\af31505\loch\f31506 f these materials or that you have already sent this user a copy. \par \par \hich\af31506\dbch\af31505\loch\f31506 For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special e \hich\af31506\dbch\af31505\loch\f31506 xception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that co \hich\af31506\dbch\af31505\loch\f31506 m\hich\af31506\dbch\af31505\loch\f31506 ponent itself accompanies the executable. \par \par \hich\af31506\dbch\af31505\loch\f31506 It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them a \hich\af31506\dbch\af31505\loch\f31506 nd the Library together in an executable that you distribute. \par \par \hich\af31506\dbch\af31505\loch\f31506 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: \par \par \hich\af31506\dbch\af31505\loch\f31506 a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other libr\hich\af31506\dbch\af31505\loch\f31506 ary facilities. This must be distributed under the terms of the Sections above. \par \par \hich\af31506\dbch\af31505\loch\f31506 b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined fo\hich\af31506\dbch\af31505\loch\f31506 rm of the same work. \par \par \hich\af31506\dbch\af31505\loch\f31506 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and wil \hich\af31506\dbch\af31505\loch\f31506 l automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. \par \par \hich\af31506\dbch\af31505\loch\f31506 9. You are not requir\hich\af31506\dbch\af31505\loch\f31506 ed to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modif \hich\af31506\dbch\af31505\loch\f31506 y\hich\af31506\dbch\af31505\loch\f31506 ing or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. \par \par \hich\af31506\dbch\af31505\loch\f31506 10. Each time you redist\hich\af31506\dbch\af31505\loch\f31506 ribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further re \hich\af31506\dbch\af31505\loch\f31506 s\hich\af31506\dbch\af31505\loch\f31506 trictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other \hich\af31506\dbch\af31505\loch\f31506 reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so \hich\af31506\dbch\af31505\loch\f31506 a\hich\af31506\dbch\af31505\loch\f31506 s to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Lib \hich\af31506\dbch\af31505\loch\f31506 r\hich\af31506\dbch\af31505\loch\f31506 ary by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. \par \par \hich\af31506\dbch\af31505\loch\f31506 If any portion of this section is held invalid or unenforce\hich\af31506\dbch\af31505\loch\f31506 able under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. \par \par \hich\af31506\dbch\af31505\loch\f31506 It is not the purpose of this section to induce you to infringe any patents or other property \hich\af31506\dbch\af31505\loch\f31506 right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions \hich\af31506\dbch\af31505\loch\f31506 t\hich\af31506\dbch\af31505\loch\f31506 o the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose \hich\af31506\dbch\af31505\loch\f31506 t\hich\af31506\dbch\af31505\loch\f31506 hat choice. \par \par \hich\af31506\dbch\af31505\loch\f31506 This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted int\hich\af31506\dbch\af31505\loch\f31506 erfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such c \hich\af31506\dbch\af31505\loch\f31506 a\hich\af31506\dbch\af31505\loch\f31506 se, this License incorporates the limitation as if written in the body of this License. \par \par \hich\af31506\dbch\af31505\loch\f31506 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar i\hich\af31506\dbch\af31505\loch\f31506 n spirit to the present version, but may differ in detail to address new problems or concerns. \par \par \hich\af31506\dbch\af31505\loch\f31506 Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", y\hich\af31506\dbch\af31505\loch\f31506 ou have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the \hich\af31506\dbch\af31505\loch\f31506 F\hich\af31506\dbch\af31505\loch\f31506 ree Software Foundation. \par \par \hich\af31506\dbch\af31505\loch\f31506 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free So \hich\af31506\dbch\af31505\loch\f31506 ftware Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of \hich\af31506\dbch\af31505\loch\f31506 s\hich\af31506\dbch\af31505\loch\f31506 oftware generally. \par \par \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af2 \ltrch\fcs0 \b\lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 NO WARRANTY \par }{\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par \hich\af31506\dbch\af31505\loch\f31506 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTH\hich\af31506\dbch\af31505\loch\f31506 ERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULA \hich\af31506\dbch\af31505\loch\f31506 R\hich\af31506\dbch\af31505\loch\f31506 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. \par \par \hich\af31506\dbch\af31505\loch\f31506 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGRE\hich\af31506\dbch\af31505\loch\f31506 ED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR I \hich\af31506\dbch\af31505\loch\f31506 N\hich\af31506\dbch\af31505\loch\f31506 ABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEE \hich\af31506\dbch\af31505\loch\f31506 N\hich\af31506\dbch\af31505\loch\f31506 ADVISED OF THE POSSIBILITY OF SUCH \par \hich\af31506\dbch\af31505\loch\f31506 DAMAGES. \par \par }\pard \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 END OF TERMS AND CONDITIONS \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par }\pard \ltrpar\qc \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0\pararsid7013135 {\rtlch\fcs1 \af2 \ltrch\fcs0 \b\lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 How to Apply These Terms to Your New Libraries \par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par \hich\af31506\dbch\af31505\loch\f31506 If you develop a new library, and you want it to be of the greatest possible use to the public, we \hich\af31506\dbch\af31505\loch\f31506 recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). \par \par \hich\af31506\dbch\af31505\loch\f31506 To apply these terms, attach the foll\hich\af31506\dbch\af31505\loch\f31506 owing notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. \par \par \hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af2 \ltrch\fcs0 \insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 Copyright (C) \par \par }{\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as publishe\hich\af31506\dbch\af31505\loch\f31506 d by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. \par \par \hich\af31506\dbch\af31505\loch\f31506 This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\hich\af31506\dbch\af31505\loch\f31506 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. \par \par \hich\af31506\dbch\af31505\loch\f31506 You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software \par \hich\af31506\dbch\af31505\loch\f31506 Foundation, Inc\hich\af31506\dbch\af31505\loch\f31506 ., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \par \par \hich\af31506\dbch\af31505\loch\f31506 Also add information on how to contact you by electronic and paper mail. \par \par \hich\af31506\dbch\af31505\loch\f31506 You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaim\hich\af31506\dbch\af31505\loch\f31506 er" for the library, if \par \hich\af31506\dbch\af31505\loch\f31506 necessary. Here is a sample; alter the names: \par \par \hich\af31506\dbch\af31505\loch\f31506 Yoyodyne, Inc., hereby disclaims all copyright interest in the \par \hich\af31506\dbch\af31505\loch\f31506 library `Frob' (a library for tweaking knobs) written by James Random Hacker. \par \par \hich\af31506\dbch\af31505\loch\f31506 , 1 April 1990 \par \hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af2 \ltrch\fcs0 \insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 Ty Coon, President of Vice \par \par }{\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \hich\af31506\dbch\af31505\loch\f31506 That's all there is to it! \par }{\rtlch\fcs1 \af2 \ltrch\fcs0 \lang1033\langfe1031\langnp1033\insrsid1254708\charrsid7013135 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210030dd4329a8060000a41b0000160000007468656d652f7468656d652f 7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad 79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b 5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab 999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f 9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be 15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d 32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 e877f0034e16bafb0e258ebb4faf06b769e888340b103d331115bebc4eb813bf83291b63624a0d1475a756c734f9bbc2cd28546ecbe1e20a3794ca175f3fae90 fb6d2dd99bb07b55e5ccf68942bd0877b23c77b908e8db5f9db7f024d9239010f35bd4bbe2fcae387bfff9e2bc289f2fbe24cfaa301468dd8bd846dbb4ddf1c2 ae7b4c191ba8292337a469bc25ec3d411f06f53a73e224c5292c8de0516732307070a1c0660d125c7d44553488700a4d7bddd3444299910e254ab984c3a219ae a4adf1d0f82b7bd46cea4388ad1c12ab5d1ed8e1153d9c9f350a3246aad01c6873462b9ac05999ad5cc988826eafc3acae853a33b7ba11cd1445875ba1b236b1 399483c90bd560b0b0263435085a21b0f22a9cf9356b38ec6046026d77eba3dc2dc60b17e92219e180643ed27acffba86e9c94c7ca9c225a0f1b0cfae0788ad5 4adc5a9aec1b703b8b93caec1a0bd8e5de7b132fe5113cf312503b998e2c2927274bd051db6b35979b1ef271daf6c6704e86c73805af4bdd476216c26593af84 0dfb5393d964f9cc9bad5c313709ea70f561ed3ea7b053075221d51696910d0d339585004b34272bff7213cc7a510a5454a3b349b1b206c1f0af490176745d4b c663e2abb2b34b23da76f6352ba57ca2881844c1111ab189d8c7e07e1daaa04f40255c77988aa05fe06e4e5bdb4cb9c5394bbaf28d98c1d971ccd20867e556a7 689ec9166e0a522183792b8907ba55ca6e943bbf2a26e52f48957218ffcf54d1fb09dc3eac04da033e5c0d0b8c74a6b43d2e54c4a10aa511f5fb021a07533b20 5ae07e17a621a8e082dafc17e450ffb739676998b48643a4daa7211214f623150942f6a02c99e83b85583ddbbb2c4996113211551257a656ec1139246ca86be0 aadedb3d1441a89b6a929501833b197fee7b9641a3503739e57c732a59b1f7da1cf8a73b1f9bcca0945b874d4393dbbf10b1680f66bbaa5d6f96e77b6f59113d 316bb31a795600b3d256d0cad2fe354538e7566b2bd69cc6cbcd5c38f0e2bcc63058344429dc2121fd07f63f2a7c66bf76e80d75c8f7a1b622f878a18941d840 545fb28d07d205d20e8ea071b283369834296bdaac75d256cb37eb0bee740bbe278cad253b8bbfcf69eca23973d939b97891c6ce2cecd8da8e2d343578f6648a c2d0383fc818c798cf64e52f597c740f1cbd05df0c264c49134cf09d4a60e8a107260f20f92d47b374e32f000000ffff0300504b030414000600080000002100 0dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f7 8277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89 d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd500 1996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0f bfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6 a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a 0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021 0030dd4329a8060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d001400060008 00000021000dd1909fb60000001b0100002700000000000000000000000000b20900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ad0a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; \lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; \lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; \lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; \lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; \lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; \lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; \lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; \lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; \lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; \lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; \lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; \lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; \lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; \lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; \lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; \lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; \lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; \lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; \lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; \lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; \lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; \lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; \lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; \lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; \lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; \lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; \lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; \lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; \lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; \lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000050f6 efc420d2ce01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}}mariadb-connector-odbc-3.1.15/wininstall/mariadb_odbc.xml.in000066400000000000000000000174261414431724000240100ustar00rootroot00000000000000 = 602]]> WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed <@PLUGINS_BEGIN@ComponentRef Id="CcPlugins" /@PLUGINS_END@> <@TLS_LIB_BEGIN@ComponentRef Id="TlsLib" /@TLS_LIB_END@> <@PLUGINS_BEGIN@Feature Id="PluginsFeature" Title="Authentification Plugins" Level="2"> <@PLUGINS_BEGIN@Component Id="CcPlugins" Guid="@GUID_PLUGINS@" Directory="PLUGINSFOLDER" DiskId="1" Win64="@IS_WIN64@"> <@TLS_LIB_BEGIN@Component Id="TlsLib" Guid="@GUID_TLS_LIB@" Directory="INSTALLFOLDER" DiskId="1" Win64="@IS_WIN64@"> mariadb-connector-odbc-3.1.15/wininstall/mdb-connector-odbc.png000066400000000000000000003415021414431724000244330ustar00rootroot00000000000000PNG  IHDRtzJDsRGB@IDATxOvݶk5'B#SP$XAAQ ѢEĊ RA & ""jm/ۯcy>㼟k1zoko{_?Op_QLf>5: (+S72HRrÛpW`V\k]@_^{_Z'6+8:_[ \FY禯ֿ+^/̻9S'ܮx^3X wmzuOQO2R3=`)u΃t~6{:{p|g}> >r|aVvPz[Z=||o)/:|p+k2 ]dЋ6 *4?7|.~z\cփ+Ρ}u`suWlՇFf3Nq3;|&87Ԁ%;vofp8ya^>ދcgw֙-y3Cf5g䴙F&$h}\#)}0󬯧jax%wXM_a/;{ZueɏB́L :Q8c'!Ρ8,IgB5IWNb&C ty/|^A M,>l <#"ML\eG>Ĝ32ȋ3BO|U$&\)uuۧ#][UP&{p;[IQI.SzA(+~&OxOWhp?گ}F?%2K[K yFoOɜDsrSj%c꿻>+\wN`|<8VWk_.3t|o`P.^8D@Tw&NQ|725Ĵ{0[u}N&﮷w5fw͆v>ݣcn35pԢSqW:rw6%J}#mb]o6n_P_<3PSY ?O΅S w0nSg9Ƕ=6W[gr麰G324f}\vo㳇cQ*}:sXֳ>4+{Ƅp?h94{Kx-yIn._RwE|>Yl"yDf /#&x/H;@VW aNg0mIL{i闟JWEBw#īz뿩Gl@2_џ># V1_6?78cI,>&IJǔ6gzCStEZ/}E*ch3{b Bjz5#` N 9\Vg۫0N+ 5)Ht. t+h'rn)9mԤ<]]3"sݚsy ?mu~jȔg,r`uY[Ht?W5Oۊu˵k糲8+֒p布ֹenZ9W>G2ʺ&?gLviE)[N2 M= ^p_*)ѫ !':gNO-3tqgZFy=z'4ǿm1'Z=?շRHM{=jsczUK+o-nqEkbi/ڗ4t<]&'kNH{g)Dta3\lm0Eqk xA#82wuf/GQY\fxZ6V{+6:}~hnn[\Tz{w[[x8ZwN?K@ǻ7·{f˫["YߪJFF饞m/ YuWh\fC#u|/>JJ ٩;s%"tjvF3us0;ᬓi4y8u@&6.CNyJmYQL;:rv4vFAw^# ewYلǹ5ḼXW;/-ynwu~?S[zˈK@I7'y<^S AzjU 8W~Z;ԥ|~jVz/l+mvO?g|V^$p9ǛLEDp8ь;Tgϳ``;瘋6$R+YZgie){i׊/%XuˠTth=\pz}83^=ARS-tػsv^OCR{J[k̆@lw>=j=Z9Ym]5^xGlJ7ߛ_:w+sd}d*^9CȊ CEh&>VV#yuiV (྽O-kN̕%^;=zbg^x^0/g!B{8fJ`3qyNI^W>tGs?#<^Nذ2]% ѲL^R¾/o8 ZõwOz.F̧'mfU8H=ZUuדIZ[Sb\4(+6zr2t騖 OQ+0CI4'oZ4ҹD<\:97RS(s؂}UTp i+ؙdo1-E\\:hye<%)pY_ʜ lb Dg- ?vF #1%E _lQ4=]O?i5{g;K dq-m=dv?68a]_]BMzPHӿ!U'ZǏzQ CޫfW@q4L_G^j=CdLqyO'ݲΦ Э-cS~B ø VG kxv)oΠFʜd]7ht[E Sg3Z]`(Md<뤯!Qȁ\#ᬫR {~ tyo9s  MxN܈qYڈ_k5ZZ۔ ,[ ICU$IqA.-=3K ]</z@9h'>5t~+[_iT>?7:O-!2[Ed{w>{Fz -\DJ;V9tp:eLlǑl23(rᏫ*1<+ν?ȧ[#ady쯏mO}b6m+M:[޳ 6q%^+MP x_RY_=?ϱ(>5+zK3$h^tJ4Cο 缿@ў}zkZKPt##W;y9OSzf]ݜEyǙWIi*]=w"6S޻yv/4@s[? hN$JXl_<mՇ-}9׏8B !u"n~SW 8ˤ~g$hwg? )9eD(%gͻǡ"gٖ>;%}D7܋Y"8S K,eH?-G2CtoKdP҉I=ޟZ}2u#w+N9j=X keC2 oWfs80^MoompƳJ;O8WԾo 3L pѭ<VhNo'2wn!92- @}-/o"4[@GoԆOsn۹':M{=?W>jтCHڜwI7S~Co'hRIix*>_R~O_2~U}fg/xo {ir~,1H!,Zvr+}3|eKRIf2=/Un߾6̯fWhxԾ_~pK?vyx$>N?}Wqg~V<3CPL˳U̬FG'y1>g13k63侀y~p|9+.z=xiV,PLqw[ _ߒ᣹ rl6Y#7/)F,뫁7g%?p7tb<?SZz=πDzUNLsyzkYocČǮ᯷> uϬ/ d xvYqh~t_|Z ;Z_$V @p_RwƱp0u%ӂkb\TnWߞ11toǙWC,ı|)2/GTn+fkѱIɚY4w8vc!+5@q N6V*Oi!qƷf^MV|/vK$ug Gi>A`+w:jH> \G` )C3O#Sɱ㟨G>f3V _k>s)cb}m |xҘɏdp}o)P=(OĎC|8矊ܼGAAꧼܸ2 (BIKR'/N@k_.];AHy'*g?AM\|#pew{N^̞US!`3;{^Oo3*]Ϋu!#b r=V%ωy[|8~ʣ҉~Z\ߩe0դe/`{7r ی"-$(z7HJ\5Maƒy\M? ?egEr?);uSs}|06~n!Y8{cioPӱ_+ݻ%c6_L3yeA\{)m>oO GV P&'~28~暁0xR6\S{jڟ S<_i%&P ܚmT-3(XMfA&v_H`3pKe% ()=jr(7 c)0YOOAgz4?ڌ_Lʠ7ig@` 'h|Rl>=3SRd|cݗ_\-)w>'+^!(_0#ӊoO.xjMa?'Sj2 '|'g)y󭒠y4zw"Q1C!zӭ_M;ٟ۟m}k)ca\E} ?~jtzX@\l-Z[ys>O7iΞ'MmlLR[[dHϣs>QpO{G;Bs?K- Xܑn&U lK '(8sdK+_7g"Woqp/5!s=|,˷O>srh'OY(}_UhQc5Ae%x>0O@ Z ĝ@G窃:({A^y~)푅9f?/Ѵ}SONZx!3 %<%7s[[ܬe_ƞߑ륬DOyXt87pјQR$_Rb˄c\H~}Ѩ= K8F_ 8#.p n|EPWv9$pGB?zAp߹_H7yT\aR>_6H^5nSB ?7 ADo8j*+:9GjC5^Kʕz?@B(G\76F_?y~dxh:3 N X{ XT7@=s[+w>i?9 "HrH/d,w5"W{ѷZoyiCA(ܿ;@ gm l YJn5ȟ0  8 aMww}2;b; JTreB""pjvys Y "@ 7ENuZrJ)Oi?mZ;v4/kuXW{{a794poH]C%48 HZ:"ߴ;:.?OȊDdLȀx aXڋ =@\CK@NsO2GBW7RzM䍕5㙮Jjq+3Q5N4jӋѢ*c3F[_ vOTԵ/OME+aF>,{~[4ʏ'Es{S}CZul8߁u{~捸Wi{i ּ]^V'OpLq^w8:(AzsoȬuxFbr٬|S)6 u?3[e9EAU[¬.a3MhzeG8n]u^uO<߭<>q4;qs| ϙ A,k Pk|ersqR5y@VQMpiO>j_&%UyDf>+[.=Bh Ffv~{rL2j(h^<׋{U穖΢SSkץ ‰jSB ?/Dp<2!oZIQ: r} |gAd6d߬;*Hҹi]s!̰O9C9{T\9ͳ{9{=n|h^'D@b8d8Q5o{Q~&_ y}bcN?k Z^_?/jHQg?qJ?!-kc)f̱y/aLߙ- #l~<|6KH'g$|P(>r,nu| "JA8~ 4PJh˃ "p^$pZѧ.v=51/3}5u'P֗9Kh ZV~C/AkG%=S,,4>uW I):ymep➿߳:JJ q g␒#w)rd哟0VWz p*9ut:>=m崯4Ym`Ζk󩛦0ް!:_}.#KeV(C0ꁐ;*DuhK9DgozO?5owrxF7Q>Eh/um'>:fPތD ]#m^YY& d}\D䀮~T^SPW*X8H^##Oa.Y@hL`>'nIHD+zJ~cĉ .uo1+ZXϑ0[;-5Μ\lֵ-U,N}Nn~zG38?d| 0ltY m겺c |ٌM|-$36u$ :PJ~xeok]+ͺۗEQ!@sJܹw.te=>KZ'zsBIp׺JGWܩLouB1Y_E8;l:}HD<3q_;g,OX@m?_>:r]><97n&~`6g.Tw8z2|,Y0/w= R?#/ñFQ)fK7Ĝ!{zԓn6-QLu Rqكq:2ƕ n$-{bIn޹21[H%IL%X?9[eμ7txWoX;KtM]=߼9D
79! wypM_]ǟ`&h(b4A!ŇX!>qRwMC>@ u%hNr2#h޺"nKZɱZ$El ]Ez ~6?>gb19jH-?-O #A?8"6Z.Q:K^׊m$@YS{WXBn8U'y){Œ^#Hѽ,:Tz,q~^}G9GGIܔeuE+5͊فmVdV%䌶Y3}RXspŗ讘r`i#3ZĀz!^Kruu=fC ,:&`wnߞ5kdglXrVX3] _}jo}(ec#z8V'+BR"'ݛ`[)S>~oKuշψ*|k~g\a!kt\C[Usz⠤Ε޵:evYI9/2+$3}gu#O{[;̺ǫ>fd|⹐?/MT2C㩇l(K9I?&C~*Uɡ{>, d{š]?8hkrߓB 6H'1]!=C̵<=QJe:?V|3q|Z4X[_ t?͕ZmBϚ2FiX|y Z?ų"@O˚ X]| EPrr{^ff7d-4cH0aW)&ɏ cT 2F1?ߞ|8L]fBu9G7gYQޟ<ݭ7}{K'}B3[Mp?O\EUԪep&\xـD~tNXÁ>)<$KHXm}49^YN6A yXɬML5Q+j#Aw+q'Kvb9!]79K+'tƙ^i(OiRO+p5r Dq?$N* s9^FhB b|+$CyNSE3a5}M_r."g z? vSkyѫb`''w/g:`Pʽ\}L|/?ß[ko[]puacv3 e=Q?CbuZ oP4sH?)~ᷰץc/Z\W-{-Z߲Oh@no-Yw b{GWnf=[ xr6+}?c6uw#ΙQ (zUN?TVD*#ÕW$$Ul0zz:2RPڊbܟycdrkz_>Ř[g2<(}@>s u-cpyza]}]Nǟ6{$M۞ r$Rth5>Yn?Y [=zΛa?vv2}zF&|)6?(NjS,ZzMJ~6p- ݂u!w"ٕ PeV ¯1ۅl%ٞ+sưBt޵IZ_$m_ʰ+: (W;֘ O|[磐@sKg[y*1X~t gG)=(A^ $.}#x;Vm~kɁ>? _Oxi=k? ?puYBG2=9py!m&w%0]n0o~}|@0t]켚;O㴌~7޼Q8HoN?:m|0ygS8bx*X3[cΖ_4zCgǦxaE!ݬZ8 `%n8-ᔖ@ =UBo-ji /] a]LG"HտzJσM?pEd[yqx['^|4a {m(\FC}N):')߇=303#m~zYH:Nӏ {uCSF_NoX˯ߌHW͙~ bS-y[71ߚE qd>ۋ6on9~ D#%֑֙ "p>p8iU3~/OMsBH:MUP=K/E"5"V =^s@íSWsTVc-+һ ܝg>%shsҵ#lkqxW7d[ه]ܚu6_?HqyfBiL7MIIVx뺘Co`y_A'|(ǙgkL嚏D睱k(mC\:Ȓ3~-h0_o۟cl]PoX,YſQ\ggoOxsQ{NF)y:^K9Mbu>#` @Iڲ>Lgđ/?u;s'k>+z砪<;D[0u6>z8Z;z? N/^DDnwz&{oayxUμ4_ӓ+sBZ.?:L(&uϟc йxAQe[D1|8 l-4FSzsuռOF?GUb\1qF(1g4s1pWdSH$VzbB ?ϥ0E, )ܢQ@IDAT[]Sf|sEdkX ^ ,w@OLCo,|i"?tϐ{ ZL-q+kgd2oXZI}Ц)j84s9K? m#PM9ro_Mɹ+) ?_ $:#+՞)f![cbPc| F?zhN)R?(N~o?VpZn#)/~n]]8У~[gG&'R6aY*RG:y񶛜0dWm0t_z:)IAhrWw!R}?\Χa5*O}쮟fo=rFQ ^1g~ PG[f=,9tCts 4trhV?&.E(ՙ&G?CFz~PדK8d ap`Umܿ=OqkQ>{m]}~S+]v zm43y~A7tNPv|3+I:~'3ut6M~[z0.|DLw j_IOE&D`F׈ۡ-zD!>Ss_Dю=g ^TZ1?}Iл1}$fJOnEZ=ZGOu_wf)Ni'A8-7#\*93Gѕ^tu[mt\~52*`Išc콝w)\3nU3&o7|&~{s cfBֿAsN9 )9j}o*)C1y>K] 3rT~#PTs匤g=meYߞ>5m~NJ},ϧɝ #kſӁ6:Rɑ~{jW'^dm IGjOQ /RoMD.pedkR;32*?hXsBiy9WjOCjMuo ԦbF }bǢ5:Eg~XJj)~h3~'a,f̬BJS}g~ Ā}|sryuw˷(_{r:fY^43y/mgߙ#d}}B ~?k3I?k ?7/(P>f=ywf uiul eEV7LLs^4'syO5}D N`fN{F/P7:SRh S?vG$o81a U)~/X(Ż5$ 6>l t(G5_=Ⱦs+W m<^;kPK>ӈ 9է4}Χ@@m/EҳgubXW7p+?JV69PݱK P%|\;>Mrx̬4>yX52֢nIv>wA,v"8mZ< nM%ֳn:)]7Sm6"DB]: grPMO>d^SOEUb<m tZ7j?X !>7_]w@.y5L,X9)1xZUb¨?.ӗȎ b6ZƓ3WoXD}Vefu:|ėq|G##rx`yD (nwV!'^i z]N5ğHi[^9.-B3Iq=):p}O̽gᨺk$փ3f~WJTKۧ\ ^#O60Z8` 8b;Is@^>ЮOLRx~,]R.ν8& zY>T_ZB6x~20$g:ͦU񇭘L5¡xvU3"p&A>ɿ?P|.2?bpʻJ~~l/1w:u,nkZO3ʠĀW:^ä/`'5GEpjRsHD[kE+ԗ ~\=dBKw~>SoM> VmXy}܎Hx,]CH SJL v9vF Der9"NflD^|ӉO kA\~5 +~ǎ8_ʲwٞ(QrN^! ]XSo}~]/RIߟMTqꯖ{l_qn8j|O3A7:FƎg+嚩|a~tlOOqITT:J%iu yݛ=xo!gZ*?u( st?ZG`26קYuN)?=O`S;O w&Ƒ@,QSkj>i2̟8{WNURG"ynQˋG`^%hZP0tǔ4P:ϿpY?~XyRTw?g~ο*@Ccnx/>-=dOiF<='*(uoߗ)bzVޫpo $ IT~z~~([WwoP"8wo$y'.]?J."X2" B/5vsZ R_3QLJ}0Fк~: wpKh <ۚEzaC:#⃞K}/*ѽh"pyyߋl~}9ۅ&2[w|u|,|76S Om7Ua_4bk1/Cy7X8.يGAW뤻 mr̩ uvIjZuˆZχ ʤ+@j_3nms%2kIy~/KW/б )3>3uju~zKH'up9ikH}%sYZHe~\^S;SlNyPpsn'ypgȠn?X+Vk fsOWy?0"xVl#&yx_Z_E<\:~YI)?9u6T}~S_['ɜ/;:O$hODTJV z~7?b%!D}R|\fk{uϚY}z)WUQ|D>a|%/ך/bd^p@fd!yBƻBIH ]FmS^ ΘQzjKZ9,`Ab|ֿyp!CXhoG@ [ % '"΁pצ\G d%$k]$յ?=g]$`Bq=<,kuG d O?mƿ7LܘLfcG*`|]`ϖh**Tw-omc6G n])[zHW%"1#io? ]n@qzLjgҏGkޱ,8 7VnOZ)sWP*gk:}z|hl3>?ņ A]j%I?,9|=`Z_ g8Eu:5`;A#M{M}&0[Z жPa 66ێ|͍p?ԡ5tV%VϹLC]63yG3ғao:Kd!N+!q6U ҵ0ޜXOp;]3h{lVCk.YO/|E?}>2?ٿOC6S%+g/k,=2۹{(365XV/klQZÆAo+W{Fj%XzCy/T2;8m~l]s (=զG-Z|v d=ݳS͞.W7)`X㱁fP=?30x6q,'i%Q3h=-s8%+UPl|5ay pu6li|Ӿ EJmkXIv_hܫF <a9ۙCEZs:M9@*i|ϳ" +B]rw}{y$Z*mvg>|`,16Se@IM%Iu ntbs|F7ۣ+n\_嬮 yYygCUIͦ>+]ir`.ėѣH?yQ+Uf>Y}=Roݍry;8ꜫ{%gqźkoGD׆YqtXNU=#砫ſ_>Uu@" {Fi1lh'{88_M:h>  ūR?9XW𵥜_o^cZ4 >yZs5zsBޢ8-sƅu|5-4y't<;##(]>z^F.mmc\K=b([_h=3,*%= {'ɏ13&Ĕ\nyN9ܢΝ⹦RW鯮,pM䣍>3\'w|*7OfLh!45J<=6NYU_{(1![@46`{4?6|,I aW}fc̊M}g"b{Iџh5u6 ]*mxE1Fm=}<Q**v&rUhʇs%`Oqr戼3 ~`C%hAS*S 1{΍0h,wɱz&Q}8(>M2 `ϵSoe)e;87^Vl#|8HǩPUriʏ Op.l|v>c 'V_'%< {ry7S5;;%1oI\96;z{jNAQē;@tIE{7lI4]0r-P;#YZlw1_5xykzz7q|sݖ'V϶"9YD7#OB)Ԙ9geujn+}U;ec,.~< *26hl2 w "?_9=|iS#~|L?9?v|%!W SFlE3^7g!mf2M.jGG`N3y-K`d`Bt\\dQoԅqBX})۳s+nePFċbfHBMZZ{~rYB<$VϜ4iLcɐPÛylJG~MǍzI_6myt-){>*G)doys4O2H\v_gSI;Dk Ц cxe+.2w"5t /jتkL 99j*=IG1÷O֧)8l3ĩpk%i-\&Yۖ'W*:Zzf,|giD] H77kMvHb`VM"QS`>;gLQ>p٧k :t05ս9VM6'i]3ƵyR~IOuL++AC._ UqpZ6J{ v}{{+Cͽ3#/BJo,v7Qoݭs"mc])Э5Z,oEuǕK]\Tu>{ L| o&*K>( ~Ul/p/4;g9\uNި3m꛿X(#hiLAIM\? ;?xlI~ɱ(SыM~w5TW}a #v0OYq3yݵ? UJ ̽,&zrԒj̾gsCdk۾n黏a8^(S7j;ǞlH΁XD _9 w)TO̅6:&Gn0;>1zF77S?K֏ fO񅛍M4>VAMS0t$X/BX|7|Zo'lj_>"Lk56#V(/|.#e.8|]:IX`Ịis-'~JCzhǗy9?b9f h|ϡ[M O'{ʲTq='|JAG ߻Y#A\JװpPqtST} ;|RtŌE^q]th;m4okp~*Zv'>Ggl /y7Df1|,}2G26ofZfv\T|ۄm,je /O Βc(wyXBLЁE/^⟢Sd/aPOmY;$_s l89:}.gO..@m\UV. R`1 `}-61{T!:s\=IIM8蓅sѩؑB`]}6 F~ay݁Y7Ѿ,}z˗j%}cDLӟF*<>-7e0v*'*IJ/`'Xn]?!i,^f9bYm)/u Cgw>> V a^}"pס|Ы0 >}ץXc4TJk2=ZJet7`;=WzC5jZk{N 7NoCscND6¤cs3|ĹYW7a `Lg[Æ8|5$k X)l)~@k"ujݡXLj1IďƭugNxD | ^$/i*ڿӮ37~HcPG~xG+nTWnb', O+k3 ׍r8r#i%#k~]HkZvg}鷳J[ݿRn~U-StqPWR\~sG*gß2,ťRq@eoUɁUh֠\V0)h>EZf | H 56aYe[pG4e4 ef> "MWJ88OAk9?S2z0Gs6J"Ez89.n<']ES;[F!iĨ3ʎq{f%>qɹƼ?[fbљĪ.o M}թ?`͘>tu[*TC>w?_Ha-5UݞѣnWd٪= ׫N~Rj9X4u{r"0;q[jh#C^.)ؐ\r򳩁#Oz_#0?^G3c1=q^힜S(Bh]{#ktCm R>_4mvt2.,(;ŗ2@LYmL5-ZN?@ǃP@|m=a `$@=s:cǾgֵhS3"=͟N_VOt}.S 4eGbj޴݆eZdTs/S g讀Z1Lk%r<#xv]u<H(=,Rmpd:Z5Y*/bNpsq6sMγ:֐O=j%u>0:'7—ؿO]mW Lh3 ?6p0$? &Ͽ>6:qa.sP<ӞƪOUCeoH?.^~;a}7zvQɝz*wD9QnuF<XـPfѴq RONvs>QeYOɭܮSZ)ܟma#wf,o_A(8{Z#â;x|jMz6` MИ/P>P9uPf:cϕ{6féQ~Mo }˜p|ӊz̟SݲvVG0OI;>RyIθF|c IYysY ZrܧxT~^M63:BdnFm]=.CLs㙸FjOv>3E/QhMܧ{6-vE{?~k AE&Nk5Vq|ܼqT3LZ3/EuwR j{gqϟ\[BuOV#{yӶs>sOC`|J-Ť;'EڇXͳAZ]wq),=3 pmo&F= nݛ+zbC" sz na?YV[fc"drZ# 'Ij[nO@aNJf+!֖ųg}Wƈ9Y2wfb7{v[xRMW} OGT2MT;+9`dA4hجh/:|sF { Tv#C&BY}V0w#h.2L_$gMϰL$Iu7blހݏ{Mo;6]Ѐ>S"3.5ʚ1EWƱY|N&d"d07 C pkO:"o:P74J Vք+:7ϖkd}sL+O_߿z6i5v'85=_+Q2*[gCHٌN@np\³w8j߱+c<r dՆ.[g o,x`'6 h}<]>TY!]ߦY}/0 \1H\ιﺿMsS,bP "6w85bu;Ì&؈Ck͛cЙPE a~1N?œ( vcm=0 r^>нީ$Zn_IK9e9R\j2G H {50ĸ|.qߝ;M$8l~foMW' 期 Nx[Uvh a&;]@Yrp?G=,Zzss7j"İaΆJ+r#JRDPLOJo+?;U#>GApYvZm- ::+ C~,n~tXz8PȺM`F۱+Q 4](nHZr" )yM'EG5FW:5X{DΖ4d+c8 \Z|cmԲɸGgϫ [ L^BOMv VL6EfmO@211acW7[\T |QuFjH/09Ia|x^驃t}/m֟H|+υ @PP~\%ExeEˋLӱ_D5Z=6]jx6kVh, r?Ѿܫ@pBgLurc1КF^#OSs2s }X^񇘟 AFq%`ˁ w7Xp]5&Ĝ:dTa.eF9=/Etp|\dD枇vHsl`U(b#1۵&3X|vNGfzD -\ :}|B<1h5T.C Q9Q$ƴx!e\0~~>a䣟w\?+öϪ;`}9)eޮA$5T^O/W:߱ocٍz7r g8>AQ0b|vH';|X{fy5lg~8> zXr)y°:&NѨ |S:_v&!'Lr|o]cӀ(Vȃ{c>㷎/]BL IV|}haH$t-SJwte۫;6խ5^5̑,0]AA?l@_m՛q 5ݱDwGG?F|ֻ M tƪb6@ :xLlC~?Q\bZMK 3Wr+qrΈ~| 9^VI[Ҵȅu5Vj{.}Z:w>,5,he~ }BWK)3 C$[u #Pސz_r8[z?Wj:̟?젇C<# v~pt>ij58mQn.|Dal|֜uJ=8'' &d=WC̅لIOԝkő,z&BĖb7Weu%ry؏/|yޝn/3K`j~M&tSǭzyo?ZXXL{# nZ1Am<9N->10'w@cLHMYt7GTš"k)a(az{e?y=Aoҟ~!eqiMw֋tC3s<,cWk>g_ :C? okb +{ sooxzP|B@?foנ=L{~QF{{ZQo 7q7bjhigVG:b/;[|$Й{ܪJ|sZფDqЦ4@bN f!u72Tt[U'̧YAƬ̱iA!}4>IspVҴ•Z L79[HWКf@IDAT+޽n?XEܢ#5TeV|a$[A-#/b/^ |%= 5Ep_̍@jE4ضl .iSw}ib}7鹌̈]"$ ގ6]3v#L{<M0Cھf,IIc-r<M6+s6zY2h@Bg>)tk΁kY8yڳ}r/-+qVQ-[_&YS] l@3/ O T>e|d`P:kVF~D0_E)S8f~F=L6Oh7'DiRLʃ)5} 1:!KYQjgi SD_!5S8|WS s`OlVJQt<7ʒӲ yNj-sO蠷8Ng4Utg ?}k|B 8I!̊3O*dk3+x 2ջXp XWH`g~:t0@5lxOx0kg4ḽ/.ڬn`h_{P|XZ@OyѾ)(3-J5=HZ=`Jh?VoY?q@8ՋW:^1YbdqྲྀS341ZCȬi%rK r-. ɷL,Yo;t5vSݞhfNfBMdU9*t08wMp|":|8 ?u|(&ֈ@ƍR(\6獏::MX+acZʞt^9ZvfwMIR3Xz(kA 'JB={lj>jy<-k`;Qjny36cQpuPQDbbX2VeY;mth=22 Κ`P?>2_Q: M 8|l !;󧆅<]kRXAy+]]'N|b>X2( Y8s^*dgԜÔ"eC"Ϭ@|{J5bbڽI;jǿN[K9fgeMiv{"&Kk5 Z0~A4&ػg{f5}kjN72R%VI0aǀ61@@-'jMO`jtt3β*f1lԒZmM 8[ʳq:&\9gBkZܵc> zâ!o{nAkЉp Vz@?Po} Zk* (5=$z[u]K@' ?F }6ݦ;G|RD HO]&:Y""qIoyHlv tf{߱g/1ᷘt^31B, 49 X~ު? Q~5;^Rk?)s[2ӋmP]E= G]{csF-@>ϯU8}OV)hOxPumnanUsn}jv#2.](,mD?Qnpg~o\D[n:mSA0 xD!dpcÀM|N}8nk*g+iTjzJq6.?t@ Vc- 9Zd2p҅FDw>g;Z 2mЧ/'gO-b~,3b8%DM`m_D/5-w?\Z'{G:Z'I>ri[3Rh/Bzu@if?&G>[.Sơʍ5@3c/9Il$+ԲE[z)o);f3Mq6nWpMeop?hCF+5ga +36qŮԋ\vNT{2\a8:ffO5͝ gp‰TB t>8F ?? rwgUΩSog)87_'18ǜM"j71;dR8-HRuiJ7y\gMΑ2~E鞀GڳhW>9+8֚ViOsreĘ!R.r4~~{.Gt*pЖYǃqA}⍬+) t@c3˩y8XA=geb04iٴ>8qtR+R?D3 xiay;/Y-<m4 .1+ݬmuI}p=(g( ̼J|>~ןՏOy.զ1*iоWλ<"6_O~G^O|#\b[~zzлk['S^;?R}_0rTҙ Ͻ_^W]Ÿhz~__:O_)#zSex! GN(#/S]m SŏqP*Inߟxн=rAZ.r Wg–P'$PGx'͕ 6GB~f eTf|D‚ ]ߋoh3AJHF5#c2@ɞ[|}5M=9^0oR]5͋vlZ qfsNzJ+ՓNJ||T:3%]tiw:1%g3 [:ǩ2_9н;"sڥ>S~dMZƎV Z'Z;~p}yԉ1a翨\o-cv "j+/pb+ Lj"rypJq;yO D̺6Apè/03Dčk3\w +}MH(' aHD~C-)| IEPo=,[]w= (b%n^2)5bn /^G]_V7/b"{wG2Gz}ŏzk~~iW~?lZq;}t~'$n*з{ksyp}>z+ׯ's^ޣB /$ΈuiIa%?vo{#~}s\ >qId??r|ؿySDA~?:(s',3? V8=}?^0!J}N(7 h“!#"Ә҄AsV ΒUQzY#[{Ebp.rHn 'hҌt~2/#J.9- |z۞[Ma+){G>{B?a/9G}B`"=[4`+^0I;x 5@l%;;h8^uL#ˈ i-S!YO!]$q0Rd= ՅB,kg';&CgmfaS= YL Y=*^Q'<~(g\t~⃒h]sAm<8tphzK= vgf>]Qqv^b2:3),m~Dn:O `osBK{jsV7 M Xx& }4+@M`8D~E5*gD0iկ5M\J ӫb?huѿr Zz1֟J٬EfegUC T?€㊑GoAރuaJ\7qI~<ɳSϳ,Vw>TCNP *$K*v6 vCkG ko-Dt'bYW,b\C2jgkg0l3{)GQvgB[0]|~lc<5CB| h~3t N\}3=bfh+x.5i#a|:z6!|hfjH! 9B~_ gjA2GLwg| q BR4'Mu63gݱVLh1(`hl+5axOM8[_4ƀtpZ6 FD+&zd]<97=ZDkp$ _Դ[z l?ꥀfA?М (O$h7=Ih>6&j{IksvpLIdߢ"S793ЮU9\wΈZS典OL7 pc +g{֘gr@tX\wcW9~:`#(51`"T+'A5}[~<#.>r=rS}3?SCrs]}G_E<Br8V88:AZp__w}_}}{#߽d%LEi> 4bۋ}$u(Tʉ=O-gsycן>w<ݿׇ?1׳\/1p<. Կ3zɏp# Cz?zG>z~%_tH9fȰqLp.iSYk>̀Z4 q>1bvyIΫ<֎ 80/wy")8>S^{YeHbZނ +^kĊ/Bu.CǷ|<4w%&O=>i/ˑ6[}:8Y9Kв7}z&]~>))bGXE!pѫ`f_h-3s:'XpN>zDK8F+s sʙlcIyiU)g CsA>6Ƌ#L5Cgx]~⪻6|G|ԝ4B*]g}gΑp4זͤ!֪0n߼S};DյXu|>x<>}8d͵AV]4se}ypʆwr2G|/x.춬׳Q tK夠y?9h < Dh̏(SvL H#0@4Dϰӯ33lrv}>XB5}ZI>[c ?M綁>4E4P=P/+=p[ LuIs[Gޏ1OYGu2?o ¹kTL>i?xsg`濠G/%?ʺ|ث:꺼㉄#e=(is۽n5|j7)nH-;e7+o}ٵ7g/db *wB) (?O>oٷg_~R3i97˓=>>>?z{xy >w]YD;M}5>i?,@aN|~/Kׯ#;$}wC BR^NIg_i5"qOhMs/{:SO s"9u(S&r('/D&H=,6 nKͦ> ) e=7X>fqZ?u]4u:3p5ƽ5f$I:u 0^/!a7/UgKZ1ț;[}efr̺|iZ @3nb=t%Z|V}}GMm$\'q2DNbb:|~j^t8ͳ -0(s3b g nǀ ݖnω8]iN)IJqRk**J,p9Ogbu1{iQ5Ϡg!lC!suѧ5`ˌTjk/Ӏ,FHS 攚*IAqWAw`>y< ;ѣC9!<ډ]7,+\ .b88ĈvPK&:B`-V#}n#5bR痺.w1-h>:+}He."b{~Q9MN&a}sS#ϜH X5* P坏9 ;f\/8&:Ӛx`FFF8ppNA)Nj.~:p=S1Z( a̅>ŁVˋ6O)}a-}g\_oƿm|zN~B?y-w b?ˬжjtU)<+&f4x}MS'Kox5/AܪgN)fEѼ75s*FᅳA5ۿ[?fK[}nfMZq~-#3?jJ~Kǿ׻x`<'y+ߵ|>}waJy;qs?*h>tAC(iqx\hL ,|fu+T +)8ہ#>9x%D20oRG~E}@f&/JxEv~]Y:*t+-_1"sǷ>>}MQ^O;Xێj  gz'G2;2u(s EGRCWu׻]Po,CPo|0^S_ iOz=A ᭼緍=ܗ ٓn߹st= ?$o<__؞Kp#bjӞU7cB}I_? te{\Ⳟ=-wE޾f|c?9yOY{$OǮ]ߧg~ן?<{6zY>^]ay?o}^N;b=?!K'^ y()wX)5kf7|)?x=>~,?)tQAf+Q~unNX>44ww _4ȷ,$v.6u„G[V ?o#Pڭ5ӼKu> }5Gp[A57.uI|A[ShOb'!|)2WL.geJowF9Ep̝V4S3ӓNsk@ڿ$S?ci7,zwk1uU7;iCm>=Oi3+B!TR'XtnzjtcZgg:WטugS?YGXl`f7:K`m€؁uYs/_3 jvN, 5+<_vF8tr㹶t3N_'RxhUގ/-HRU=?<\}#塮H _pLxS@si5Z\jڽ1B_#D}bɱhL-.']%+S"[]/Vzֳ]^ D2< Y¹S+yޙ_h hU\]ݎA#vepI{?\1:Wݮ>zmqF?'e׷'+~upkhG^_iO[/%/>3?A5^O?GP]73owW g't=1'E̵FsW$ $l;ܹ<πK땃o(΁OKnK`t*bpZCC5wrx[Rkn\z!hV1Z3pZ 0Aj˾̉]c4#s!-i,5@.ֈ7%&wۿ)~$tG3ѧO ~GtgR-Zp|+J2Oygֻ*j|Mqs!Va|W'UA0ӑ]nŜ'!M,Ӧud KU~Ɲ+ꓡ6@Q`*XAV^Px貄KhgR<*>~6%lbB0Xܪj?fAS\`ߊ*B:Zy@~kjf?k: dm u>{ C !gPi eW0_D)FZǍԋ?\>l์Sr6d/]n`9PB[ #|je>+ř_fFR7 qW_[mL~G?ٌ.:Oa+o?WƟ:M<]g_VyUONo/M7R?1 ݬ߮_;VUG,W[] =UX^G{ 3gcs{K_y5?A/w_s)_~z~٥`?>zwm}W|Z#oƂfߕHKr(;|<^8T#a8lxL gotY\0pyncTz 1.qWFF o=mުj@q0r~ۇ 30lkx?]X*Jf,T:S }H1dJo"n{O2l1̭VxNGL ;{mci>:1OOyV+o`^uϘy5ͩ *^,$gqZhGlOR⮘},/0'QD_3:1`bׄ{s1Ԛyr)f{H#Xʯznѵ&M^tNS֚u^Q7^^}ؒ?yFeju;?r+><Ю hUC]: /m¯ΪY־#g{\)w úJ^~f3W}D;ȬAϳQ] FJ^+:ぴuPI-TJؙ;)gL sPgSa[fodBghv0q#Ig w>LGFWr27sb]~3Yڔ6 ~Z+Rpg1I]_)mO}ay__}~™<,5^D[_~}~_m~K=$7ً_oW+>|Mq6?d?\R_ߧ_e~A܇_Hr|WWAt) _,o{z+Z׃GC?Rx4m'w=;%|}=?= oz~+ק鷹xrxkŽ{?샯[}5??="/n_0uF%G}䓯?$|G_q}7~6\69^"x fF/T4&)ϨDi-/-]e8\~|OFwZQH}fUa$V:(ev1̪P (8 Ď߸߫sxρpzYmK~$:qٖ/7+G[?ϯ`PSyzM! /Y;.]l|pȜ=@|w̻%^Ŧx]z&+|QTf~T(}ӫQMu16dYW}Su d)&x I^>7ZZβEMDS2}ofU*)64pG=K N.:hꞫͳW9[)QcR,t7MOEn2|3W::Q"}9z`Q7ƻ;댴?wZ+d Â={0Ϸq-^M?uQ=] U2* HFHa.D3>/2'J[kS1!<ٵU+?,r>B[aPS(q:kg)O3h9J10 9 ROɈeZkiY Ӟ0xS,=E?ydU1&TVi9{m0|z=NȺyVH "=rcqO}{}~R;w y=G_.:4&#q+?/ ohb?^[2勵_ۣ{/s'?RrO{K+_?.~QˮoW?x}=9|6hA>IC7?Kgli6?wޏ]ZOo ^" uAz7^~\_G~C}^g~<9r'^:wͽZ׻>9}pmF_7^]セ;;v3=~,4ۯ"ӿ{v#20#dy YC ghGH?)G$:61+sIYِxXFXa>IƴN!?4Zb *8(}Pmd+Ӽk9;mq3`\KNH>+kj )cw"n Zp`D$ hfj>Dk#4jq/uom{V?2HeL a`!@Q T@ġեVkmvYQ2q,IQ &!) C)2$d Cok?Ky9s׵u=9w3M19-:o2Nu3D"r=)q#1a("F/kȤ#~Xf ٜfx٥=K6yhǜ->B-y4_,! 湶'ĥ8}̓ERli$xf,n&kO^Z|qeۚ`e(6h@= Wvy\ܩ`Nҳu)ij%?3Dtjm&vrDk{$G8{RCW=f&^2*(=sy4w Χg€s3x>Q?t'.S[I6xm7IxJwrg__:] ~'e<|̇\Werw\Oڏs?ϐ3Np .'|hI~}}/ÿx{#f.:}K}^x/wQl5e{#y7P5M饙[ΨaWs:k`着48!1B~@|,"@a$j(C[~f4%9)EG5t?gs򈌜w7A i"8Q_gvhtF5NZèbttF%JOSfv_THEVB-fg?~6fG"R{ KZs8k9MoL x2!?):`P!M1}:G9:Jw\sO5|7Fr>)XyGc /IՏT\޳u1cuq}e= gɌ2VH0^IˉXHӛvX#lNPf/9@IDATE|?EW,tmXki`1ǙtCEɩH?Jx699aY[3m3|MyI%WBx+hE?u{)YxWf~|h 5gRS hAZwpT(x.3-5j19cBE?}&meSDqv#vY4yʱfܣAU_쏟z`]֕\{O}x! 3:ӣ>~K>ğ|C<^W_~_9u-}Ѳo~SO*}?n;}R?|w6q׻\>a/{kK~s7+(}y~k8t̷q\}DgoOyZ.VZP8xpǷ>1KSƶƗ}ۓ.OvKt~<t?]sz~H-I7:26N~B"kZ9PsOKJH6$'cOh7[<8,1gD0RS ļ;0'%3#mO>܈Eae`96{| Yf~N[>jNU*ȦHFg㈖W`2&ǍOZQ}Tl]-hܨ쒈9|͌ck\H#Pk7-^=墕k'ltg61]]sfl БO7pj[%'rD^8z73ulaP!-֚oOo003(_j +azf—jdžeRuO>E+gN2G0 ě`/aW\Gz՟G=}ſ<|d,h`FEW J̶˜PˇT_XN}]0[Aވq4A!a]@I9w2xoIY#m4|x+Z@ɋg4>fjc; v?N@iu]1sLM]RtMHooΏ{^P&᨞TKHto]p(,,9ES!w,<]mM_W7?~S-աªS5+:~VO9u֪cYbrh] ~f# $y$9n>ybv{ > O7{MKi&ˇs5PT6Wн1{ْwō2B_y3"cu |-Wѥ؋l\"=t|L{0t:3c +=!]GX/5ol] uŷt\UAd}rW>g[~?gW'j t΋Tn"4E\^7\$I9$O>[`~u9rUr3_?|bշbl$|$A:rY\phj́&_W2$.mq6r~K_0al:|FYʀ̬%}?S__P7w8g~f^#v{SV1_l$A41k8wcv%љ!#ăbS4AÀS;oӱ+Vd"o|94'OMR)լ}!Io fjFh< Fha(820כJR58];1`6x]K8}.䯵D5hPcnkAVy!JX1NH9e&jtC׵OQXQt}HnOւ/Kg."uI v3ĂԹ @t1}sPd]X*g^?%$;x MWgAGkMb5>S%m7r_)injԲs7}pY(@ k>}0.aPke~ ?$zxz\f?jZ!$M8ѲO6ޗ>1"ft'WKt9KWkm0}?3˧<{"ḻ\~fCL{fqwz=^LKs+ -ZGNO߱7O^^+jΔ_v|>σz?O?*5 oWxk^ߗQW.ߦf~E׼NqƷ/t~Qh<~0ͧ6JS)]D)`St|ুTqhGal!`-$Df(΋p45Ut8 ,Rxg몺zd{!bv+cavߵ|B҅fWVC'.>{q}懽CbN~N<>=}y[}%[\Z{<}7s?rwsy,{#]8x,ZϨrˇ<=.OL:xד535^}?_]R?RӕZb< OW7vIaZ߽A'<֘AE>ן}Y:]4&t<0|4kOJǵCPou1T ʼH= |cu 5C>!jEN@rƏu4+Uz^"»j-b>&vu6ӷ-Y&c2qZ Llxi}k#$uuV} ЉsY9^Scgbe?'ܙq7IMkgoKU=m%zhd9C#Yu:"Dirn;>ظ)uADwk940gM٣=|jƉi椝ٴ WD&XpùTZa~a1Ӆ=A7d7l U1LFZ'̘ohPyFGYqm~_$3TZʻ[d.řpAbƱEzw=0+:}Ul[O9Zv&`_qou̇OZWc&;?(hxh#G^}g' 1FE\=N@CkWWkp__s:3E|?>W67 NzW2WB(hN%`k#aF?l%ݹB2:bhG83_o W*zu=`з!؞? D6YV~O-_QpB_ֳ}_Gzv`-ŗ9P!lUP:7؜n91 N8g茐ic=@5N\И7Sg2E]ttق 6I1"%$TcKGy3VOiZVla7$$9fx6M?0=.C gւy'Q`D"xeJqN^G&31D gͼ4iW<]%꺩H^&UjɦYpGB`R/̀Pu-iJވH}ò=P4xjz^RTGSYs^|GG=W. '>3N#sa#H\X8A*uz-@W9Oy aT(Y-SPb๙=h ;E=sEBvCcy-)H$>?8 p:y~d\$XZ^obC?hx=aA}x|܃/CNߥOT1O sč_+ht~?G~gDy| ˷iǗ Ҿ ^xy~WHs+7A}h`0[7k~ =ZÆ-͏ީ8O}X2pgTP>XǟXS96i, bedXDKƨ~ 7 0"+Ϧ5Ye٣ҲK;issP綼ECik{r&f`@EBR77$׉ ?~YbzKTlwcހT]9a>Gkg lgRɦWu^qMK餽gh՝G譑h06:*{C_9]f5荈AOĒKF¬w5X+=v y;@]{O~ DJ*?]̟A\RO:ڞ% d5wQ}QJ8d,60p.P5 кckCɛ f6t? *dNNxzQԇӝsoعyfu}86+<M%,hO0y=yt+5xqXp]G|Vw53y2=@?[=^I h0#R^w}\i/=|-Σ7kgľc>t?Z>֟|%o/_yy{~K}.iw)7ko̷Gܚ?Wml><x[A'8]ggz*WѠCepNr:Zk"9[3Z՛r.azӺ:n3h7Խ_?1/ YSKdddɍh?`1GSu]^S#g VNió8[s;h\ɳL"dӍV&LW,WZc?'w;nKKpnM4о}p&=G,jf>hTmTUjLT{L_y@ %i񖿩gc|x: \&C Yx"^L$JY6Ze[2fF۔CfgS'3>~ӳ5xbHԍe4J/@j.Ms\efo4.#:|I+Dk k;ĝtK#;_25I.^c[g=Ÿz}Y_f;ܫnCw{;]^.p^>a;臬x~R?A+ǗzIǢDhlOL~I7?FΌOd?+c.>/;O?{~1˔Le|*&m}|S뾗w9;~7Tr۵?zy>Y:k|w}{\_臼n9/|c/|'Vw)>hqh:IJNWT}׃KONIuǨhk&G3tA~5V+-XlcGK/h ?箔3UfgsÏK}!^i ><3ЙS~Wوz<,Rt 4 l/nx*\돮ɕ{qBdV ̜3#{$#Fp^ /;޷\ຎ={ `V}'YHmd[=T:'?"ݘkTR[߅E3y8^.}+kNძW)?hۙuI=-vm_U1Wu "É,<%$03̽>ݟiKX"YSv袩v|"yS |g%VX냲,qט;$ɇG.ڀz,r@&ݭӥ>֟Gj&O`fƬkUwR:5gcy@t.[0a{ϲ\chUw??;᫺prL]I9TM>>tuht4^ٹquM19{>9lGw}ub_1v8~/;U9+QѬ9]g|Yj|,=(3.B,5yeVzz~?Icߞ?CyCWóI\O|~o@7\U߭rm|y~)ړݡ_~w\OƧ=rs׆&O=?r[y ?uw)Ysf! mO x+W_w_x/O:B+fq_):voNO<|.~FSzs:O ~bρ<]1+G<.XѓLpx]P7Ocup g-ʿ,R^9*@ue*WӸiqSO]KBk2zK^qEy6|>|%P P]TJ1 k'Eg0wĹ1:Y WB]s<x,fOI :Yްo[#eJ\W*k.%2=*_m0{"&S \^~{bWO k7Chki'ȔKʡFzz;0jyYd$#c8v;{ÿ4˯lƳicл=#mT.ui\NÜ qX@<]obh.P =Zsҡ8ކ/5޶ScL^0KeÏIN<>שK#p~s>~[:V? fSm:<>#&qɗ>顏U䘟JTJ}+ۉ cL2E\q>NZLORĉ'wÎEqb:^V P>/ap-KK?Wssz^Q_AfZWk,)y[羚OU_]^z~E߮Gϗ?Q=W'=A!gqԛ/y/_zy2}y/yg^oǏu=xw|"ϼ8أ/K-/W]^|KzFz?o>) '}7^^]AΐRg9 b)NNX?kUqao6j&oa9 ?'<5vtgܤPG]yTGsCg#i7k`c`?~-@dLxGtOdV=/۬s>9tL^ʷQdo۴49|b(|`ߘڛ&WG;jnCS:60z)dI 0B>k $Z, 0XItFՎ[u$%\N ߦ&6d̞?3?F_^CL׾ֳ L(&S^7˚ vpk* jV}{6p<~t]j;(_dGgct[Zw&;zXn=լ~Be{˞~4Cte¯\1] \!P'&!`|Ȩe,0X6Aj]\ScyUh`R&5GkNjPIrZtXɹb fQ'c Z$ (GgOC'XG2eumOcq8҅GZ:z;\^+ j8dw=zV6#Ǒdt@t!voD "+8\a|N>v"D otU^Y)"+@6Vqbtφ>r%ѥcj0Sw=#H឴L@A5l kF}:@m ^Ԧ(-ϏMZ ]a/Dž;Ϙ30<{UW8'##JzkRr3r4zNJopx*+Fgc]S%Oo,&g=GV8lOh GffR1zC4p ^ Z#ܣjr0f:G]J"SP\;OK03vn0p0钥sS^ =52sae{VNpWS-f9G}Z純y43tRPcs'4.< gLt|㺽#,"k(tSIr4WK`:^]B~`U^+7s jP‡)ϯ+M-ۀ^kM7|bNDxG- [&}i7uOy&gs>VGo]8ņgz9dXQk)y/j֕e?xAN'eXpz, h21bjO G>j$9XҞ6`ZmHPneRb<,>OrV`*?u`SpAOz ֔}pzLL &U nS'fFS#]L=e I\╝EfZ8q:[HzfT+k&u^*J㨮_I1{:ִ*:\Oti]-fcŤGId,J9:p#{dvs~nɅ'sM-tuƋzj ӗ-~!`8dYnWt+<:¦P8 '78z&JF؀}Xԫ[L=̳J4J Z$LTo^֭=ަPUL;gls^sҡXkQZԅٮG~}#^H[ :Sbƌ „&t5$f&5u=ܷ` |m^ MǤsb>ގC6C&oYV>BV:|9TVCZk*SlMAj|k/ݿrHOEXgQH(7][#Bx]=Ovs>{q:~<9p¯¿)?zy-?3s><bFz8r=:^Z55l"r|tqԷM њ.?0~I]CZGA ɾ{j/&gF).XJ̰&YyDLe8[ =#lQPd zA}a!#?}4Q7Z 9װrjKa:MW!yIt8}5KE W6GjzHꠂ}6l)ja^.,>c@ _sv[NA%,^F\=֞U ύ{>]G~Mh[/m ZhRT1#ǧҀiۿ pc9"peͳB}%pD >g9݃"1z.eo^SS$3bSpxʼnXv/ @g7Mt/ D=UaTNd3nQcu6^5ܾV'E!+?~=+3]s,8y[뜈s{&ZZXRA9k^TxW@65=!]ģ=tPQ=/ѵl/HarjS{®›(FF OT)r:S<ʳDnސx\H6gm0g9݊qƓ{@uK`ңo&z-芁TSɭ?R}n;+.rBw/kvyk^{y_BK{\E(/|?˝ߊOG}y+_[{/νcn2יe8N5sک@{9) mZa!h4w?ů5?x.Zv~-W8Z)R>E=c]wv[)T>L8{z\;?GmA1:?ˆNh\ۀk7홢7ߚT~$Qg~9ؽREUiB?\C0 ^[x8ǠhMŐF6~*@4:IZ+v]ڊr\ޱY|@MFJ2QG5^k'[<+a@9@PQP9~mXTP@PrGha e:֮q z >9앚6k>zcݘ{|?8V5E*ncnb -S2Cb+i15u8Í4>YZQX$sNk$/d <.sHGXEZK[\d\lBGS1@IDATkpJ:2^'ٕևD gRVetAˍjj lNRs\%Дsc_˜H(s`8VOhPd\2̓#λNܔWˀ ut@g X[% HgV]VF㨁ֹw>LgWnYeKkSf  ˣ'ZZUW^ wo~[V "ϸ?ޛzPGWx'9^O}i(Xr]4\ϣ5;Vq_~ݕ6i_xZNg=.&-'+䤔/rVэj3z59tt-m7~qW+ư4V 3Iz L|Es4&~zc=oPjtDbaj' ׊+=_ÆJQyjr žU`CC '۝|YkO،pN#qvnѰ{gͰzOhjJefTI/i/ȁ0#>kGVkCljL(. sh}ubj-GglˉkFZyOt1szOLK%tU@g0U1IHz<!Yc_▢3;y :w7>&:,ѻƻn5ıRaɋfv+:YmLMAŲ=^z˥53ALC"7} 0V8,sA-]nsZ^Z241 W&>PFş1$(s]rO-e1DǎbHp)_3քg"JXǜ|U'q%>OaƿwMxs"O<vMa?FStWc1JR&igH-gN˷j%f!fC7 NҖhyQ\n}4U>We XZ?TVQmý'/?*jJu~)FO3;4Z|T½\ ΍hڀ.3GK/S1qVG-TG\>S\w.; ߢg??K>i$_YNw۽wӟuyӟo5|->.?I6Π2Δ0Rˆp:ekO(7kKZQMB8cM(DCwpv6F'H y-yu2oX}k9QSnqpI;ZLjA*̪?>m.^|PRy[ ͵pouqG_BSx`SG(_5*[Ί-`q;Gڦ?zBҙyCM({EyԶmHQwN(M%{Q#&nc#vY` cx 3 |p0+$;6kѹZ/_B]L#;ç1 &XQxn=B>"PQx> 7Jhi?kMSo|ëԕ]KgσR亥d? q hiZ{9p0nKIXp"52t%5H)>k[]^idg`@#ł<#MBJB*| &kc{rh{? #nuBd=? zfFQ;aI^p/-jɰ|u4e@a{ؒڜjcpV@ k>V/rpHM՗{bneާqeMOѢon,dDx>܈h9M_Bo|:ǡtҰ o6ѡ!SI,L#G߈Xx`:bdjϝw`*1:rXy H v[ ]glufgN],u'k?3oebf+`tKn͌\.|bNp󬟻淎3WSXj.E[ V3s6+r7߷ӫcAHX{dD-<X1<[ c*c?^ *ęQ& F]/ -G75T)39P5Y+ $hVľc2ƌ4$皣"-3%m%6tMmf0(Z{42n.^~z.y?7w*Bt{FQdJ÷Ocʾg[`|lI9ʢ_| o}H=VbK̆oMi ?x$P~ s } o e.|ꮚCP9-&Þ=6@rV:AO t"? Kxp,TR1+ [ bJg0|'ٌ,/p. bь,dOò, yz~2_jUY iA+M@|S1\R-vdF˓s['+rZF gm%F/?PLD5f=)(G_VrqdJ:V_^o"c jrOjYEes&5?)t(e!G!ڮ8pFK c9ѿw NJo)o2Z/'/^>ky)Ǜ_e_?Ɏ4:wlR.i B?O,y/O|D-6S |kd~O׋ 2d%;8YaOSKM2xA  R3[+lеrN$Y* M$lF-pA2 M(}ܘ膵X)b) {:ϵSnøs_į"DnfZk8N{KONt}zV,=+y\ݓ! <%? .Ǩޢ)Xl)6laY5=Kw~wyNKr q<'M-)gi{: UkL#p`QlRGhj voA\l]繉>?~EڒvFm߃@b ^ugg$tWnL#Ktzj)n%#P1>_b_ۇkG_x#q=ӗOjPl`-uƞ.pI``3~2⌬3K"s"3ԧ1"$wZDeߏL6p@c./խK*,?7z@"Bwx ?^FJ@P\O(zOtߓrM 6Gs;\/${5#k  _vkmnw\Fat a` ^ \ڷ @ǭ_cWCq~3Ղ5j`5>/t>8#?3=vcZ96K>5ڧO|4,u4ӕ@t968ndK7uxF)4X{D> kBBUg.o<]u)TG~4P3rJZ0+μ[P^yah Z' >XVԚݵ& V=?x:s>#>}/mqKv[Fww}ul|=.߶SZc)l:aNkfom%^}d>׾{\^J>R=Yۺ- Y) NoXOCBo2f;9gͮ~) u)q>DV>t>2CoU~iL_#$ )psgӯϛkVRf~[?„Ӻx#2V=zྞehTTv^]d*+]+h pК< }:`bG3ཻ^C:uè'yy հNhzR}?='ƻI,rO޵C3tkIٜEWA'C0jӞ14=%.VI{%4kz>?J!*>y~K$ ŞMwsjٱµFgO5}cofᆀ,ȤR#6AY 2Fr7> MGTfck8^M0r`WsvB jujypo'08%.-|>G{i^< $IJIop[yFno"_sU9iV7 : px>cý ʧ#p>=O?g6~289,^_|ƴtPZ/?F1b!̱Ml]os̖Po?_g0)E#DZL‡(Hnʇd6CPSʜ>hgo~+qEE o 5YlýCRGr>e-|Kl%'ѻ38b<07F !s>SF[DitvqM라<䆪K {\ phB6y8GzZ{6ar &qsg^sjbdH8PLL,z}VFS2qm}MyYVzz+hsoћK=˪* \DX$ D(Jk#IiP1+ (lF%J"X9X @SAEN?Ɯk}~޺֜c9\{ťx1ލ4kc U>]B^wHpR>\,|m| L8 MU7W]o^?QJc.^sE W㒥1sWb%t[$?5 CVү]K,ˎͨOUpʙ~/ʼ9}:}c*>sW9=40]3Nd䜯vylG> 365}§r5 zUs956 t?"ogĒCW|X}# #Vho1Jj Ww߅79$YW|rU~ )ճ08~{.MnDʫ}F.ÆGb`9p*Yac}:KN?rfVfKLX (?o1 npS JHwM=;41׳k:G|5$Y՗YUtz͖G`8nDyAj`thu1p/ U_83DZc7\dg?L Ҍe?ge$WSW4.<QL0?Y)kcht},W. ge /[p[^*Qϥִ˹r@6i(ҎQW p si@e,M6d ]R_Ze1̾z4 wPLs;Bb{j&bz72jl2Ol4y ɩ9{&I|:? (@|i )]LuXL^tssuJsן&0'P"ʴb 0ӤbvGbbt riN28%k} "dj!e^U\')_=1mwl?WIor}ߧOߞG{ X}U~TNZڟ׮z5#i(t_rͮ;p8]W{kSǷMktjힵl zߥ /3?z z" l_yjoLי7'56-Gt)ϿYʩi]mk.b7U8e7B[a *> ]os qsš`d]x{ڿ^+`u-8;sYwMmFMU7A~/2B>d:?7ELOcA4Vg#}=k6Y񄀫GĆ0 >Lb珐CNsNBD0Oln?VQ&?vHrPǟx]0 d`>ca ]ofuL AJW%} /w3A?bca8Ӥ*>pA vŞ>L@ >0׿ND]Sio=jljCerQo>9zyv]׾94'/?Q@3&0`L읜HM3a1$¤~oL+x2>.gPG5&H_u'pߪu8Fc !h_-*'ot:bXmOӟz>N3XѨrcJ9bՎy|3"2Õ`ۀwG˟^)_O;_mɐn@'\85z7s+7*:wJ[u/;@՚I._ɷc>wץi%cj&Didu`ߛI ^zmb߈Ik ŧ `Uxq'a@xyG=~~usuJtse-gn\o0Gu[UVP+smi"mdvWZgP%&a< [17:M@yV2YB@e o{5sFK>c؝{(CIfUzcyi @I܋28ςkq% b Dq4~U-4xFXrv\phv_i.9>!IhW$ڡm杸 լ4W8JN' {o2SZYi\ݳ r, BÃfJB=:(5Vf8)oB4<ނE3}zFpٜ\>].<s/a7!s|N#Qto~q(BfL]:FYw~0|0_HG6ωfa v5Cx%f!CΕ⨠q~7F\6}~~H!;[5ZFGYGS%r.s4`$W;)uLEzC sZBZ :8u47Ķs1Eʉb#@e):'t_?察K|n{Ӈ>V_\K__#*ҹeGFb8{sv oc؂߿nqno?z֟4H W:^&4Vf+Ot7M[l \MV)5Z@3F'u2@~ŻŠ=%Hgxfy1kp8ecbǙدč~S,HJ8G9s4u_9hFYkh#Kaw%[uC몟f3Šrx~Y2r9?1v~a묙{ÜVhdj[Yk'H9W_OϮjlg(; 7_iy~# G:\0:Wu3U,TffF+ZP=و=S< k~8!ތL()"(: OF'.nAj$Q99ts=WEAqD07v=ݲ0R Y/\!uE_uNug}ki,<ߚXYkmίsl\j邳/,E:Hz`-fٺlnZf O8BsW꒸O_I_kN?f%/nif]#RcmnfFm^4q hwU.qi4Sa~sX ՝![|GGP-z34l1'r6-DA`y"O60F.EpWH6ºYk@z.!Gz[cAbr3}q 79܎o0n~xzmBmֱj wsvL,{ykrEgc2wdw_v/MPy~}o4|4 } v}Fv>%\ JKg$¹/D+>5%H~V)弢O~frqR/>V_*W33ig*=2@ۨK=f}Qi:R0#_kA%qw45OPG_Qy+~0בl4RV| s74^Ru3YS3DorUP$5yoC6N]cњ&#X;ZgiZ˵ꑓI[?W$~:1:L 8čs诽;&X>"Jli2fxiâ9=tGmes秞m'vp +\b_7{SEۍ|iێv/o/{ygS~h4;OJˎ=6ۏ:? S?=WLC<~rg' {Ϧn9l͍_N$x=yGm?qvO|{?)F ^* mgn|]9gn?[nw; ,w?lz[G=U;F{Nۏ~ cΝ_~'s~I?a"}ω<D3V#^3=-:aO-5<J*^7}-]x:~ 0 CZb'vEjC0 gφ)*pt4l|J0y8Xa[sjj[u Jv DZ_3n6COǡt 64c2lxoH< `99p')hlۙ~e?Uqq gT"-vpȐN> $IF9j_azˮ=cgS1cch.>-Du9 ckg'?Eے~ԟcwod^Fkr+Oz_q~( ̉I}fnpG vM,sf(Gko*(XLi{w;W K pi E;*Ҏ9Rؙ_{h6r+OAZk qv4K Q:3S5ö}mgz)ߘ/q ϳDy~|rƝD 8X!`g Z3?>'=!ZDɥ6|^d&kb] 0Њ~ rFqw;m%,WqOgCfhc,+;'H vD0 M%$_L`|Qki]t~AZ8Z !YD"hW3šqGU@f}\04ci&>}(ynsS8=eĎevХaÚ I:Hs4nZDt`}2UɛdFi;c\_(>9KXb QC4u6XU6麺G2?4U^#bMb#7O}hآ +ɸp~X g+o|),5|OSׁ£3H2&5goߢq7$,peUL50Y'6i֭(s{T1pU nAvo&)wM̍~/bUu|?ǿsw{vEScⷽga&W3{M}~Dݢ6׼?p3sϟ}Y翪7W˞p;m?Bcz7k_很|hog {y˝Rۭ+_"ۏ 'U*XejͶPGVS=~w.|o|ů~mk\i{=oλv_BԿΧ@IDATX~rc{>}?7yl?~i7Os?vwO^p7}~~-7~@욘dcfs&IW{׾;-zE[qVʙ@;!#GӴ+UQoHS0nEK2Q5w=dY5ry+IbnjsqXA%bWe0f]zꍮP.;tJ8nPa>SyĞ K!NH=9r')t8A+HQKK`?DxOC\ )XՆq?$i{4 >ʧ!Sw #{E.gU90ILlkW,]bz^[+`ߡkH8]_bFVșB :+6 D v|%}e6U  8.;Bmì(9)8Y <9 %9d^f+3Ay9d8"s&2;r!6:l.Vj 5;UШô賮 ńqD6ۙ.bl/rHA9*jtj|?U h,kFIq7OG[f+o$_s{O&W%_p%B6fbaژkzzΑoѮnʰ(A`y(g!ٺF{ƨߎpU7F/ju}4Kv#=fn,O`2/{)ID0\顣FE>]O0+ʢΣ#LzY}C]?8u۟T_n{>= Z3;_c?gm|}3gNȉB큷v}/|ыۧ7s#I vëo :r  ?:Hh.>ůˡ)DŽ~‚߶׾!T׃/]?b8fuy c3KQl'z轷wxݫm}co󏨿Vxjos+ }/@l>Z5/~G>T}?3x{&7oK?~v~㮾=u'*~ԏm{c~e_ݿ ߥ7ܻԽ9G=n~׏|?eEoy`]_aexjU6w@(Hyo\*?S:Vg݆}i-H1FPWm__򤆋hNnH,'Uz"3\dtZ29g٠Wɳ Gٍ/h)2KGX?t3p2Qw1+@q&y.ء_VNnZJϕhYZWvSŒhaׇ_8"̃xBs}3#KN%OPտ/;2-Mƅ @Iè}9SAg$GZfbvU~@vi]6كV$o(*V V+x^rz}_:*Z/`&PL}/^ m3Y%\)L&pMm#ts؀JBQ:,7$ccԜcsYkǒ*jL8?%}2f%%6r8K#Qi94kѦXjFzŮc*Z'$ 󀵅dV;dV6؜%9 &G;r ~Xr笸&u~+>_\Vфm#KU? ui^i?\,UpSs J6AQnQ:rQF0`2`yz9ZA3 *[t 32/\qHW/]pkei +j,xMzGqQ*ZKkiJͥԨsW=7m߳wes~ͶcxNxÏ5!D)?m$/1n@XpgtrÚphlaVm?Hs 1&}%1A}R<NjrYV݌FBFfR8}4~ ]['.n8l&&?񑵇uͣ_M PG m[r3;5t^&j\ZTcE|&nօ@nD}棠ffb^k1O%|THmBT.5 Е1!֛LVu[3WΪ6bKi 3~3? ŧat:y+'v2X2kv_k4d9MXn{i"Z36t)Wo*xru=.k= b̐^ت<-q\o`_y9Uz)ꂎ<\\HiJޔّM6[Y!Yp!C&P7S߮$\EI\HgݣA:aeG?ҽ8$Z1X+Ԑȧc_:!^՞ EQ YwY# NZp"I< 2?E{kW9yC@/-.Ig?GW `jqk{"kh=˘/OZ%/=cӢU);<<ʙ;h("vo{%)mɩ5bs6+:ބ!zd@Ț >o S!b"4\4pZҭ"6,1<_$߽g ,F74>#R W7-}Ǹzޞ]ncco=]y^GIR:vϛ\suoړY =r~zqȚY+gSj1NWج -T7GBbzWwxn,8y+}QZs)N=nrTnG*jO??pPzoЧ>/zcL'o-OΦݫgE}Ծ|Eb}]R??YPsνzx;_1m;|N\z809Ύ^rƬqbQYk &u G@v/+=quax٧}ǰz:* ''"<ݾϳxILO*-t q\鳏 zp2Cw1#Tq:bktf yXAtXsM# ۫7$T%X᜚K 8\㠗3$ImSIXdž/9]508`5k鞶9\(b/ԩxuyM0t<ΟPX?#/[)tqk1#ZZWuLjxsʴsUT:nMcZ_$uTG Y9yQ :` .H9TX&;<%z |f'D0?~p =l-  *\!!u#S+cm!#ΩE;9;煯- J`ѩ+z3mg/h|Z'Z`L]sV װ(p>ZʇuJ؁Jq嘸vZ) S?m8vIX+G{Ҍ]X_F?]t ߙ㟥f{ڞ}uIa8aøk+>+޺cz|/:}Oo]R`tӕiߚLd~'N<Ϲ )zdz;1yGGOwpeygn]9&sOz뷯 Wk&AkȌr]3ޫw_1U?5wg^?/x<_}έwx̘S>I@_:?"蟋hk:F GVT|Aoȑ?Œ5[8I1' ?b=,Lp ohKC>se(3ɼh 4“4&8mc Qh|&_xhC$f{LIT4pYwԄxx Ac$QΙGKה3aW3ܕ(gs+r3k3U3isi01u/@Xk՟4dL Vw=S2 y&YO#ԉZvș@;j>X!pEw^sd 39 IOhȉV](ה#/"l=f(uGynG`KdT?+ ĭa?Z ӹ@ijf\䁺4>Re=![>1=Nzri{l\bƗc$a8<kpu|Hs']Nf _}7c ͢ɘw%^wX73mzc0~Z|~N(,̛1xwjwϰ}G<}'IcBO;}'; Aޚxݫn~jEFFuv~'ƽ̙i]Wv?q]n[}C}vo=9rZ-ڪ~sKS^?&9-$ϩYA,n7"/جI{/ur~}f=/%ڃ3յw?ǽ;z3ѵֻ3Z_UFڙx;}Wף(p8ZSQ?G _SNeTCz߄z+piw•кjs`ttBueѨ\CO&7jMX3M/XQfBwKյOMpv\MN,^78O7chpG+#՗@ڇYczrmd9u,SњEgn4ڌҩN(g\o;>Ƭ=@Et";?J5~[(~)dLS|PM,+_ϟBT¹zj@4N7g\\xIzֶYe Kcfdtxyc|"G&>@&,aךWǦZ7zmn$hp=L|$2c1xq%(е6<1_: ``*ruAt_\"%}P̥YRI6~VMnC|hPR&vc7v!Fzو7Cjg;MSBnj*^M[r4Q,V.: 'P%b:cL"}ҭp|}ݾ_/[];{{wնS?xyN!tmwv7/O1 eosa-o)/؞O\8WpoЩO=|ĦgcsH׽#{/<۝큷k{޹]V?.;]8ٗ}]gGL]nw08"WP;bx[wK~;w~>-m?ulwmgl/m{߸m G}I?}[߯}Izrկ76;vC]o?#y7m3x'^x8]n|wvn<';p5z?Eo|eitݱ~CzkŌSQKɄHy{bHpίVڴ"LZ p[z;J .,J5-$h $g"`r`_5n-~@'E%^9}qb:P9J*$ggZ<5m䰳W]USb'w #723KY`d}.m*n)^r3=ضȧYUթuΘfBPk]i#Xe}me>0@R=qL0p0uU3mt @_<Ŏ̲/ 5/7@.Y@K_c7Ih$jJE͕> ItNUaLc<fUۙKAk.&8MhپMxd&vT/ +B[I&*U@Xlt Џ sFa"г_{ qxO:~z\AUGO};t^sJ s >~{O֛YGsIRAO TЀk;/ǝ+ׇ__m?cHɾ/ +u`KלTI_}/vs~O~Y,^q◟}{o? w޴3g΋"xO=׎}4#O'zvoօMXTl_ W?10{]W=`~N;SW/ν}P-+62yx<\I5п ۢk9GEpn' i6^X io>Ў_@/L.9ZJWoB djTjX@RFǵ༣>O@Nt==63?Dkz)xW}3 y>L>d ،=Z& TszgfK<ڭi,{ #5)n X#6JlRN8s[#uR?泄ŒFt1鎱g~`eFDDmx8lg:c.5ˬQ*k#|җOʅcG]/47jRss:^ˮ]?۟o_/}vx󑡿CL<-_yM9?%8/3oB|}< tKتt7=ƿi dpx;oֻ?_]!p~$q/z<7zol|Q~1$z'-+,mҿ<{c7؟9uظ 37f/"`^FY52V+ĂB$nmHp/7LJ] †,+N.'J :}@t0a}`MZ erz]ҧ- ]ʷ/[ zb0:ZA0Z'Q4{m,"MSӴ:7]gw7%|Wz=k\i&KFZWjy:%Z'N/D5%XwK/CVK/rӽ(3ix+Հy,CMoWhGή33K3*1:L\Nѽ WV+pvj}e?i!h-pwOg9V+ҋ\0¿K wלhL&KVҚbpZc.^X:`4ɾΛs^$,gz'&o.}6՛%\sn5'ތx3_"]ot0s}jɪNՑM'_+N}{y_ǙEgp/R3׆|ޠj:ֿ>l>? l.96iG<`~}ԙilE Djxu,۷ϭ]zNӿLs%0~ )2/w1+;fؾ>uf@2M!{n}"_ ʳ9Z6S&zdΥ9Ӄnv,>"Ș>m,%jG$e]0}Ӽnl֎ wr௃YN` /Xt bH%}hb O3Too @8Z$nzDkO8fV،1A^\z8YăX]Yg)\Ff.cWO,G__?G,]G5l!annԅr3aZG Z*Bq Hl"0gI揀$ͯCվKK`mVXԏ7Mv4MFhܶ w|۴_L?EAK[8ɜ}:t9.:|@:ُaLX䥫v0jx|uz9ibSY]g!YgUxe\O=U]rmz58 =~Ņ!$w3nmu/躖@>4WzsM^s(p]yKa;-͓˷萟^_PXty>5i\׀T2Pq`sq^'m rzYu8Zo+w+H\S(v[r/t,nkg"i~|f}\9%9g&{m#I\k,yl%!" vs󲁻9M+\ڈ^4ԓ?>@61C7^Nh5 -™Hqw) 'q1x^7 3p1 1r޺ e_]mD/V/(0h5 Sk/~,BV^v|iq-ctf:o+Z,`G dyQ=҂=lE>]"S4}LCXnǡɮ嬀5(Q6a\ϕXaXxɫzo9úҟ5בGa w72u|zZFx0Bf/2RTfkM 1#,\B$:!$y=>}a64Gc_}.Լ}FϾ`Y]KW+Q4>ruF\-1fYW]kܻ#6yNMMx?< !ZcZ+? '/q)cƧ|Irw/pLo'̡lWc*s ?/E uU~U^4zPΎ~eG/Kw⡈\v\dgFz;4C n5fYA!ПBƧk7# k"ۯa}!PQ$VzF]`Hǔ4HZ3ɶ-pseC0#>C;5 9/#2^FeE)Hkuw1ȗsgHR I`yG+lwxF.@bww$HB۔\/-,ةZ 6*  ·5UoJ18  y+g(&IEGw\ZgYSQ2i2 1Y -pnyZt\3ވ bblCGlۀhT,Qk\lmъ2pڤ7ě`O3O4V+JP&Lcs.aht8Z<[9@MS۶JUar('yej͹4r9ڪ!oy?UWS.rh&3*$97[]ZܔQuy"I^uCj$ui`Z;T /7#AMpIh]qLRtv8w#Ӹ"M`3(=ݩg}ycKY fN3(8G|ء1(YyZZ"V©bXiv<@\Z̚L .2tբ£L~SG|_ϋu(̓UZüɜ >ȃDlNߗlS|ld6{/ *$yɉ4eqI1׽*Sb2vL6)Ygx5'6`넩 Oѫq4.}5-Dע[vV)1#F)Y19h`,e޺ 잉|yҫ%W\:$W¨6\UR`.cѭ.UVG  zJo9/Y}5FbрW>ImpF5F$5Ni H]zW,v5G~έs?`s* cGs._#}eC354V>Fmk]sVPp@k)@ #>^S|ޙ y9vs$M>-S!eG&;,pY+-{EMdF沈 yf#?9Es){[Ȗsk͊m [/WaELR.<LX19kaKvᐰTMc;caHimq[s İ 5x36d\'΃hA-,:c Ə~l:Dkm7 ᭍Y5~jAYW+?潍yΚ+)4ƤDZ)M]𳰴?P5v|y:s$KonLZG}3<#{=$> BZпOk6w|i%A#=&1Ր2+HDA!Mt%㝍\ck5& }^T3JMLX-x=X^Q)2w|Uzmxt|f]e+m< A9m$uTVpw]~k0ڈM@PWM~I|jv\vO%espږ.~AdGI8 L殥&'.sݺVF,HZ~D6o!M :Mkݸ1I.v_u pY+s}{rӕ"8K=6'yǔ(>..aݙ" `@/JAgjNtOL%#$4Th$4bȝcK]6H\>N6˖%GT/`.mrŹUy%3I'߫U%IjŽ0/*q3ZkC3?`4L*vmO ]=b Z\ܷ^6@84|}WljA/Ky)lW(6,}?<ǗKYӾ7*/̭:Y\ 8fID:^n hu=pq5ᖳ0s>zsO})r=cJ618 9af 8s'69ñHrݞBϏkdD{P j`suOVBK- 'Qo$vXkE< <Ǟ4tEl -[sB[~"B8 $09m|i-saښ+B_f׀/q+&9\b 1bAχHDc#ٜں'Eѧy:kOX~3͖x9"lgəTݶnye.|ծ@w !09n㬭 ߿$88G%}@v :hwoukE5`ys3xQK!WdԳNY3p&@* AkesK1㥸+0j_R̠ lݳy;Ȣ~ pckZ=.t^QX{߸$-E#=c!b@rb!\$焢b G|{{4qjN^8%f'J{?;Cէ[@ɬ /3a?w:h C6EܓLvl P3 Pض \ X9k;~KaClqهyPLѧrLq_ӻwnñQ^$'FaktWdv.1\K~ #8{ur(F֎{'8#3EnF*=`@IDATH eT#n\9"='&윤Ukc_Ÿkg4ݍ /ԜK~챋\)>ѯN]nZg.b $S=VIW { D's0{\A+pջ5ڀ9\+6-~Z/+8mltg o_ŧdwhy= h*l<<9~ߋفC>j/34S PcSH /WW03">X#/Bm3}+|z5޼ҒQ>%療+rv~_IĔՎfnf?2?x; MP7Ⱦ9)5{an_'3ЎZ_8C@JL\F͵к܍^97r@oNK͌P0CWN~Y`W_r؃j)Y쀡 gĢ^8laݱsr͵wsS/3;_ntO()W,9;Sga pNwٕ)2h=E}6 -8P?|]5zB7ז4/5"ޮ}5>2&c3i戼2x a#jl~SI3qV槚x H54р5s%P-O zeXMRBQGL=vWM֒{V'~{S5ǔ OK%[rI6^5W_֫S#W뭮XEX+ܼ+~F sr'8gnz)O?'JOTջ)6 ,Yt|k$ˏ(sSYͰA@@hM'pO~, 2V⹃sBSJүԤ0&t*3=;ҵJ}V߀.K`GlO=?.,5Lڀ3?e?~oɬcJDQ Z_5C?ժŷ`>r4ԉ5*R53JB:L!^ͱ8"vc.q'{ t[J}ğ[*=ǿHJVH!u@-vNTkuk vxr.ʉaݔx0FSpn/5lz`/(Oą?ո# rfo Sxu}ZnzQ&{Q⧥Ln&+lHE4eb hOI`,k-"+'?3Is|Nw`F>7 F% N椫0GA5_1bҕP]_r[,oOkٖI 7C)nUz~N띵Bd=j~cByr;@!˯\/X@ P,U 5T bD]õ_t,}cbsFNtVKM ]8nCmu 5=.=nRq u&펊 tI'[qS(ܶz" dIO\Lޕ<-Sv}XHsۦ({E &I+sg.5Դg w`=_)I닼uɗ|t;tjmǀN^zޑThپxv,PfEM1 Ns*YF7ux8ZЇ߹n݃$hTںsl\8nOOz[ {ܤVe=5-sT|wJOO}@Iykc?SxNJ(zgBQWQcOBz":rؠ/<(a9tڛ㠡·?g{)5lо^Mz=EzGa8_gWmE%z)Ad/øΨ&YZ˻k"ZAꠢGU"_/70?DJ1Ct8Rѓ :}gK[ (zՉc^xhG&kC j4r:?M2/R(iГS8_N6ھ̆9"mu6s}C f Zڰ iH#w1KEE^؝6)Ri糌%G:|b n8@f֒,ls՟չɆ/`?|,(B<0kTsS4{HD3Nb֞Jj ͨ೾;H#W**8unz5s >Z:+8|Қ:q: 1Hsds_i\UnM XV5zm"_ +}J^|,~G:ͻt/KZ/ndj논u>;H"By @kWho?:'W/C#T՝\_s=$Mh9ڃ%OQ-]33JYZcN ´ߟ?}und{=*ǹZuyk#/R*\>ٽ[@6նHC_{q|&y(2*Iմh :|";r8';?m+_j ccEPkN go.ģg6sj N®;n3p#|AN Yszj&ٚ%)r~>pDy+n|{A&Qrּu3z19iL*͹|SϨrFDhӁ:~YGՇ<bFpDu x>?g g`q';Ibqb|ict܊J[qoR^Wh}i?Obkm B]sG8EXȱ.W{saC3 砭5^&>qVZ"ObpU -a/SnRq!̨nMT˖]Zy`:Kl'GikQ2Opshg]§fڏ+$TwBBWu뙣7xdO`P%;@ʦRo-wbܗ5<T?xA=I@p*s׭98CE3'18U> gP<@I|Qf/ǎ?'fjG%'^b'8>G 9qTςEpH[k}0GKܙ24?Of/N7<斟zf JWhr)fc)[C|TB'e/J ~*8j7[ }lOeĂ1? $;s}`^<[o"=g6r= ǽ~/}5c[ԛ CH-bC~%-qk8CI2?|p!TK۹w.hٛ *X #fm0 g4[-7[4v94㿟?^89>Eыs7Y(~=Nο> ۘ%y=,IRb2La|-)i`m_ĪZ#? |q4d2j7(xiz|}fo^mqy?OH=i>#'YŧW *<7':ߞ z;sxSq-%淁δt}}YC5SKL xߛ%st S*8LJ>۩=1Q׿s$i)Vg !(ܶNtp/ p}ح3ͲP3Lo?`3igsQCn?~ ?|jٹy"?<X#O:oԻn2tܿB?7hI$4_XDγ:؜ii Ej}nyAFIiAN&FQO:G08ZT7^‘Ie׮`;I~yB3npU/گB>K54?߿3d:O">XRN}߀-J磡O2?I9}Y ֻSD^K2yqZ<{\kSsKآj/˒=jޭoKj5f'Dx_1 >>>=̦w{.rWjֳ3T"yBw䔉{[g?K;O`VvsP߆OArw~!yVys83} 57yo(l x]O`oؠwCUHx"ꏘ.>X8 /x򈆇w~I SoN櫕]D쪂j}/jo%՚is?F]ߓҿ*Y`LP 9ԈvRJqt'ց lٙz%aBVH]{qz#{=GyT[Zj|÷{NB 6RzoT\:OGKB8sZ|M*0NN>UtK4Fnu^E*A^&sk~u쒊9&8%+s}^#h%{ʩ;B4A"{"%޾os^?yҫ$aŝ|u~-&3t9:GJ/~+`aU'ҳ+ȍD?Pi>1O#P=?͔8|ܨ_5B*`z\3 (nO?+$_&Lsހ+ޘ|z0!;4Qra]&4$r-PÞ3q<هPUP(CS○fZ{bA\ųW(n vp^`vA̗Ys 7[UpX>[Eׂ$El;D1H~ *ĠZ1~-%ϼ@}/tOPFLt~Jqr6 WG)ސw5k IS| s ?$_Rn/lW%#ΏTjõv=h_>=>g~#M}Irxp&Vd@Ws 5*򑪶:TMLrac v[q!"ko?z<$xWSg}Q3Hj-<.C"{EuӁTH H=5dُv_rN@ Kl$IA׃hg[óR9tF@*3 ԥsγ'.hPmK_GA-q)uZhtHP}%eH{g{A 3ʓċ8.X6I8'k˾t5C%9U[g7 >דC6O ^P҈4l[,;qڅw>bz 9s&0[=`Lf}O 3H9 gޭpZ2g2|]{kϹ ':l߳QI"*Hp5] HMۿvc>q|ܣvS'+#Ѿ8͕3jTzP]orC^'+`(?1$y|`g:\&8Mn}صfl- frZu@ = ?يNϏѯŽ{sșdxY%`amhwB1s?IkZ=[$8>19'chN:` s8᠄VTC*5ҍjmP58j%^!<ȇ- ]tO귬_Qρosۚ;25#OB)H!~`_'X ޚ"_0Qe~QݙE|4#S5"2iKmae€c&?𓗾~K@$>15oI%ܞFyi- (5ǮI\犆0 lo$9m$2Xo>Zr-V&L7K}ajtk_6qyRӢkrK+3 *7G@yt,KQcWkMU+/UvfvߟGy-,BnIʑ@5~٨Hڢ@9N:Q4ƿ-*nm0k^'ZuKX-R¸}5MkιVVWĴઑ:޿נ61] Q?тwE srtyg NJIx4BO=}R# _h>\8ts7o*o F>nc|xan抳M;Ğ.~uY)WB#wH!vuw|('U7h{, :00'G!akq4ů] bƩf F=yܣ0nep(Xo?dˊ5GFjc,ڳ!94O9k E*[K-u9We>:*?eBƇ҃W3ʒÜ?X#Nlwi*jh$uދT>s\~mQRO>1a-r8E2/j3OiyY~px~ <;|y~=ӥQ-V@ijIjd>[vMOM+ACHMGF(Y~q槙) X^+S`zSD Gw+o<4=W=>*/K*W'j.!8>QAt9ΝHGK!I PuY+cdzN|c (X8BGisj'l`3P]kɣ OLSJ3}⧮bd_| q89Rt3 ]h> 9sHZA^W/[}?N@4]:$H=j1H F>qh_ gZ]4=SW ɛΝ;Df322Wӌc-?ؠl](waRe$Y"L~>K#`ڂR`Eݨh~ur6J(8UKjEH!F!i>_e_XA0z7{69 cYdĐՕ:퟼J "/P;?Tap_ ++^]{ kVMbu-ۓK8,!|),8p;:wۖ2ZEdp+#-~jPo1%U_2T_ɴnR'su2Y0sV2={p#Odì#`lj"CHWKSSTkƖk!p%H?lW톉H}"^>q؀r.S6ơm8 G?\>7 a. RZupHRaVgUNa3jvk39>Z]J\oYo-W"pjIT>Y=zS@)iW]a2Hl$00tBgEf q4+>RuIz)_U PLC3!ySь ϿF$+ '!je~OFᨨ p!<%-zBz}ny%u{|[A^Jl u_ ϊwI>:W|N[w  ij;30CG}ݸ گV:{G7q|H?yg{8րԣ;fygazlen PCeMh7́aby&>q-^4 { yuwZ?%gsT1*S'E{ o[]!Aϟ-YIiET_soWXkxV|XW丂7P{@Pk y g~^6sUs8T v9( {:+{e+Osᮖ*R(<-L~dU*:W ߜU1}ܢRF: 3[G GnC6cnA7. lc{"ϦCO+B|?%27A|Dׄ:_ŕ}Gi?֕:`!e4_֫3gCۿdBj<Tml4k 'ޫzH8)JW ,z]%<Ѝ: sl r=|eJd[ʧf.ߋ`*X}֫hu;ќj|R*0k$q"ڜzM9;H{$'5(kb/ a`N=:ȸ\QNqɾ2^ߓ;qcGH0?pjHՓ:j_TU_=J'~t6ϞeD޵%kC ? ,PطˀtpJc0=* f|LO_}+ O_,,-nק?k@kشW_Ɵ%b?`9 ,/U$L̒9N:3?I8oI/f&2x3f9 DrjTgUg&}#'bGb\4s|rWVBd}wy9:˛7MN`>w~'-j&=WN>bى*)ۧ|S4!sk2j@%C} :a?"ɕ(5l0w]oN51C=sSg(u ųUӏ|:O,uLܹ &\8]=) @J$(,m: eSGW=ҭO\8|(IX=6Fk`WpS|inEc1r0Y!ˋkNTI+yG{y'!HIPGeu(NRR"P}$b[Xۓ4}ҿ.- /.P(FLf @{SصSSgQdus`7f/Q6œs$);b!4ZuVmYAOKzX?xKGUz}%Mȣ}ꔯ`18d3Bo·WӂmOrzf ^0*CE&.*ǸߣvB}nZ:~a'{ K5Mw`h bװL>t'hDAs=滑?KKsSƲSg'?S>__؞G}﹨SLJŕt\t>c/šho/] ם_Aď "vEurxu*mgYse0<#קRyO14]H^ߦ1B|VVыӿJu̟xƋn@)oՖ%]4=7i?zZϛʟd綺47c'^mJ5Yib+}\UyUr>'ae'ߏ`߿#-HhfCL:?§0`hc1y(ZH 叜Njx#: ҙf1}~9%|] b`g*d>xg{Y5Q{HZ6z//'d!5F:ϓ~Rw5ͶV\af>*OղQdbZ3H}hJ~:XG 9=  ZsOe~|& 4OǾ4@hOq`m2] Fdr["|3z#سNy>kUn@޻"!l4 ȎbH'1hUgfܞaL Yo2?O:+ ?(hRYx$\b㉐o5/fa4\ʇ?gC_=X@sI5:߾# MOQUr<|e@߾\Z|1j!}qNr; #g:޵.ijh"皤 tMMEB̍k`DUO-))> LʹcDp&Y^쥡ՉnD@+` _:Gzuf>s\ΩﵡTF8@@1A%UT8-`^`׶]VP7ӳgQ4>z%'ܺJ*!# лun]^|L<$멏*h|ZӨ"\~|NjwK}V+p "ط"A~l XMVw GF>6 +$0\EzPr7A-G7#)_K7/xJbQl=~Jz|go+(5Z5fPꉁOJwJ?0~4sœ4jiUeK >B5݄CӢ2S(I#ɍj+8ߝPݨ[|~.q/#%05/@sB挢62Y3Oud2sQ@<~&z:| ȭ1Y8ؔ7t|[ɂp?Jueh6&Q+9aaTF|TwX,/CgjMC  4, :Ͼ ?M=SWDo:K$ "P5W1sOY oѶ>_ި9NR5}rԧ>TCi!E;!2DK*^!~ִ^@ 7Y,>i鞩S4 ͒U"Q;M^aW}~CkOZ 3By~3xh3\)VkݔG,SXJ0.L`O=I]nǾ#dsv&>{㥥b@o޿Th٤VjΤ%9{&0;D@Ia\t\z^U 6X/u\ř򈹆1j<)PO C޾樂 %4?9ZA#<^DcC:m ;Ί+}r@ %GbsK~V^*I`6WR?292Sö'+pQjM.m, o,CBԬ!9ϏTVVPSD8g0Vk*UZ[YN˽Ï`yx􈚛f^,3)[y.fﹻ LpIlJ$V t'ʸQ$jH)Gde}nG@UiCD?W2=ݏ kR/Iؐe~%i~T6`A_oD)T9ruM󟤬 h'xfq&bB M~3,lk՞f/"+?Ij P UǬM{Tobs,SfҐR'*t.}yLؽK@mfݺS/Ҏ8DWb 8N/L8h6 a;ļ5 ZZ6sap5756k[Yz4QVmx׀CKEfIL%??'p^ϰ!rz&>j$'^R4y6* A^k] Ċ[2)̏NN̩,IJ Nǡ˖0[0zF4WbÖ0]'y)[S;#63+_Q\ɚ}9#Eat[LkjFUgdUNMc— sbG]'kg}>?'3̧/O?oLK,'bɅx0]í/*_':oUǷg" [hN_"4~^ݑ\ ٽTID3\!H W K]~L3*u?[ r/ H=!!$!]:LmUb0)| YحSI-AiGRc@u?S'G[A\zKR E9m2yV5+9q>;lQu,I3п=R]:02s ?ܔowg,]uFD3<RBĤo_zSsW๧psNC]og+>s l s&Izpgd{߿?#ٍMksoxwr;O3_LO7tAG3sQCo|FskJ*l  Ξt *(x~u';-IGH /SMGyvhݮ:q0t6Y'@9C#x1ݭ?b5-#3~D.O[3 =rOS[.Ѯ\mozY .GU*J˧Wl֙u%$^ vǿNxDRMf?syp|Rk{z3;/$su}mxڞPM;{l mK[E\>wOKTmDEƜ@ (!~D.ƕe1=[iM29a+ 7,ji=X $Wkg2)ٿ{ \MϘv??/L/T:g_:9OmB܋^X{hԻt\ y_nҭу,cBESf 5m&l W:a_>u/D6ᾪh=TeE7XSxz ٍgN|@Wg9 | Vs! ?>ٿj~S K?5.FI7_G5֌qUT<|?yC u hāPc2"]ɭ[$|sqB.^U`^R wZFΐ8=Ar#ĨTr`q9%$>j.d6֦m\U%G_~O~4Q1}ֿs\;e$/=6WP+J͍ZX\R1ߘxMpZUE ]|t</@vw秼g:R 5C,I|3[\b}k:XhTb9)Bo ^/֢?h,@`cӋy:|`PʝYi^ot|N6`whvm%t"˴ %O8záe[w0G4Qz;Dt晤bisc\T*t kλ< ԶCu"|"ޙcբWUgm/W"?|2T_fm4z>ihF/ӧI2XO#ĔEi:CIDATO95g"z%sjkjzvX/y/UOϟ9-CuR>\'qwdǾ.rkhYmO$[x=yϻi: 9*@JR__zXyA}a?/|u,`tv/gZkF)wۺ/VVGs{=s yf>[|~ PJy6yL2ĕ{J~!Z,' ,L_ptr`9F@992WY@D*`.9wXNN-j}*b.:uT"Ƴ^+6RO)^|V {ޟO6Ӡ-)>~x K΄4~`:jow-=󏀈YZKvk󛋗_>7kk*`}7^}UYz3qGxZ{8ΡjqOнI>;7 [I_zx<` )S[z7߲[Y3זQHſX?֚J˵)V.t_9J~\4l=n]JI'2"U[c:5eix|#/uWDIENDB`mariadb-connector-odbc-3.1.15/wininstall/mdb-dialog-popup.png000066400000000000000000006600721414431724000241420ustar00rootroot00000000000000PNG  IHDRpv{sRGB@IDATx%I\$jQ*Aeh Uz}r v{/o3~!DiT|ï>G5ǧ Lքn/D&al;gj~KSO8c`JVϖ9ɱZ /ƲׇqM[Y]-U1: 3O9ߗVɄ{:@^j`Nu*֧u-u(ykl{oe7\Ih0̱֖鮵G>上ʋ;= 0uٔXt{ Rboxs~|<)?=_?v`;lہvt^ =jՁӑ;?K#WUC*G\In Wg0M-55TjbtCc{~T1;>r?R*u?FbT}Rw kfg4G5%*M'Y"!~{VB\`)k1bh-%}3L9NM gsl_96N),ϙ6KtѴ.>DIg>Z034]&ek1O)!GϏsÏ|[猜Qrb3A%'K6lہv`;?3EzV b!ЫaƉa<>c,<{@be{7 =8͂x2a ۘ77_.'O#uN'*!V98^nbC-]}MIN.|O! ƹE_rD$OMI4^?4s~Jħߕʟsgysj}8{&j+?ݶ2G2WϙuT{@W$?s+(:+qgŨPFk\=`^a<#u:jC_:;=`vlہv`;߶dF}S<=`Iat$e4 4gctSK>LԟͦZS]w.#Ոں>PC>y#K6Bs}I?G=es)`YWy͆>9wPRmgDo)= Ga)س8ٻϐc#l4RX.\7_\5?˚V%9bmoP(@{hr|g&~5rVg*q<;6rL-$ƷʞM9[z1;>|%~9]7|-Z81-bہv`;lہ?8 1H{ЉL7=p2xa3L/%@>^Ά!w0c. ]S~Wܗ c qZPZ\ yDO0u!>};ic&qpZi~b= ,ˌYW^b3685 x/>Fu3>]IbCs=ܜW84sR/? (v˝"I!,.^$mYWȳ%`oaY}%Gykv`;lہ3 0`z0qAaq ޓT!Ȍ50£>3NExt OacD5 `HjJDG6x$tj!7 1"rft0? &N9Ϝ kjML?q`|\J^:Kte5{?\SAWꟌ ߪm}Ggf1;L5p0P@Њɾ}r+X3dMa!p]_XYh],ǃޓ&1!X \m XC-`֜9^ہv`;lہ;hg"eйae{ap5PZ=Zb$Ax({[̼λk1MS~u8 ©?YtKq:ԦS?I ,|k@|8\E,kPr1cT[i.0oڹ0;AE~Ip3-e>OI,o(,=x??U};)8zZ6zӾd$/v&~Ϝ?9մ 4fHKڜ3k|;*X{:(QZ,{?Z%aw>T" Y{_>ہv`;lud8X\$ vHi!tبz#nFd ʳ 8bJ}S뚅k,͡LprIpJF bPI>u<lہv`;dp%ӳn 3cgaq3'9;Ư}< _Xtνbw7%G}ϫ;#NkڽGk#](XQA=Ch9{nypl%hL\t͟)G0$ K]_]oہv`;lԁ<4/ n%u.=z̀ ᔜxyVfD]Ag=l$p-P6堉C8U<jdϭ?%p;R';>M}!vܪ<=*.O,{҆ D7 WR='2?B~~ 7p-qԬutsdm$R@d f0US;ǦF$8BIߚ]7|6@Nl 3H"ƿ*uZ]Fb7]bzDϋ51N$g N=&~ zoM,jJ@v/o{u,\@0ujg~`QSv`;lہv?}LFDgNzrҎYj{5D×uq`T~ 8k. h<`&ϚKl#M_!r{t}?rkYomZ+ħ'&co= '8Z1RpԟVj/gvgs=ټVʆkC'u-b}=).D?O_@X#o#<ǂ 4o1*O HCLl*QEʷeh}3Ukہv`;lہ4Av635w 3e2t"L1Âd L9fÛ9QI 7+q\{5Ry)LwnxIM"NN ae߸W:.Ad;WS)֯u,gYEJz%ƀך+ΨUczWqb^Kmmj@[6kr&Lm7䩟.;F~n {2ՕoL|:{ ש|'GoVɽl{o#v99. 0޷ہv`;l?;( ̽3/ g8F F.ChfG$ m]2kCn|@k%ZP[sx֣)Gz1~Z\Sfh~6o/ռuH7(^}ݷ[Ywy*)s|-sIswm5GckHh3>]2_`Az}ƑRz;vspG=Y5 X#Zw%C^%{E|F.X|K4vȏhQfZ+^j 1c\v_u]/_og$ *{mہv`;l~eh :59jasS̥3E ?:t1OБdlYJ|rU ֭aGth h?ty #]+Zӗ 'H{Nw介;1%~vcQCO>ʲu) tc4(&+hU|h~bB:E]OWR٭ߡ XAHZ;oԝg8Z dSYrJNJ XM@?~Ԣ0ߥYF>Qo\?Z_L ܃ԁ'Jj#޷ہv`;l{f鴇P=fex v p)wy?ttxf =8Y.^\dh<Ul~_\G3O9.P7סWBPKA^Q?rp#})>>r3՘#0 lgH-J pD4 ӺY?}t k'<۞<9^:@kmh'o~?pa_)v<8SmH8ힱi-CjҮ7rB׿{oDwv`;lہt 7g4{v)Z]v^暜}ɧnG!w~KwӦԭͭ?MJ mz `6*n}"7Ź_ZSS=[c:HUԝ,SZ)fjƋeJ*P" Mng lzf@k++kl->_<.u4bO\K[*3.y-K?s |Y@lv`;lہ:FxaR\ P,*gag0cLC{4]bG:I>CK-Ljx Ўe8=]źtԸא|"k\N$7| PVk3dF}3V0sJ$OVN'ws.pu Q7H^c0Q{Opg1H "W=hHU~F_ջ_uH> ;Cs3tx*:?0+Cl Dj=ܳ3=aEn~s`lہv`;|:0u<ӨL&v }񙽝 u_g>Z°E6)s0kW2e{f ,m=͟3Ke~$MO-O R£:6?Hפ.bK w G^AP0{~˚sFvƏk6&7R. ڏls"Q9YIuqJ/Y1_ ]DƓIyƍa2 㜞:^d1Vhaҳ!pA5>!=hjQ`ج]8w@w~hQ^*Hg'E~]"/)=jABh& 6=|qTZI1˜ubg:l? dZ i=VcfG""9zL EZq\kA[!9r 9)n \jPH 7j]O.[woxn~z2)IƇuYcp P-!t¦)%){]]mہv`;lð#iR=g$DCf% r;*A98`N;yD5-5zS( .e7B '-+Eu虨v ]m^ }IE)z=n{o&]X>/q~J "ܧp0eE8~5?8;`K7K>SUEv@U;DCp2w͈Q(zT`L7@ %Z ro\+ږ1!gƼYU$yǍД?gu:ѵ~fkQͼ`yGC>\95po贬q,.U M_J 8*֍u{ L^#7fH溧?~ہv`;lہx㌨hd~MO8Gm(/ß1l̿kh`>feESWvn1c $W##~㔄o^ 9!MgH N۟*rq<ssR#r:|ї[ ;e4_mI`zaeP$~w;v`;lہv`:0/ypUd^!W= `Ry,N//6noF82?R7pu x砓jH,\wx@j">4keUl59@9필9{ӤQR舯]"H8{>X@sQ<|WKT47C1'{D% PCu5CCZRC6 Du5MGt+QѵnFZ?՗I}j\ٜߕsw#:C Щ @bg#.ہv`;l{y`Yj` #gpN)?sq8g0 3[,JhFkEWc`?A/׋(y7p[K~^&SG&".+u=M=KᚯHS6i/]c1?Ҝh}9@p9?1sE:Q =M9sf>X;RO}\K#EjU[Cz~5x}S9\( iѫT9b Oxs(HT2YxASO1s|0}jJ 5j-G,[/߾0;k~lہv`;篎3?2(  d_Jyh8ZoVP84SW$jO-YgaM]Sr3Hпh=>73>Nql?KZ7@s6˫ 2 F+]A#P>)qN`o{cs\l["sDg1H)P?q^95Y}(3o>F:.Ψ;|^zm9cQ1E'5\#>ہv`;ly?lK@ >u6rCg,\8*:rvq9 闟&0t1ҷ|?s5bPۜuҿ? Z+(MPF:H}gO hhy[*q3z1|cB9=8S2'9Th ?Z^oh'=p.J9D,I4J_ovN(}H6>Q/mKоxeGr:e:hdO= =e今Mm^cہv`;lہvٚ53,G!<:ax[kʰl~)@[}r=`X1q&OiI *c>D!CM31ȸSӹ?G̵T|=v`;lہv`:_,Ltۙ(h?=>jόB `GN|$[Lev-$jz_Z:'?9:U@}fe򮅍++SQvZBKd4ʡL8>?eB.kéM{bK-9N\N$]-"T7E"9# s0P9ӟخ/8_sx7r`|nR[GF!6 ՃCe|I%_Nps/FIZY[͞Wذ#o ϵLy(m-^=69մ#?} ,|ǠrGW~w\oR0->E.ڳmہv`;lہ_? 4/% >KT 4+T.!:OSܭj<"ؼxΟD{S?]g&/_ʼnl:7U  ?8o!_ARVzXo 'QK$o_I!~ VAD&&[^7Ӧ$eѣ4gl1=vu&͏m<'|]jns9Ɂg}4"=YsON|r𜱀;~N6nbNR~ud~s^pmerT7-|_ q|@vlہv`;ߴދ!Cn /=@ʀ*_sʰ <jz~!%ijԫ@gɶC;te!0}?d% ~I!#yjq7~A8"Qjj2MZxŤ1뒣HIy~fNDio.G1gn,1ndd'{k$3S.)m5K]uP]r)! cp27nyxIY7uy3_ tukDn^>-_^@ ~8A/#MhVOt̯-HvռI}qہv`;lہہ/Cy"=vf0ۉ!&RP ƴGvgs  5Ɩ !` /.AЛhǨ(W`7gx (R5L'XݫS.AtϊN (dED{<iiRu8y)ܣbt=]J|Wư9kS6ru=!, eq} qoH!ROS66mϕ]:SZ=<̢l^S§.F<%]n!BO9{s=|9?!^='~ #>;8έ^r0w}?8)S'BX+ߍC[aMO%{֗59 ipk FNk~'N$t6^ L]^z~wn'x{f&G>c=ɥGq㯀;60*Xlہv`;cHf]3EzH)+Bduv`G2w|cm+pf؟RS;8!yP♞$suh|'~g&/M1n?%Z=h5xM(ik, )V/nN.+E`M 9y{T)<HbNga=ퟒ\ >m@_jJ48BV+0ٵ =(xGiG<-pB!mb·;*6YΟX<% ^ z|.̣vbeuyRo|cD||x3|Ct;ہv`;lo~L<\j]|3? ᏘLgHɕ5^dH=.q}*goDe6ϋwN>T=ؗ -e1^e酀}9^2Chy̨"߽>jP-]{Z 8KL,3LEjQ|r9^SsZ-EBG* +|G@ w')*u7jA]{A uć̻&༞2r_uoի?>ի~9WwM_4w?} :CŹɟi[ڮہv`;luUFf `-g靧b2.#axpu Ze. Q06pDˢu7SAN\ِπ $ڝ/8gq>xqR#5ih8!34""z>dohi-Ǔ' M1G3/azN%5qd$ԥ|rh1][E6I.p*aCkWh9 O9#PY?I_?\o>tP1Xf ?\ֽtL5N>9Q" 5<}XNz.ġwH'Vȍn3/zU{VK}D\_xlہv`;v 6.k_L3lw'~ӁӜDe u!)5\'+^CRZJ;'1j.ҝ1ԑ7qJBҟ g]r悄tJhp^^> T71u Z[o=2f]'o ѓy;?DWkz̙Gr<ɥgὥ&2✎7X4ߓ 8bO(h>η`H4!|%DŽ zղʙz0YwU/V8J3x+7^;_䮶ہv`;lt2iLlm=f^2Cf_>ta3v-C򞛙s'Y @ CDPWp .Ԃ0'8aLLJ%ZK_"\¸xeXdGc08]=[ԙ t ֝}:kn\k5~u''MZ(gI"g(, @ }k̥gb(4Z?ppllA*`G_ t͙/dk 1%ms$p|sUgeuN}.s=|{5#|>ہv`;l?:C#gt&V.6xxM 4}~'kɚw+e]Vf\cK חP!odȧ J~xYՉW zoFGa.|{=dſψn LCʀG|qWyi31;`qeϖ?|п﹮6a=p{s6:sLQnYy^ռM ~{xtv^DSiu2}W_>Buta_g=.J~ہv`;lہgtk&L> a0~ΏG3*ݲy !g W h}^}&"|V~[D |*OŘ.>+ 0˰q#PYJL|:wjNrumJ) EւKƔe W4J͎kl|sĒK<#Ħsy/,XEmA+xR9?|SϤS@IDATDbRb|\+>O=So΅&>7K\"F"GYWA]|?'\?̫ |t'{lہv`;ցdP:S){G;ϸxRvw5,hPE/JXT?gp8[;uB ;64k^ݷh,Hhio3 \A9=2\=jJ3 dyaEl/ƲI@w [K$BEqoG;b]1xA}?z8mb㞋 -sGL_pFOSZ[>X[$?~B?|K9_ok;lہv`;v24;ƣ$ӧ>ΰAbϏsd32uPWm-U o@8;bGťU2blZL?:Ӝ>*5gOZĞB6\DFJV\1>hsZ}4'AnѺG>K'u?>MN;kﺩ?U/aW.Zz 5Ә<&/uOOS ԟ*sJ8ߐWڭ.O~\(}U˾yczs`W9~^ǿMlہv`; g] ~P,Lhl#py!,?wӅ32 ݟ|Evt'g;51=:RH}\=hOx:ux0SbuxWgq|VuuuG<,9 P?Ϫx9希@΢peNӳ<}l`'ܧʀu O懥3=se26ԯY*8D?w=[A =5'Gx>)YNP|.99k#ۣ>]comہv`;lځsisȞ& r>ck"3E~XC1cvgrE3GK.2%_s5,0G<Mޣ;gkzT:oZ>.s>eXS8v!H޴'{ĕ5O.A5:vXBO5w1!33n-N((=1yqpgpnT elgSVV`l}MYjS_Z^ ,ͺɞ$9gnVo$?О^BfBU ~8s#]ist"9ʟ9Yk-LXs% ߞ5?C>zZxC Me3ⳤ|Vz_SkUۺHoƧ/ήf0u812Yr]-֗.40=/ s7)Hƀ#> |#N<\X֢# +smĬh3;MW)͚:Gqt9Jg. >#OVN^VHk;lہv`;{:>/å L aXAZ̓! s Y(dp4S̘&|kMc~`zbAXvM򎅨{hO>x^sk/\ۻIЩc"G8#R9u֞6 9럂~m$~ǩjh 9ǾG-F/>grG Qk'LƟ՞~AD{6z#8{@> cD&][s~X+z}vrlہv`;11fd}DygAYp" 2DKKq?]N G(\>$lہv`;q]z`nJ_rcA?/5pT\܅cMċ3<(sj%̀|ƙZ?׏H/84JI,lmCi"i,לMv+0F ߐ4r`PVꉧJTq/`~sN u?LxVq6A*b|m<*7& T21ٓ9B;/~Oӏ9[=`ToѤnCꃛ9?H_MS {7xnϕ*/*V8Ḡ`r;lہv`;p:mm=PyFKϛ>)f 'oѹY| {сԗ;ȗoH G뷂$<v.<~A>y6coI~Ʉضx͍>Y#+[OWwyWZ6~~ޣѩ)5Z S/l@5=?!='rhf燗u'(\X̞hrZQaܮӫ'#ۄcd15mno=|9W_>}klہv`;vhge@ۙ#d}^NFX^'/8Gyk^m3،fю.\'_ b6Ѕ\oޮLt5y-Yŵ_ lU'jqDã)*XM,{[0u<PKcԗ >Ǹqj&wctˇ7G} ߀CCh$r?)pu=?S<&69|#Go'OOQbǕ<; ~G*pT ۧ?[Ld/ߧtZcմoYyi牫пnہv`;lt@/ ` $!#O@[ XXbbZ&NefXOYlm:XYýaG ~ߞ9jcH1XrŦgx_"gýu'ť*(k*ߋ#c@57oO8Ga<~9"00ι?Oy&VyfcgGe@t޼5':~aFw3;L;HEwb5q%]ʍ9Zx};lہv`;o U3{}g'8f7-yn"]dzΤc)`UŊzyZ"OC\ zV?^Bs̙'dmn㫧?GI nz% \{UXo͓a~ϓu?+dp^\&NSOMeUWX%g ;]8gӢE+6_Z ?'9?gh?p9;4\1Ӛ:%91/lہv`;сΌЪ${hN(  '0ˎzޒ2 ʳoADf#pX#qH (Ԩ>R|G]EU`2m0; ~$ =&P!™,';VUS#9η()i'|Rs *xt0'D0OR|+ |zkXD kk4KKBхVo?|^~JDk~%^}GVZjhR@bv`;lہvv 2LfXw3E3 I1}tf~ej0h{~o "iExydjgHeU90$G }&Ĝ8yiR(`;G;_c'kz7 ɹ6O#F_~Qek4=?}__:?1?{f LET|>Xa(a+\sSS Rg9(_|Ulہv`;HkW2`2syb@%:ÆW=\ 1_RC_zH O܃OioO.RѮI^)A\f;o/v\S!Unꗬ/36£E]^O(Er9 aug鉧2Jh7x.TL? >.˸\u@D[xKw,jh0S\8}6ɺ6 $OĞ`k?Rr1VDXaA@5{sƙ^/%ֶ$ 5޳|o6%ѳtourŏhP?(5R?FBhMk!:)?618ԛb{fZ&Mf%G XLQ`pT H"9}o̎a8hs,+4Ǧ4t oŠn}x?5q>8^Mh;0S[vlہv`;pWyȘ,u&3231/:&^̦?i𒃗qCI;<u"p4J5X*kgi/[xG4,ǖCW+? %pQ !tY pSդ9ָX/x2G$l2gj/!yz0HVNX s|^\J@,t&`aŸ^,K}s&05J8QO?Ƽ[?k[sN,(`^PYut'=}Yd}?/3['e^.Οulہv`;ҁ38@R ee}\̚Z5& 2әT.;2~{^ osQ!cy,2|٣9*k0eS#0v\;_O<{z֡sYsr=ϗ dynp'IJsܓ"+`bXя9sA7]&nc}l[Z)gi`Op0p ͙"3=ּerqibPDkg$sZ< H}(Le4(>g `_('w?t0\pKdہv`;lہہ=gp䩁􌤝*32 p!Iq)q$顛O0-p;@ *[xs[em to&o'$0cY_X\:+\+Sw>qY<}LJ)SRZ{b 8)>[;I"|LJF[lx/5x oGd:z47C.=@{:gwEʧn Ge=k蔭0ŏtksNpSg7?96pD|(r޿OpoQ}cS ,PoI?38'*9ȫ {i)W#3Ukk3~}$X<{s;lہv`;}fL{V|aډ48//>HWiyŘ7KjETB<ZD9@W t CP)u9r}|zzOFr>:‡@8ɞ;{Q%A՞=Z谈 篍SD{jb1eh%Y: #,GgubZWzDl`m,Q"rGjCy1͑jEWpzZOljϘ$܉ܷr'Dz~w(ß#4rv`;lہvv 32@B 8V>6 \ڱO`@SC0/ (6h:*OrB&3GKt짇7> Ěpzμr.5Bҟ>>@!ߪ!LhGɬWbpy4e #(f]b 'A}NR ;Ҧ@tH̢9鬰:oP?gGJO-9թ5rנI->0*7HHj)K~zҵs H~P%oہv`;lN3CjQ^wp%o\~L/ g#| Zje] + Cc] |H%2~/[sNRt@l/1O(`݇re\uQM&rv |cjqq?o`> y 49 %8~ y>Ѣg=b?d}U>c͐Do:0P,Sɜ0N5_WZ|N<9}c.?Ă0lہv`;v Ͱhhe3ND'asyr#n̚w+:&MLϯ1:w]Ρ5fo9O"ս8iXOy 1捧ϹZ?)1(yaߗ S pOh۟8c/W un KCXbv`;lہv/πb[Yψme_:@wn=W_2?t[4{OAD ꣶ̹hJݺ<]?0g>R\~LȺVA~&D}->7_)yYC>֧?KZ;ZSP,?_<Ǐ*&0c'M}߼[/NcC^ԤZkq7 >1) 4T0mO'ԍl/.sx|)d=z7Bc_4TE]dcϑs2n v`;lہv3  YwXE kx}M Qc7{v[@ ZC "7r31|F圏IWV04+Ox"a_s~\L83?/)#+{ '=4zpԝhKK>?-M񋛕_͕j?Ȱs±o̸\8ƹdzk>թMCiJ2Yi}ڷo5'NRb@7gG0|}3"]Qo,){;'9_wv`;lہΖ}ft@{XM 8HgpUkR 봟}  VxJ?'5krz~m/or. CVW4ړEf^ӷ%SvJS#R5ΚC5&h?V< 琣EOim^֑K/}({Ѷu/C>5@57S\|o\~}G'J'`z?[Gp+Nޞz/|3iMujIݪosE+h<>\W?<~s'9䜩c`ǃpxS_O*j޶ہv`;l[3Bّ`e5ᙓm> t@#'٩tXZpd6)^DKSisCBP OsTEtosZO[cwyb`͞/?R5]Ss;|n ԟgh|?ֽ~o q)ZS ٽoہv`;l3ڬ2jdH33i:_3C3x?Tˋ7- sƫufdCl?ok!}0G7XO8)W9+A(~V Wr\@`\8KBr9K6Ɂa -@ ztH s~{}(h`sѷ&/T@w=(<BA/(fnc~8)cuMwMg1eNQQzL>ZOd {槾ECԉ>g;>糗sk(0o EEjCt/{&BG aIך=*oL?KH\Sa\v`;lہv_gL ?a̕WNI?j@>0%ϥ0#ԙG_x>4i |mSԜx3hNJ:Eh=*ksd_)?VG8J<%fcT?k_ܹoC.4 *FSNa +"?^DT-^|H/c?8)|byW@jh-8=c]ÏLΗi4V??aj};lہv`;g:@ם< P' `lͫ}a3AaQ\߷r)i1-0:^@&l񧬗÷_ek8= 7?j9&pΏ\#ptW KuYJC:x-v-Ɛ>\1<} "Zpz\jGaܤa;=EI`$@ :K! g9jnM-E`Hu5%ӿ%Vr?~ wP}kq21mZH(ON/{lہv`;_! 3c9#|GQ}G3S5R+5uyJS%p2?"){uޣ0w~)^k`o=mέ51ːͩ ^ہv`;lہh3O2wv9w($g*Cn=*A/SOxǕ<\Ǔ#`\QHӡcט9{ؐ11Oe{n}|?ɸf푽ƺ?L8,pϭgO6GM+ kGȘP櫮MB> $rIIWjU 9h:ESK!8vrҬZ4 (h}l^O09W|d5#6g {>:>_)[_o>tR=O~Hɮwpc$DrQLJ s;lہv`;p:w͙Yf=mv6g5$ 5:vXQQ5=e=:;hϕ^>; MܕLvǴ hdmdGez^c=a'~S'=]=փ}/os'M߳f޺ӃJ iE)g*ux3]<\#>6IC!fU1ds3s}Mr7L(q<~ZSS7Go/TxBoV8^ T=GE+bW ?ύy%6?Bllہv`;|:w =  g¾N筒&}yjH O̬ITo#1עۏ0C7Q< sŅ#I״G#i^?_#5@GGϽN03ۿ(@zה[SCYP~(H$z9ÁdE.q[#A}Ս_I9oC'+|v_fgujOjpfM: q"_%t^J3{:G4>c%Ι}TA˞|^ڼ9Xs? ?xZyg2lہv`;>a^yH y` Fls8mH#|^&rK߂ށ8 傫o~@kZMxIG. +oj~Yfg+*2Ql$3/%q1Қ&-!]P(y\N']WyFs]e?<#!g>1 2ֵ;=qJmt$uO 8'鰂p^cր3gkG.MPH!NH'cY-g럨r>^ϗ_ r~׳XbS߇yN019|B{TxN0^^4'F9Js}N*'j ze~jVbҫ#~޷ہv`;lw u w@íZ4 z쁖ՙ:gpU3Ay><2ON$6׍b=<15mmͯV_\)'6|2WkGt{oť%"@IDAT|*de+Ztdؾ&&Uݜ)7}+Zxq&I/z֗=j!9GYݺO8x#1Fg?FC^S?18wn[k;lہv`;m d-fH%l {U "/)y!Ľlo+͍cpLs_"syGP)V b=ћ9=gzt/9:qjãgS#q>mP ?t]Ji9}ű݇'x4t (oe@s E52˟ W`>?);w>?QSh~pɹ_(݈Nc㾙u'G`9 R{mہv`;lgwH7+GJ%3InfN3fn3B5y4k3in`x۔Xu!c& T'wv%z"oz'8k #ӓ`^  (U&$IE`LQ s]ל L|:m]?5Na(^??;~~W~61j|`ɹn#ez&F88 1p}|]GpY (ԟ_Z goѦ\.{5X}{sXHM  ϐPMLW$;[CnU)r :_A:c:¡MdKDPj\+ߘI&}⦿|/&%b#>/T$W/=#W=?)bw%SS)r)Gt=I,?1:!Scc<1pLsyъ&}io=0:u89|[u_n֓XY94,g ??a9>llہv`;qîg<'!_\v<Ҏ'LC b` 0I $d3{51g:Qi]՜g օW/kp#o0g:x0Dឿ 8*F?}}=/sF)!>hQ@+3'h|Eicn/?sj}"p~`s|[U :k{9DA>fL$g o8\X6GUo)&_wso]}OH@ɷR:rӕ0wd/$W/O:BsO vlہv`;_A&;TfyJfZ)P֡L8/ \KC'N< hx^Rpq{b17+/{b=Pҫ>ys~V7ykrs}>4k;?W~&c??py|\7Hkq# }0\kXO ~Y&u3{>JI!z,<5sH (p 2d.: :>K+|4l#5Ѩ?Y'G<׬5@Lk;lہv`;y!8c&C fhT;  u03wYw\7O#z- ֳ,4Uj^ NE28 ,X/~|= ğ^Kmޞ}<\/oS>DeioJ\v9.M(s YfF7?1Gns^k./@1xj3yp;.W<¨7/ϳ E|P~@>NxT|yS%5b5"%К աLb7c;lہv`;诎3a2,юl;fwf3vM )j^ޚ[6CcZ`47(9ôBȄsYFC`|GZ?%؇ NTοW?%p$}=Wx|u'KjO}u,fkQ^eΏ{fKYͱI}h}q[P"3c_S5zKGOO]L7_֓S-3B`5p_qR$T(B>X~\(T*Sl>lUԞ̺XE,|.ѽہv`;l?; 93: 0ؿu~ Ȉx#BֵD gz@Msk}'˿A;(oM^`Hsxv&:>?Q3})llہv`;voJϩ3i=j4jwa;H9 %,uiIqwԵ' D=_ ,fcu&ܿJChbXC7=c}Rrbd_c돀c Sm~WQ*$fU1lm|4=}g\rteZ#M`oojX /1 __f|>?l:dqjϹwO`9KUmkIϩ>|NiO}.GS>Lolہv`;hVS&34d{6 DߋXa<'XQk C'+ϼAXSPWlAΩ^̧j9," o&v3J?/5EC 7 oǎY/j[Gߦ 5™r.2ӯtϾ1,k俹N}pCzl)&Ԟ2fTnb:>IA?RJ֟xΩVSdY "|+|kļSbnO9nLEIbU|3|v%szٲrہv`;lہ_;0t| d0|w3bˈڗ |8;6/h\$xjG89Y?ս緮pϣ?ƥ:!ĺci V|tb;pz-YU-,Zc1'b;&{ԷPO=ȏEYϷ׏nNRl/3d4SFATZ )>|r3Ig>\[0c K^W^'K!XԿKJ$Ɨv˱?F\ ~ Fpq 3,k{onWہv`;l; |`28XN dyF9=cbWN Jƻhl TdX1'zIͥk%Rf9&$%rgsZX+S4*ŶP܅)F-`oݠM?4B@ZYO#6՟蓵ss>N؋~~if5a?>7P[ϏL9? }<Ӈ|5#iK;Y>Jy֧TI/SluwX/{ߪJJWjv`;lہtd;gٔ3f2x0n:w}G;0 uz$?1_G$1ڲ?_zȹ0}@A_='Ǿv B8F# 9qdƦq>ŗo}SA!y[>IX^iƑEp곅7^o!7Vd 4*Tză$A;1dA9>ohVq`S_xƻyg:z |{q q}}|d\G|~`ہv`;lw~coU;`a v%R~e_?׉h}~AS5TU\Pêg/_J>QOOTn;ז҇Y'?Վ.;yӃ;5s/~BO}uE}sҷi>i{ET紶^ kό1TNWEkx|zihrn-V~T4kM?G gG? ujtc'"L@t]ŔwQm{8sRsr'"K"_rlہv`;h>c whX0;ff1`M|Cyt~B3$߁^~YF h'hρU<.>P<"SƋOA}!4k Xnb7{Mںss 0 g9?\뙪pQN_>ƨ#z>Bh=fOl7Z_˷8.Z?JSWg0}7w"Z1?ن g:u+0۷r=RZ)~ہv`;lh{fy Ʉ1wOfl=zgtk1NjEΞ/^:;8Ufdʎcㇵ|bΡW:e,BG^c`x5eTY.sD'սUs S9O'F\O{kǫrJ.qۯ;7|»?%GoPEop+xkO}'QΝz?9f^o\Wy^߬ev`;lہv\3H2t턧c~pbxZt(P!jH[zV$S:hX&@gna) B黼$QyssN4?7|򤟫:b| H[I+tΗ޴gt3Δw.>੯g5w{e⺌UFb`|V%Q8v^~@"OG C|j`6'gj4s٫zS]#GmI#OIy*c\Dz~^F |bIjc Gʜt<9mY>JoO)j _*};lہv`;ўVxdaUOIJ̔E8zCr@ϗKV ,3bbB5 /_"\.`nջ}s%q㭩+[T| Aw??k o]87crr`e5x-܈?3ةn=L 43/~p8wa^1L5FS#puQTZ_+). \NMh;1^+ROoW>[h R] %ϿCo.*4#l?o!׬G0*~g B'P v`;lہvGoɓ᱃Oߗg"DYaB0@o8|>|@d$}y5 ?3!G¸1КUJ\ƇG<*x|.l9&EB|hR7EjO1:s&D+'}ekKHSw`T>#%o`J $qs);ptyf^~p0v]_ЏOo0,3\v4n1G} ~T]C9?=flہv`;5202;syJPY0ʆݥFR1,fP>-JR3]J`9OuF^.·ETD~j P5_[UCdIP!Eg5e:VϞS5ZbOؗ8+^rsc& ,*a۫@&'3i]FDgƦmP?rs -AE&-|PL61QܿCL 짏>e>0f)4E79cܧJ8৾N) }^osUXGlہv`;v 24&̐Nx2apQxؙb׸z3{mĨ:Q`-g!hwN O+IϑSuNN 7rxO:plX 6J|J7N 1${-t`KX*{EP#[_kb22^)?9{mہv`;l~tuC$}yj 2= J9&gmp:82C(D.+kMΔ >l5uRW-#m6|l 'q;AO}2'Z_@}yIR>ggħ{ھs/nyDO\mKo aՖ4YOi2zz zO䕴rͺu@vv`;lہ22wМs/dNϧeiìOxQ+a~"Q'ֱ&Zc`.7~>$Q |gƽC̹S$RuR8zx3'T^k10' ~rCX: vRC_?_po-L1}^,öȻgo ϸoy廽p-q9 4X'?qS%τ"z'\\zҁ?w9>?1{Ӿ[ϕq|QL5X$ÓHf;lہv`;;zJ3Hf>7>;k{վQɦ:p!0S>8q[~ d~!E(af a7:y+v0ɵa gMX.Wj \<e:kcgJu^܋Ӻ2ԭ٧s3l~"Ge5}^4S0@cuuz&m;lہv`;?:F昼0y%ѕIg0%_Ff ysEJgzbtiFs 8GOpv zӍԯ_ >tjgZ`>v(^ -ϖ45tbj8p} p{ĎJz=w7p:Տ$}*Dغ>8F3ӈÍJ;\:h]| ! ڮn%<(Cwe_3Qj On0hg}z"jrp0K2? -N]lśc]}1iS̜>Kv`;lہv`:hy+4oxX͸zgUtHdycv`3j1ukTlr`@S; 4 g|_>5u_t*3na/DY=|}?yw(|>b-1-ۛWC :|`SYs@rD{Z9sԯbzvx'>ڪˋ[Ǎb5n}tw9U0Ǜ{#"^.-sN}> _}|?Ԭ1p9iǟ@ng}ֳk;lہv`;ϋGlO 6zg= 3K^ 3pFT w{.DuK[J%<@$jBQqSH;)"[KH|c5^&^w+o֫uO3˾ T<`BLO o=( $@';߼GLeTfyX3} E!pF7q$=5#̧ҭc ~j]樳1YSrΤlہv`;x;7gwySɟy6b--M?18ډ860{RV ~1Ǔ15%޸4 N10?HZ~aQ9y'd$or72tkVϟ"Hc3vi+1ڱp ]kr~Iq{ܿ|HTJs+?ZSwn/-*aA7g|T$xY  ssj}ßq5yN6kM~:&, {lہv`;|:wQǾ8hᗠ 3~h݀q߃ fؼP $\ٺDKr̼ףkn9u.'ːMJAn-VȽȸ>:]:g/xN>q]l_i8[_Zo`UaL &:=qLQEuuo[}q_Xc\D5%o~uzb#֎IeVw*K`ݨ"qTntOUk}Г\c(⮎qA=@6{lہv`;?L4?CRSOy'CQtYpNs L%0$|s|GbBϽ/Բ-+E|>ZJ&r&8+kt?=|t9A:SĎ xb{%g{mہv`;l~v3 =jWHa@u4)2:Ђ$mqeGAW55&?~z܉@- m.L

bEv;xks1*7ǦէN!ہv`;lE/E4Y{}Z'1NA}Q,:4 Z\;ƣ=~d@NHarcDw ,iLx:hHn4\԰>o%^ Fׇޫ?:bN|= ^jNkį<`{~ƃ]@ZkX'(c}3p??Yw^-P>S[7e~GR?}-=NkV(`_ٝon>쭏5ivy&\<գ7_ʩ oΫU)/LnQ{mہv`;lo25f'RޣqNAxj+$|/c$:2dƐ,xbH&L֑@Zx9?ҷW~M]tÿS*&wOl25%9WOm Q`^#=HT>E)ϷLG-:f>AfF;܇~~>Ӝ(er,t)9gD= ?;56Gn7bB?BTtJ5yx m7XE;3;<k8;[#Wxv`;lہߡD0oI~0a{w*"K kHy֚:!6ܩ#2B =9?G^Qz4`O(ꦰ;۷@oY=O_SSa|66*=c9Ι%| oۺ0K/i'P^b>g퟉KL}k~|'0l+糛5@ͦ&7ps/ 0OZ:U&=E-4Mg3{xг,zх̹ʹ44}gJ"2"G!9't- IB6gQ^uI$o A?홠Pp|V:2j:^Ɛ% n/v`;lہvvugMF ¬3^\;zF%$\ 3;&|!^Dj_Tfsq myqx Q2/T$IZ{C-g_vLs_q"}8R|0Gi91|CF$f7D79LuFWisDz7z}ǨH[0]ܢWN}<çRd$ !X ?/zOomr҇LOz_>hR؇>{!OV\0$DT:_-×8&v`;lہv?CK#$CyC@$09Z[aO >/phpᤦV@1f"쏤/.Jo)ĵ"9TǮ냁 TwUoh'lP9?au|{3U{4r7uLWͭ3ˇ  hac*:V0˷9>/\nO^sJq~`|/zpuqqa:pu4Nj`ĐK4̧rL\[obxK0WŤST@Yh F1~/S/\cռ}Tl@?Blہv`;'qX}%'l^;n2j߹! aL)z6Bz!"W,A{@?'ټ* Â8 8,玆>/iThl\^7%sl؋|cYV8]>7ކ`Mcs8eG+QsjǺ%ș<bZ)5'XW4B!5rp7Bg?tw[(rupď*qq8̯@*3}IrDt7iXHh%Щ|%&KY9ɘ G$s>E)ǀ ɵDX Cx^AhsI+N}E]s^L 6e+kwƊ6+9,)?,::n5͏G-u‡֟?8Fx ۣ]?p5tw޷1ƛxaRr~u"Z2a\$lہv`;ف=l{@MC$; 3kLļdlyN(0WOa(=IINْOc8]YmB~{-Y0\)D41=kR|JwE*zqIK8[?:ouгOݣ1g$g2ѯ7Tx5|D'gӨS.d<%h^3]O0T=GbK` *G,o(|@Uv`;lہvt gTLydJi^26׃ 9041='ww z^cu1r2\ 3s~5=(5/"֧kۇn f5?h=b;Ar0^~dɦNjyO/ѽ z@p+2?N^ ^{O[pRC-/4T87PjfId8O^vyۿ<-6ydč]K<ۣU7Nsvpv`;lہׅ Cydd,FOd'nOC9(!HaR ~7OH.W;(R{2"f«M ־/8W"$o1`4tݽ9#嬮N̪39=OP>+ء.LrJ:OG%iѭOqpV$'&?ԥ5vo}t_mLg"2H uƙ/ao_{}ᕊ]>'Zc7Ǚ'b3b.Qon s >tp?B%lہv`;!Ӭ"̱|1{:}4FK81#m{7Wڬ(*Oyo_߁w5E /] <=ImE"OşM\o߉ o@W>&}xy_.hCޭ~GzC.m>:2#^z~Ÿ~w?ڲ!1_qÜuSS;qh^4 Oc{~@z5Po<ż4$@IDATNI0Z?Ogٲ*Tc;lہv`;0ПhkeoFKy z1n`E\Z<pP9Cc_l`;S[ ܮ'mLo?)?t #s|V\ޫ6EO|6a]E{ﶝɍ+N{?r{Ȕs}*%I "p=%-X{}z2]L^J9c5+/9#Kvx7?ibB凾\A)?V8Y~5gј 1w]I 9w ,E#lisOɵ):G57cQS,G-CNy%kPZ#.Ny˨}2;ڙ(;Xy!v. 8>>.RN;ۃt(' !e*;fMqM,oSSǐmz`}c#o_|wo/Vudl{iOAi3'oY_=D{0Ulϥ~3V-q0GM;~D$#[ڞJ=lNcgZ݀7"-,gãOǯ8G]_Tӿ;Gtos'y$C"b} pH\3;X4uƝo~㣓Kq|⮸|#|``8bau7gԻE{_Rgn :֐yk~̸p Gˎ?4t˿vh' [N\/2ם'Ď禝+GF)TR{}Gěݟ 8~^QP3_ 'mڸ,!m M¸>O]JWњ?8F/!Y~$100k3_J>#j4?,򳊩}r<̧x63uuuuuuuuuOpT:ʇIM ǀ| ½d8 p1VXzw[K"~jl87šk_"F^єgnz*0N4)01)&e 2db=DoOԤPS4p~믜j)eӈ)vDV|0EY7&\jز? †Η#*"5uO0b]y: UsxSQd.d*"G/"đ~HApD+u4ךY-> "\l?k ۫& ܻ߼X1@2\WL:4r`)ypYmJ6vx5r磽 KU|Ţ\rf6^1[|N%*.cns4^/NWo?:vX(`􌓋bD]a^!ܤ!%AN.pT{xR^(xgy5>?!d l<[ ]< ʺ;3"J{y;h<|Vopj[!8|qi/t+x[13?oh߱c>բ7.|0\D>?>`xZds͍Ur|8uuuuuuuuu;ښ1H0aj3 kLl][uˇmgM;i ?I6&ͱw& r[>7\>UemMt|ifO'o 9=Qɉ AtMѺd>~A]ce'`Noë  i-5a!fY'}n)ϡݢ'ʏJkj\e7>tNӟbU[Wo09y= ;=K~Sx(SO_#IK$pI]Gs :1\2v(~A,f?{\,~Ǘ_l{YvNT#5|Itك`wdtBY͟gޡ;I1* nDܩo]8֊6+8Fވ/w:}vՔԇ݇L;۶1/ʯ\ |U0 Gʩ8e$Mϯ$U|õy6>Rpw>uNڦρuI%?3;qs%|dB;KN| Ƚ۞-P.RA ?ק]Ž-Wf*LfOvFlt^4Cq (Q2PR2N*˅4LsNi䏝*Ń.}0`1յE|}P'lc^~j_#e?Ggӏ< }۴ARYgO+&'(U|a)씾Wo:Q#ϕ:VµFm@uuuuuuuuuOG{<"èH\^2T;yXs1lbC0p˩Xm`>Lt3l-azB8 =B%GdcKtOAZu{brڈE,O?g19@TwV1ք#֐F}z}$iD,60?08r@t|;{gǪ&uȟ+*#ٸx({5q~;9VFpsKɜo8^r._;@g|q/8'nu_5g8߸U'^jyZCd%Ȃտ5++@BzW|و#V6>>7^ }p'?c*źW.W}V맃<4qijz@;mxd3xo͠cg`d,P*=t&bf~'`ܬ-`h^^{OΤ}AFBKh# 7[Ř tƯɱ^oR{iHL! $QM" M.}.BLcҵm5{0=Dɾa%/b<vW|9d si+Iu)PYQ/ܔ/$kD΀3%w[(#MWLN칪0?ο@/]{f'?~~1 U?Œ?bdss qBmP;-ۿͅ`8,k翱]ҁy>òK&=skd] ٽ4zpa(ي% -|eNd+ 5k4 ͟c԰ &ɀr?XNa z؟أ&Qg {^'BӔgv(Yտ+ꡯkxLݵjӱhq ~i ߰$@lfѪdo}8S*/3AJ&>ʾ8oU=ɣO,|/; 99>9aS:)Ǚye \s.T|g"f~76$t7"đ"3GƕUS_|cV5,'H0 lz{N\ɘ;!iW\)[͗ 匂ێsw|+rZoW"e-F1) f!D*,;_pv^kEo_(_W;Jk*5mi۳?sbcWO,gLs˳ˬ)Hؑ38{_g/oyxxxxxxxxh s |i8=s}Y u e;_Yo@k1 2| WѺl矘?T]PwRNGrHުy⏉/]o<ٸ'g]ע=:㦍*-CsZC㘛jDW6vap .~ ԳNՏ[?\g{=)g$?ɟl4 g`{>fX~0Wٗ?ݽX??aKP=k~2oJyQh'gn8h'~2Ϗ돻qׁׁׁׁׁׁׁׁׁ߼DU6 ㅉV#=\ S1zVCw+_&bUSvpM`Ҙ\XkV暛l+p e_T\N׏Ez.|.y+ Bs|z^z5qsm}4oϯ|ᖏ]jC?4s6f-j5V4q'4A[D1@:KV'|7X?76(&VwxF׺бFJ[{tx1 (]Z_4=VB4w<<2*LGU'NsUV/׹R9b/}?Z nA/cڸs7| 5w̢7~o^B3zȳb&R<%/ae]h ؉٫[+gpI@az*,}U7|>J2_ZeqV~* ~HZ8iaO Bݕ=,J\<W_ gb'˗Fv^;5D3g  vS)Jٛ*:C v?v;<㷮W6[Y8'_ķw bk2sT"ԧr@;F`^L4g/{v ;B@ͪ1/5'@cѣ?"1PeBKUnd?G78cU q\Do 9FU? 0#r\d2:%{|fZe lNLA;qr}Wzx[u|d/>]UL*q/qF߁gy68|j [cQb>Xա+͑/Q` 1Ou&>ǹs}sPK#6_5~D~^(I죁KVǶFH`d|Xׁׁׁׁׁׁׁׁw@mOc1VmFA|{1 0&5J@|\{8"}&yroz2`XmS"%t)\rU!/jo{P?kqCČ4$yI1oEH_%9dKOEmFOj?[KWқۉ˘5`p \;_iuqJL&bZˍkŰ+'_.> PFZoċfM/]evSs8;s՜[ w{xxxxxxxx8ދ!U84ha+_EgˋgV&χX< 6|QU9Jyo~^|jX%2, ׁĆo&b}VyL <ρtO+_p6O?ZRrG;;=xPĖngۘW'$=\;'^aW.S/8o.Me(AךqKӐe5 Z(TGqtM4^`HrUJ|~| \5Vo3_W!庴`gqĪ恻> %$i~jW_90E+sJÿ)I W ֥0}?կ8Yb17;m-YTtdmKs1O?AsEAwҜ}WBGϯwcQ=!5~57|:::::::::OλAzs;jRC=g+6i>Q&56{"qgNh/kxڃh}KYcY‰q8cFX\?n-?3K=+͍%1"+&ɒclgJٓikd/ͷq3j ,ەbҁJm N>vx㴮щgAP3g1pp2&o=G+ns۫u>TS 3qHCŌq>;*"o=Aa[76, qBs|:v";0u!C$р0@;Ulh8LNkX58N.Tf1nD3}&o90w^,6M ^}9竟8#cڇtk~,N|𘄝ӟ3б̷X˓3_C:N?? ?,Z˸t^c+ W3j-1w+'fc@5cK.h4::::::::::2NL>pKUXUw+gҨehŐփxƨRnp MhsE|=vݗ~4抈qx|qtJt.뷆 1'ɛvlAACVWF|dQl ~gY{SBz>x~^.@ɲoʬ ʏ{a#mpDL]u(6J5NVǨXQ~pB]rsic̝cH$ÿrعe{%%X#?vRm0>=S #Jr:::::::::L})a0='=c0\2rl&g3/\pYǯ݃0;/2ߓĬy)]?r œ"e?!|Wx??8sm9YRB5*_!' awxף]54Mygu~ `}GbkG>?"kŘcxK0qxGՕƠ8ɲ͜O%;x!8+սw6[CtzCLWyq[ >_6ُ$Z'ᣀ>eb=\lM|Cк׾)6)kquuuuuuuuuӁ25^j=fl;vm5pMHi&td؃h\5 i}_L8xƌlqL9?&:S-k1帮ynPS{%bk37?+X*3J3v*)-3v6F#gO< RI:<#/1/vTɏYҳ[`xs24͆: X6ZʩM_I8+}%ŷwͧ۽>DN55"){rldAk[{U\%-`Jl}&$^uuuuuuuuu O*5zjdaD3@ځuh!"V;ÿ0tnƥ0JC'9%_J#0u5aG,i|!'rEbv3msv s9sui11꼜ڕ(PqҋkK r:$'Nʾ&-GNqGu8n^&O~gb%4g/{^r]:&g矵V .:<-7R?~`a^ͯgO7wҷSEQ?uѳu/7m_^^^^^^^^q^0VPO1hvPfվ-hˡ/~)'F`x- e$P`KVw wlSO%'plX7ٹ9ds#{}WyE!e?[4QUP?痘fNIdFl(6eƢ*2#z_Aq/TVȎƣچ}#!ĆEvpt3콳 ~IjĜd!z,nN9 x {!}s2t\'gԟ/?]±܄ɶH{qk2g3Nm'8|Q$?&K|zbUgU囂boKb>4#8\sN"{+epg=?T Q/n4Ay|w,d }zzshܛN1C}gl/!ߵc.p^6?k:A4o?>Ϲ>zxxxxxxxx݁+֪U.&4{߶ `{yf,k k>/%õI*+i8fqA.رN}o.CHI<:uj||aBwcKȕ.ul1BAÜZܱIiDm Q<6f4;Z9w+4 1KWK^ ֗dȁ)LW,5a%$8皍MpOݛE1at~rVZDjA5Y 990B[b?q/[kr\==`BڦVk4yI-tm l猩cdI /I/a|`;|_ƕU>06> JL|e*3g#䱜 (-Hl9OV8~"3[Jk|K|\yId/g?'ֿq닮}R7y``.\BW568$6|HoٳѾvi(ŎhEX#D䶨5S[c(]D|sO33맦P]#&pzwMNպX=Qۿ]cXO or#<8ٮ9L/oVͮhk[/}xxxxxxxxt@/gd~8%CVpK, c6[Xl|3^9NT1,ʏvNDm}y^#ر8 ,uN{箍O;&땫~8^ GTX, 7*6پǾ:UB{3 ̻khB^nUi]挟>{kOnfʖ | &_󙘍izgW;=<ۓ V(V'Djث% kz{>ZO%>_J˱tNp[ȱK'T`YI`)?֎ $˕ʋs||ׁׁׁׁׁׁׁׁׁ?t?̋tGFFP23\/2s {A&/[o'Mӿ6datx?l9cwSZ~Dt1/PXӞgXZXX[@Tj/}:^ 7Bh>9Pݸ(W1$6P!ca}]qLK&RG+J6i 6Ey8(qw }(e~׃,_c6ELqglj۽\haSKczǠi(/w u2`!0RvO :ȞھNyN|0 d\0OmޗcR.5hH4ϱb (@o69n&`h^xRYKna3mPH)Xi?+%[ +@:=bgmҴZ߆DS?X-cсhp̃>\Q.Dd[.e$q/ b@+{ٛ_Tb] ><[i]dLKC?h C-B+A#O|??-W[~q2eѕU6/wXol5IÇ/4)U'G✴="-"GdE _TpF`+ D/9ZS-dΊ#8cp_9ƇBNNEe[?-ǗQ9FZJù++$/[Ȁ?tܩJ2zu.6;]q⨙Z-`Ǥ\Y[}dvtW^8#n B6V6?7~_*jj;¼uuuuuuuuuhs=#cU6C8ISf&Ls;x\?" =M~_C7"/ϴOLku.qjҀ3`-o{qiS+2&)$ '7L\Ixk(|n B;K3J^*4 EdZ>aYH=6X a::::::::::~U=Mi(f5f~aX nAQ/솽63#eK@6h#qǗof [Hscc1^u;oMGp/ʳR9'kA&3s}6L`8 ,e#\DF;4~˟pdq?',9=?z_G/wjeS6d Bץ]? ŨOc nCZm?ܯ_h[?߱?j*͞u9.F*EWJ>gCl^^^^^^^^^_h{`pg)'nL._dZEO('?|)36ʙH/zS`ўԁ̅"VP\L8m%/c8qq OwT<|p::8I)ႁ髟'gqИ^&gc0r6p6Glw|=?oHL,OΒ3ԹCI,=ڀԼO1Ikߞa--cSL0{a]OOo%e.ag 1W_`uuuuuuuuuӁ[=Xj\w͡x F;u|Bm%i''te E4L1KMq|αuྯoWA; (OYJF)r]5'K"!W9m,QMMd\D9c>$Q!(6o^^Sj'N|.2|^#pl]}b&cTK7Y݂ϟ!NHNpuV,VM"_S\|\į9_Tu/#@r5>LpPNoyxxxxxxxxtu)Ys<߱y1~1Al9;wm'|kU+掚,kq"|53ħH^Vn`3+JkYbSw޹Wa ̟hΟJWʑY9((Z jfo r?N!/Bفte?c(8?e/2. @Ѿ7X|[yxxxxxxxx8_yFT g>/-d|PU|7 a |J-f`G_ғNqSv4$z//87Z#=[\qVOJUM~{.qZO]-SH^~G@UOێ֯pqXP6\ e㻿K=!Lz3b  hVsO5FЁ2F(^8 1&u0fVa΀8`ftU;nh{?UK)W2st&ٖg92u!,L\:KKP(@IDATYxV&m P1-:8K]7F+2x Xz#@k;i''<ʇTО)@Wg&/M-s]elnX]Aq㋞pFƌPi;O=;2w7oO Iq(O??$m|o'rcIՁ5;ʠR/$ja7k/g\P4~ܚ;=bEs)cq"qx`^vX9+Gg2/\Z2 KyrMG?!c؀9}B}~ ^vr$%_^^mQonͱpseU 9FŋOE/JͷMV>:2žo9k9|QV@tu[ Wא ~y1f%H7QpF8sh|Nw/!\zxxxxxxxxՁDvhIͪ;15xv3z@$VC})`4lvzh20:.շx(y.eW\M@W_$|ʕx:Ybe@c` o?I/%;Vpy.{鋌G_O ]ۧjz.q㇮Ońh/7[A%3ʯdٺ+ME;Y1=v4ȴ<%]"g@:e2 xZ{_>:2>|0jNL.@.D!o$kz+8f8r6u%sFÇ0XMx,2jI $o"krK\ZDRj& M7FocdT3%ENȱsWS*)i,ս а lM^wG|^oCESt߽my.#84 'pQ{t v$9\9Zw{xxxxxxxx;IN_3{c藂Λ O8{Ƚ=x}?g5xٻ/aƧ_U㐯Gy^}:9Q6mA];c~3X;@wYCW^<S@^+]1/w?L0ۍ>GKb_kwuKޠava]jY)S׎Lj8f,qςp]|MH)m8٘"Kpܴ^ٿ #0%?.cW[_^^^^^^^^/L `ѿHαЫܤnͯl^;cݡ^ 23Y{ _Q+??wD Z 1kkHćB`>ԁqw] ŝ:F@ 5o}pE7O|1[hJPvA@iON'CR8럆 }?_6'ܪ=[~ɑ];'q*qONWkV%~`5)8WTM>lPʥ7 /8"_[(>7ŗm/! y*aɽu^v4 \rZ^W"?C~x4<$׻Dgd̄TCM7ss3Gw\ZaІo莚w5SK/L||'sԗWVP0y \Vdvm=3KF{2>9k9vnek%b'f6=]~r(<}:[Wx$cPfc\ֈWW=.<"d ^w4FM9̟b;|?I6t?1XY+:::::::::pu`Qhκ2UF %mNgMHdXGڒU~\jy/],/l]|^/M&5Tmra卹Ax # 5WyGũ쟵)ܙ>o@ε1m˯[b>/I9:NQ\ Vq`%}ס`P9_ٯV vN{#5>r:Fj~?chÂca7շlS>4l\,CLg^֋[9M}ׁׁׁׁׁׁׁׁׁt@c5\zÎWK&8V>/fv{sXښȬ~loq Sp$E%X<%#JkNGQArJԟ;UZ7.Ef)fAc֜evc˸ޗ(Y6ڬbh?k ԭYYupܨy{8XZsuίt@F3iVX҄j/[vqi8IVC/y1'pV6sbJ341s'itbEg ;Q//8H&0fRc+ HRN`;<Au(}s@t$~G'`Ty40Éu #9EK,Yxn &0x;/b:Ypq+?FwjGvɞswsc=Fr8ul~ (\r7oV4{lC>(&óhƑg, ΅%^6&V9[Ie0wׁׁׁׁׁׁׁׁׁv/<;:Y2XT 3Ȋ53lX+p=G lBi_YzW拨/ 0~V, s6芵X&抌]o O4?|9+C6ܺEϢW bJ{tn&>T{W+&:z 'r·` ꏻ&j6U s+>7vg2ϺEH5 ?⊛U/9(=97SPyh/$z0N7^^^^^^^^^~v`^glPqPsꑙz{RVl/n^G*#]m;V8_-.I9 r mNˆaس?Djet.l fdtjRA\߹6h[~C+չIV=Ig扔bC$r#\Awߓ|֍aݹ;@k۫*}߀TjY)[*89?$~npڧ ,+i֓Յt/ȄQU3vkXƗ}GUY=݂ctZm{ׁׁׁׁׁׁׁׁׁtoLXvÚD~1SK`}К0zp})3@$9>%m=a8R|O6|wT/5m^?j[klna9ݟl"r@?GDq6pͯy VOqN4i \?\/*կ15Wè6V٠gB2{ţ~~qO=Zɿn f "gso<_k~ô%܎=H-wyJgţׁׁׁׁׁׁׁׁׁ}2&s"5 ?$zzW"^G_넨z/x;I= $C ]o"}֑`%[+q&Nɂ%ff;Q\π9F+4_8_L<=EOqLZVs;呔% #T'&Hn|Y%6}%uoo=B%285~sqb,w$a.z$ 2J u/պ9ԉ363D '%PnjÑ瑸wSgbM꺄rqI.Gۇklߘ[_^^^^^^^^D`rfkTp22<: j],cy989R?OLΊ_9%αc9za.9^~n,jY{j2(b>?*)s gK+hn_yS*bO@ D~n@-K4c =!/_l+|k\k>?~b k֞f q 9'JI&nUcwy|҄urέvw$Q[4G˾j῭UaG?:::::::::<\ϔ<$25bρX-g|)N}aPKrm'a k\}.]MY|7Ա!Bn%~,@3J%JMV8h %r6z%xk@crh'wJ0*c@u&!(\\s8?yd/L(YW74q6ݝo^Ӓ$\t7+8ˀ\˹ٶ񇣌r_kI1N҃|1S~`}Q>s (~}P]ޫ~"8w?hbnνoo}xxxxxxxxH毎AL;3:S8(rvAaVWD_uUAzH'V ,?u0˄03w]!@}~ Nԟ|Wb_yj-l&F^"394Nv5DZ/]p*9~0#LݚC ϸYLqMбw4ꎟGW<~,vu{nƵvxԴCgc_U9,U\|7_EY𖽚|pWkcg gel?ɉx>݃%O+W?:I``Q,%{[uuuuuuuuuw/C`wP b$8):E{D^4^J9Hi݃5d{'%♝G`\,|7փC?LRyhOxv.r̭/- P!;O,%V|.71cCOؓ7~}z ,y>`.rn9 (o,of4Xc6i$Pl#%ps'F7z>__+]nswߟ¥Ҙh>^_ìxuuuuuuuuuT09كf힡YPc`W'T˻s+rI US@v|;8'4O|2hM|똌([ 7B`(Kkw, Uy~ ?'iB (w; 5_?'Ou+h'>KJg ]ؒ:@+ 7wb|ϟ歯W;32v3022۲YwPpU?Y}l/U]qZvdܩT1OC'âr@؉N ׿O+6|tEҀVfR/A,ʹ0[\2O>T,;m/[t%sBadD3虫h8oA8(?Ek_\y(:::::::::Ea~8$/z>M^ ^cKr>[qM6gf'|uuuuuuuuut`61łWΟuqx5p\0hg5>s|ǹ͉:4lcul|̱? ]?~Դ>j MjMgI0Y%i`dFhNM744hLDS^ %1%j(XZF+zhEr%$I+@>@87Ϝ߆J70Z9صs\oDSbJ?Z] [%tJn2bu].žt`:0LӁt`w}`(K/Fm|[=fM.LF\^t :|;=_^KGo̪La0Zrl;;bl~Pi 2A@,1./qKfoŏvUH2\֐| {ɑ)pG|U)% K]?3KDsP|ù<0|K*_[C\%D2evD$*9pHܯȺht>R>S Z)Q[|gݫm^_ǵHCK}x4W{J۞}񧲙LӁt`:0ہΑ8m쓇I%?:T^-~$YYӁt`:0LӁtS'Sk@/m։Cl5b{%~1NCɽt w6 v؅h^F[5~^6|/ʷl RhWsz)4Ŭs*kGPr\[K$t]:w]-/{q%~--@]4d8`IJ\ckD:-:Z ﭮne9_ P%W'_s )̯maW\4bZT-(o$کm+}K|R K.SLt4{My6):Fc+l0~rlӁt`:0LS J=^_bZ{p]/Odv ;3xo> smsMruI "G\)D/WR9cbt3E:H'VԵ8G\: 9UÊQEL (l}79^[Kµ^_ )Ԏ_P)@f&ik\7䦭t25B!{<_xjQ?+iȧ:2f8MV1XlUGISq`g^w=o?!ܦӁt`:0Lԏ3z䮁 kd-p5cP^f KL &(^CZ.3X9x8XRڝc k&l%®VNvm2yKT߉q>Q^2~Ǘsaݥf8C&}OMʱ9E .0+hg/Wλ6i#DYϊxy-'ZGewԎ&&2YϿ5V:\]+x h3tMNGSwi{OW, Ӂt`:0Lt24\V~ץY琙}/0Z˳^kQ=~K*(Y˻-#|ei^߀創K?qzxo pK;pYοG<98ti{ҕ@ؑSyD:-;8ǿ\w%Zt/ KɋMgW~_y`e 4 gʼnn9$gPs-;:Ǫ(%܅otN\q@\ƫSWVMTIGb`s!_z)$%̪ $j_ϯN S䁙/b6(g#泵Ӹ gЉz XVJtfSgde{WljMel~E~o_p71/յYw*QCKǒ?3.qNCv^:Y?quԿmUվ_8Ž>Uc~FZ;2ʌ&{K2bV>0 Lt_s,LөX҆9Ʌ{+?ng}woO#+o}?$ls\ȪVo/񕛣zG3/Im bv+TUw*@T\/lpYJ}\# WILӁt`:0L< k6ʣ!D{OV,p]D^tu ҃3`<0J|Sv 2e\%tT$5 C"R9F;q%z`J@@rI (d5 ?:P:t-X/~ەxD;I'gha*RO(7T ;滲\r_%ill e6~~ޮ ?;/%p[]wŗR^~}sן {aK ]W_N$'~ꩧn_7w~-Q❗=yrW>3E HQ LjNybWyvVLIĊZ_ĒT+͕Xj$GC';ut`:0LӁtv{@v06+l0ܮL:¨p9c嫱V { / `^V֏9sV% /Vz}zνVW%$3hKF;~[]9YJcZe|fl_8&Wˣ#jO GlrW]$yms-wp{ZGƒÞ+_b'^#=; |W|vZ/O}[|W縂eNߩQ#~';$I]kj ||?wlA6Pemccn|]tJCJ$\Ӂt`:0L:P?:eтhg%wu졷=uc&PLŖ!8 ~Kp^l[nԹ_0YtiqlsAЉKeDopu_M e@H`փf|$+1Vy4|\Iu DCvʣU;N׏1a\LW/w^XQSq:StlQx׉~K_r藿x/~/y/~r{K^r{k^}u/{R6/yiT-1zg%s ^`p8'L?^%7A\GG Ӂt`:0L;j |_fKC~uϒV^H54naí7o l[]ڏ!OM+|_oвn|OjK`w]+1r3(o>,:kSZuK}צs}rvG gy<ڦZ^hb<5@2'gs\+']}䭼ꎂݥo'ZnW1rK|՜O"GO(?/ {{k_sz zot_Dz,,/ GJ?"??[?+w-D+5Ά+/QD v"HU|V]G6LӁt`:0tf6eՌY6 5a2vƦW'$1߉wk{VFٜ5$7G.x5 dEHF&"Z`+l*.ۅ ׏b~ `K~A}ٺdrF5R &?ҊݝcuV|]y&1>]t&5b%c [6F\bC "Eq,W\|! njė9*PvK9Wwg|_̷>u{jG|DJO.|a<9bթ{!zpQSW+ip@Yp{`wx3 qB:*v6;Ӂt`:0L^XHss G뾆t5jFL bwfa1_cd[w4L+OW,P4kV*_uvQϸѮ_' 雚 ]8pwoOF^?wW>c#(oj궕iu_\> ^6}ŵ_[5 "u. /0'j8+`c$ z|<'lӁt`:0LӁt_s졕9L˯<8s@6Ck #ŇCo󛾢dm3 ʝk@ƶg^5.% ^էus .r*nryi藅,6Q(WNэՍ|CK6AW?Wk6woT7JpՒ]_q|[7BM]1=IR1?X?Y#55&۟wՏ=7?o31sSWWdzX-ݟBAԃ$fV|^_Qj4t dt`:0LӁt݋v -I3k_׿ k|cYO 8X[Teot0rZ:ӏk+ s'&tA ^oXaL*.F'6ަ6EOP:mΟ$GZmHu@^h*a ϖg^``j ZHM+C3S ϦіƔ]k5/ \LZԑ`w~>oOz{~Ý;9.ZSzϯܽIpK .>vlNSKqzqõ>hGsoϏP|66ynӁt`:0LӁՁ`K ̎$=Df<_暎c+w٘~6)-kOڍK|}[Q/\ǗJ翇c” S}W=sn~ $웾㣦~[8bQ7ݲoeYꫴj[LUn+RO!ݯ9͓.x6_S(*'Y;Ev>Jhё ͑fFLh <yUfu-_o۟/_n~5?]']4PpaEK-. 1lϟ"=s3OS6U50J Adc]7-u{]/*k+PS 1ϪzB+%h#{u__tZQmӶ B=|r,jw UN`o3˽ujJ1%ڦ$tg}zVH q,vKAKʃR0?jB4Z=W$;߮d^jh J7=K=(rkkrM2Uuֽ/W:-q3urVLӁt`:0L]/1z)XiON~a J/%co+k玏cp3 NҸ5H@^^%HT/=kW]35^G`w drI^esg>,v"N ry-S{{'ʥ Ӄ14\AD'`Ǖ^tJEH.puV?Guć߂ 5`[Z_?//M_6q-P,r<ル53:t`:0LӁ@u)d֐闝S*GMæ5/^ :kF 0b@IDAT Gi)z0Z:h_DW2R G%۠i9g\;W.Υ9~qL/#@7k)>gæpݽ`ڌ!flm3_=QqpRȺWkW-g¤HXӉL|q,;_ a|춧e wڍX-Bp]iouݟ/}j Jx^,i@ﮭq`Rc#p+ r޵/"w~ٓ@z]{6xA~lu8[Kǂ#at`:0LӁt xJFL2=^xLᔡsWлdjs+HΑדּ27~l=ws\O'5QuΜpK@.eɥ.EDrua_,^q3~b#~N@xSu}t`:0LӁtE[3֦v<؍rs4@/['`6ZCo%0 o֞ő&vнaek43.{Ȗ2w+h/|QD`HV$d;?=P`dq=6#!Y.vbS9rH:vKG/ 9#{jդHiDp>U'/˹n~|w%I_e}P/]b{| _>Z}eRG'@rk ȑ^guo|,NIf%?A2Z:t`:0LӁ@hgeAaU!32xz8-Ny@ +zs$ؚ43c2Hܥq(VQ?:8٨Ÿ"&[k2jfq8? h1Z/ M b9&NyX\YhZtX*uօ%  {חX`Z53\/Yz]gi'9&!{#ttݞ_׼%(_x;o߽S7?vzлq_-_tsIa 9>+|9N|3^bMV9X-dNӁt`:0L.xu L0i,[TC.2ɋNVlVz\' ˺/5./vc嶖zPK};sɐ02 MJpzA+3蟸33Mo^磛QI0}zz`Wvµ&x2Soe۷~פՁWg|K3UYůħg)_i9g&65p/r'k%te3Qƚ;t`:0LӁ@:Pb6}1kHՀz=i/+$4rx<.#0[@{/?(_1i V] Zg\D%!4l#C{uTĕ>)2 1~$]6\Kf9Ve x*GQ)-K*q8F\8ȤEb)ژ X +`˶'q}%E4 !9InU+'>*q/䄯.srl^-d4+v<*n:_{gQmۿW=}|x&XuY\Bܱ& ݛJ<|SD][9o:؈;9*>/3Nl]9CsLӁt`:0<ҁR״Z`Uͷl]=DC)W'g-85Ubjww5 TΎBkm3'JꞰ'VPOO(;whH78zlyi+Wr7t1nxG%SEUIρ}T.Mcs77T'V `ʩ1Թ|~[~!O/z^SR\ƻy6,7U/Ϗ(EJS/5eNlr(A~k6#;>Yjld׊t`:0LӁt`w@/ 9{U?[>~$VC9f>P 4͙-7w|F\`PK>oels58ׯkm)z 4V;fE\W k%&9/*@ML>&Z"vrY^kJ={3k%gדXzB̝uEnyW_9Ju'Bة~q_ z;go^^˞kIFn~gn|{*GpLҘ =?;OJ,%cz3k'扻;cE8/eV L-5ZJ*/o5LӁt`:08:PZ2j: s5m2^eQ1r+Tbh*/s]^p ![HXOriWNFph^%.lr6zp ʂ*Ǫt{+K)p`<7w:{S$7vT09Qb\kG!l1đjIl~_[-߹Bϖ.zn_otGy|$+e]jO:v]9V5<3Dcr,S.ނj;lR8=~[!Lv9Nxdlֹ˃$v8U+(8;Hrڵ;E,ld^H !0ٸhh)t7[LH?L}'`dAԘbZ}֒UZ ؿ'o/ yc{^{sNϏ$e99>XzLK}4Ljo>}~ʛ[rZ\te)8>LӁt`:0@hb=wcP\k=ln`xe:R}WJ5oH8KxJrm.h@9""V~8r^[bFإ%KuU`F4!>X v#ksuZzwg_r6qE0t^5X7wR, _Uyq;o-x_ nn'j֐sZw{e߽T;6^>;.^ss|}Λ Xbtőz?NnĄR_ʣ_9_GM'6t`:0LӁ@h;K=0g01֮28{$W~{V&A؍_H ]ˍ~Ǩs"S;k+v*/BrW™A]B R\ks?SK!b_ %ŔΥgN9,;}x:Dr!p#F (WHMV)Sօ|MFj ˋKzfuq$Uw w篈癟(f7P|$~FڳӁt`:0Lw g]L11oMµ3vȶW ê5hMR~ bseU1][[UŽ8fW>kIvuO<#eT%ʭcE(`)R8nq>g!TKd4;qy9~KY[gBs"gT?v|)SKzo-/rH+_BPުcޔ3+YFT+CT Kk]y+&}~'7tЏ{~bVtNݹe\wUY5jx&2B! 3qR|28~<.a|HhacqrO_u:0LӁt`:pt);}g/bkw5Dh%р16\16[z-1yA;_,u졼#/×R3pKT/~_{RWg꧂Vl|Lrl$4_d+.Ͱ|] $IAKDiIk ]|~ԵNihcM33z/X]zCq/ |w|lZ{i}۾ to @1zYUֹ"qjs~eAs2{r|R=$c !.qn~8erm%6? ~u֎Q'0;h}:0LӁt`:<呔+O{(>ڴczpjS zL׹9ˇ?Ҏ D U:1p>l3b?\׏`g}{bw` tp׉U(BoB5Zwrũ$:%zP+)9@8a-ХAh"'ur'ڶK1(!;Q4b-܍X.C?~{χ>Vr%QR@ Sٹ/v@y'4O>6`5.>Aa2&IN}N Ro6؊Y^eKn!W8 6>Çc>>LӁt`:0H;@wBY;jdaɍ>+ӻWx]Zf"k$vMe!r(?a-o%|݅ԉKb)f]r ;~SvI+XgFaߊ\_C7!>/~yW] ζl^7.vxۛ88k#>n_7{֏~V &t;v-`D+}ҿhiByM痐i sygjgk_.}[g7LӁt`:0phdɽ&c,9z g ǀ^N`+Rm<ʇVcV~~^MzQNVC#^WBqAmT v\wYr|zڶX\stUh׹ٿXXRb]j*`.@~k Vk`}8*b ^%:bUTץN_ohw.;[դŮS \=-Tn5|^lB‚L7H ȅ_X-E.bs[ IAʎ玝/Ή~j9<%56wgMӁt`:0Lv@6f=xbwrۓ\ht|,^l8LƭX -VIh9u/xE puߙw+[MF-[tA|4HbRm6 SJ#B}Ԕ3ͨ?:%Tq8;ݡr6_)/-IuўkO|]F}B?_wDdVUY.]gݬcr/Fܽstlv`xWKv`Oi >w%2t`:0LӁG5^fv엋~(Z j=@jp߆{6xY/6l/*T̞-v`WDž/QO,paI$>ZQO{gzMrE+|Q)FcjPZRKX|Gm'_@pe]-'JB _ǐ,Z`؇ umѷ?6yt13tq߶WڙWOzUb=[ T>y-#wNPgi:Fo#|\ x ߤ͝j|a}:0LӁt`:pv):H2֊ӧeb>b0x~S<䩧n~?G2];t#. vKs5ć^u||ab9Ds帽 1}foYp4 Sv^LӁt`:0ḩ{{I@}he΋ xѓ`4fMR [.y;`Ĭ(<+sj8f-fI>ȥ!:V3$ IVQs%Ț52mjҲ`6´Qw] ҒlS ֜ |O#sJC=r{/vؐ8u@fc[K%ukŨ4 I 9{ƷD)}#&u懲ץ2%3VJY;=Ên8uq. Z_J uҗZǔ9=$kѥ ņ SgD<|~tPEo?vOee0j bMX=ڝ{8M?8ZXΊ| L!}:0LӁt`:_,B &taTC]0X=8C,1~.)ʏ3ّwh=Rv;vl . J/|}pWyt%@sˎ>kR? D~s/,?M6n9ͣk%ɷr\Z /ϗ>UP~o;nvwW5. Ҁ~(ٿ(~P&dz[nW ?l7K|]S+k>GPyӁt`:0Lu)K5j̈́A=͊c -i fd뢡\@+/l>CPNZ&j6[ʒ n(/5j/%9Qgչh{$&4'+9tnY` ;'a x+OȎӢ:;ix;8 @MdW,>Z?Xw ' e/|+?M_0=) u7dO݋n=*7!X1/L|SyOζ':e3|Jk9?]HQf/mt`:0LӁt;P? 1cpK{ .C-l2^Sr93c9`0pp;F׵b)Du Liw %?|_z_ʆWmYrΟ<+=?0[k} XsIeԛl>k%'699OvYEV|F`g}IotQ0'Zl$v&PlsLӁt`:0;=.Wϧ9gvR4sfP5@0y^keq /A-ژ"gf=wʿk;\霸ZuOf_wRo{ 6|i.|.Œ9ev6㚘+'v9 VP;vRCD0g:qr+bEfW2xPe/ >n^P]/TFAu&ڮg-w'i~YEʹ@+^}9zn+uaϟ󝯟҇nA/!@u7z9,O3OӁt`:0Lȋ*eTC}ySm[GYFͯ]aQ쎏O/=7ҷEYSs/J]uб;Ԩ#5Z ]2=~qޑ]E #qVڣ+~:8~">jG\R;P/IioȾgko s(tDrVOֿ۟pv;*o}Ps׹3wvu\Qwk"t|WZr8k Rg+~t.aRuj &U.6O+;HYӁt`:0LӁ: < {P!Ӵ5.kW.3/x`ނeb?rO3/+ȲA/]gXxp _OBm}zF7m uNM5}:݅6I$3ڃa9bK(N y2m.k/,3#H>~ޢϯd&R|WWf ~sn/-O'~#έS\'~+XL|ܧ!VӃT{mN.R=+2g N΍8ʟO`򷺑ju.Ӂt`:0LӁ;ɐѕ6Mp+̚fkي69iq6I/\☛;ٝi!=kǓ)l׹Ֆm_3'6,r//g3㓛W3wržZ9%輸ɴXVwZNqMK5/c-Y_ QJ^pW*-8ZjqK?^B|g٥q@VGdE:ۧ M̅kȵth|]VyGĬ\-NaY@5 Q{z_t`:0LӁt@h3`1D2|2x[5  -|;$q˖7Ԓn &8dSجՓ_|V+_s;37}+^b/إWӝ{dyP?zEծAɃSYqh՟OHtPbc\T[$9lK/s3[lw}nӁt`:0LӁ0 ]5\ؖgޯ)`Ym)p~v[][^Qe;o3 ̑(0ߖŇ=&n%/ ] 3 !`$Vu#σ'2,bOÂzfHx;|L#VfQ]PU./'^vzН9TX|^ۿR 8*G׍/ K϶RM?\Gђ%>뙔E8>0YH;M-,K-ٰboka F |W|EНV .vJDSr5u:0LӁt`:pO5ajl@5}졵I@12ɊxXH XJKV0NlUq6$4p#%Х &Lbq;z^1d{bvӁt`:0LӁ݁'=̨P7wL52fġ}l=.ek b1{ =՘g#,z8ꎺ^:vGﺏNENډ^`r|Ctt:7S<|J Fp"wն|JXya?~sSW' ;~**τ vהn95`ywQV'@|$ë>ǷSΟ:'kƎⷶ##0xY>}-˷\Ӂt`:0L=ڜq>{z_̟= |WkMAB2kzq_q<}䭘KqYh[鷲xG3p>(ZC;rOteZu~|y)(տE>r=V{+{ fH1PMQ5IuuLyRenݶg7;Kw]-ύn>tˊ t9U}31_)Ka?us?*{m~=_ђKV>wb}.G~=3:;k:0LӁt`: Rse[k*%S%f k({}Ym[o ,$^2ʗTtB60hvw/0\ m9*:PSܲgygX]+W'Z>šP^݂XyF2nVSq-<[cU*_H.9K7KEnyϿ|TTIϽ w2~}}V ˤzC]ڥ~FYpQk%^ao?\GbLt˗Kr#5š jC;čǷݣJOgo]2LӁt`:0t|Ekhe@!{"h5,bQOc|utkoqV:Hg^9v@U )@Ez´/gc וS%/)(hC)?v}7sgR]b.p%vBR.R[k@Y 9[zRKu{sP.Ĕ|he[Wŷ?ϸ7?񵯆[f~{vyt/.g Yls_bK|s] R,K?rjNpW||j L0s4/&? <ƺEb9e:0LӁt`:pׁ klgc-_@uw"\n&5=j$;Ek rQ~K- Z2`C㡚]we\_ciSI*/;r^w:f-Z:h'?h-tO0c/FC'V0}3(〔kgΊ9/ss>cno}>z&/ڽԳ~u>?vzgޟrs%_H ru3s^yVbs Xݳωņw6{Ot`:0LӁt:P8C1 >S ׳l`ˋ&fb aU Yр] 債^P !|YMRv^ܔoN4 *\U7BȿpcyJc.Lu?@^|j\W`$mՏcX9:G5+{9)[Y_unbb8ͱ~f}&^?[?/3y,߲gnV}zzP_zR~|$}6\t}?hE@ZaJ_XZ֯V_s"V_|NϬ@NiugoNӁt`:0Lhg p͢ c~se5g@IDAT `mZm,ukEn (fò=4=Eŕ/q4[L+lfnx+Y,QZS{/KG@LFSsdxu!A @W8%M(;Ju?Mw]?ZIݏoXgO'Wt0F;u/DάOn_Oۗ~'^S˯ԿMYѹ{rmDpח0e:m<#?Hiďz^\>[nHKlѿ6"燜VR8RG{(2'9t`:0LӁtzz4業A3ai&Y*EȰu rN*CqFkCD 9>ýb&>.-fPEOYYl]/}7ǧ_Z[9[ZkO=`E\މ:M\4#]F ~B[~jՏ]4/ռ\[?쏆~yxYq[dY߶g_ϰ @86J9;mV>n/jO Wduez.~sLӁt`:0vY<˙9S&9 vY0^b!U:*Zw'lĜ5LӁt`:0@^5@ U3jQaC{/K𗾠qHV XB1:gNYK. epZoRQv+GAاg|)X/p7e\BK$:$Ҙ~c1Ѽ_y(Iױ~_5'#2%O?}O^|~N~~w'ssϘy)dZ=rcCG-}m,Y>pRm4\!dwG%["A?(/aNm耣>ֽmŏ Ӂt`:0L:E@M w{i>aRkDenhfmu Vʪ j-r͋E2`'kqv[=:+%eRqQ] (K2u\q,+qDzhj~8e1pArzs]haOJrkr2+%mvʅpwPԇB2?U|+%EpOۏ%]òGφ֪G@w3<^ML볎T>4ZWIZW3>jZy)˽u./{)k9NsLӁt`:0\;GSq2f?3{ =<1 WU?@HX}G6) Pq8ummN;رziqlRE[ŭTe"Bİ [Yɞ9{j#Q $ļ,hhe X-e-fbJr[ڤό|듶W#_-|H9.w|4yIsX4X%ٽg@ ;Gzxm3 ,GgWl_ ^y'Ui`@T~pVXթݷ@+ @+ SԐjQpRZRWU>`we˂u8pTa]k 90@qP`+v%Pf+Vz|nwToBY5؏%R35=xwφEؗ8YL*lހֽ OtX밅]NMŸ{ov4a_?zk{E?xb̂uu|[|˙΄>O |޶&) Ȗk?~Zpv>ZN|-e%(VhZVhZV`U`{EEPTFUל“6h' e*_/,9x5 :1Y'E7$T>tg{MIKk5~{aF$ͲȣܖaqIk}#z) c.xv=>3r푧)gy%oݸSr@@s FxnZ+|E~|DeǸ<-^U)z?GW}Y&x/@+ @+ ! AҥvTTr]fYE+>eQrY\1bQhF jd%~%Z35tJi?S"U1L|"E#xʩb90{[629f;o|gĻ׏]unk^͍gdPc!Vϟ_[iI<_?_^hRɞ9s}Ȑk儭r3g9mh;XaYHι[j[{TҙApwkZVhZVhS ~8.k]R S Y7fڌ\ro^ImG+(J\]{9F<ؘWa|k\k4}1f3[p8>Op=/6;샱7M?:}\$Ak"n徶@+ @+ C*YS!T*0URbbc) %(L,c`*IxmiMxLJ<)淹zε_=>4FǨOVGR&䥙wcbOjQ~E\Xxa莙+}쯒'o=9f{OߞnOy=O8>}SR>?q&3<>/gǜ:6&CqHkIJV~dTgt#Lis&cn[zUZӋֈ9 ~Ţ 5/5c\EZ7msn\ @+ @+ (:YeU [U0glKb-:zY @+ @+ )EdY†d1UI?t- !Yɏ}[Ť5 ciq'"69S1Vz=e|%ab?bB)& n%V}9y\=B% }#lG=xOe _]youBqk[X5KzY.?QYvX#l35eIKFEGqzpCؾؘT1.Կ_$Sh‹zsn@+ @+ eh*`T_EUEg׷aBUk)7!)P[@lvjT yu 'ёu&0ƍkڧ"~o/c#&3y914PnrߒzLi_l\ TwW#۳|#_/xmqB~IN"Ve/`TӴ<$N a "`1k``s9[y[8*.>~J\uN_XݑdqDu9W @+ @+ , hW̃c|  LQzlأúj5>Y^|2.WB%^fb04\+^䀎k`_"X)@=i\^tOև~on#̺/ARxbVKxVr7_ԓeF׽{>~I=# SuŤ<5:?Y`=l"ã^k>@{p'_3:_;6Emcyc癧ί.s\RczIk1HϘGW dK+ @+ @+@hiWAf.]@LQa Ub*`+ȌZw\#V(r!@bv.b;zqdN@3M1P`Z@B_ x"K6jTZ.F1pp:$^{*,)sS=a_=zR^\4z]`=?:Mt-[uA?m7s$q+!eS+;}9o\^q:gu{F5/wZ(U'ׁOg-}57 N­ANQàǘ$`2UlXavsaeŮW#B*$`Xs!﬒J6袼CƸT*W:#> zG-=1ҒkK,zjkI;$)c=_9&#^w|Ȼ&S_8eWlݪ32z>󫃧3=y  E?NףM#gb9S@M{fDܸ})ءjVŽO~:{9||+zOEYvq^\)\u5è8k8060—ݯhZVhZVXЃ6.d=*yg+3kiʗLbQƂrB9Z>ל,02㯫ZaٿWb8-g+(|ೇoL|]+ @+ @+(:(#tũ&՚`ʷVaZ=s= W\=lc9 _y*c(fEJIh Ϧgx5GlZ>Ǿ,"sK2 )g4g:_oYbX10#,~*Q_ o/{{w/g1>|b1zAXwE-- \ _B.lt3bV>BaVxwUziê X/cZA2m_ZVhZVhZQRj“`-8.().h ·4yOcYHp&P4a0ܾݷ}Rro??ս;:ن©8^ꪳkx PlšrG±>l"rK_Zǿ6kV,:}ɿc0%sw@+ @+ ToqŒRrqfYp@]8sX*"?L/?N̯\7ooࣾGuzk[7cGc-<}u:×/8`gxX&E;q7A" 8&a4GR]٘ځk3\6͔ FLg?@=hZVhZVhR#V r!RUwU;hV y2v-]g]B:}j֩!!MࠤNtDBCX ^&1^?ً>gqGtz!Xl`YO~vcx/ޮֿ 9Hc4?Vnnzy|~Xdi],24ף[ ]RWExV,*o'ۙןFq~+w_TUă_\#%C?= >rMKřK=jZVhZVh LjQpjD!i:+ǀ5=˪E1C#N0n˗xA:h,pnBA5 ҅. waޗ^P È/0(?g;W.b8!VTX+v[݇/&Ʒ>'QNak?]C`p4Ҏ/L Sg..N9b uLIuXePk>Ҧb;eg5N12b `+Kn?6RkZVhZVhZ ZrӪ܍UA;*EQ"ŭ p9X d34?X5mi`bL3IaS=*}'+eoQźsji>#11,cAkOd?.w^=}_n3f?|,n܄_#@9~ aC^glsxZ~MKvr%bb, ̕²Ve׏HV_{/OE;u @+ @+%ίTIQJ] OT-!aV*l'=;-L/W|oPà_7=qh'xqθůE !n$AS.gt1'ac-W^Rw'CgbLaY#{/_ n@+ @+ )UkJ~r3O]ꪚS@UVJl28GuEU_3rŃ1TH@}P!5aDqMæAj1&Ⴝlؗk1WK1GeFa-uӶu۞<˷7:{ű_  /7}UxcGgxʯbE_O%~uz5%4]XVW\Ws9 _Z%S[")~za r*H @+ @+ , g.K\pUh*Fת3` V'>xvGQ\ڷYq.c`2oyA{Zii=v%N V=%rlM1ẕď_V9:M=4q"׳TbaUx IoK"o+&X>hsF|oH}L;S(Q,ݦÄKdi.&b?{@ȴ[+ @+ @+p2GIgT.::,+*,Hh[o.\1jͰ³kL HLe n? LC^3m zdC~xIa원Qx¿x773ssD=x%~۟%~x \rU+^-tu7\?}ϯh8rq{{:eG*uj|rvj<|&,U z-jj--{+)< ~_%a :l&klNr 5 SzW,TN7Qݷ@+ @+ @TA3o~* @j1yb'|4 G}k?C|}<AT\Pt GlOyн ^r{_+O݁bg/ަsc~xv׆o*gS7ZX=o}0 >gO&bq=X|rx_p6b/&c63ճ|&jś4_~L@+ @+ SYfOѩB2. զKlq/.E 5[,o6(*>%"~\'%]G^H935ACҒf9)\uQAr_>xKNyK킋/=ֻhN|yNv7\:Fs#8A?>XaHi]Д^ɹ^17D^ e+Ik-"XI@XXSh6ְ\v-\[(kVhZVhZV 8:M4 ި=54R=*05p*[K ”u`IHA)>&5C5 R!(*~x$ V9*>iBgk|Obx^Z(5#G2*pG?ISN8~mn.Ͻ欼57l.|ǿ۟w8\+k uY;=8?_C.p+#)WO >^#̉ NkN|}:5a&^ZI`-|F^u'{ie#㝨ƥjcGZhZVhZVSZKRV}UfV*BVԦQ4 i68*錽_xs4 9TL9 "O3mp8~Ewʉo ە\]&|Y< UkthA?۶G~헾i-~t$(vjwo{ۇ/{|vo]q5/s^sЬNtƂ뤬0> 9,Z_][Pj33^G|f1$z-Jn̶I+ֿ O__P. Doetol_[VhZVhZ][gA= (wy;5QjLAQƠY0T? =&Fq+^&a.hc2D.. zv·?oEx! EJȏum>I\KK.~-LYپwYqo!^Mg9nlS},9A|Vchc!Ûc?3zíoi\O8v۟'=]v=|;KOJ*TZC3ٷF3/~uƾ:ܯ׷'Sܧ_x,Kuqʸֲ)]j.ɠ8IPVB{HHL7c]6sNʉuqpo̍~q3VhZVhZV`Qh,֫4Jo :bх, 2;H vWnŷ>{SEW|r{ۇ.×^wj ׅ4{dE|'8}xy[gv_;MO駜/47_g'Da+LVsDNa i30vfiGsk@$/ʴx!SXb+1^M5 zׂm.Gb3M^KU|"HC/ͦ=$Ut @+ @+ l e*zU5jղ]e8RJRb*p)b+VEzM [Nc-,42_r|c!{ {萇pq~sW:{M)*ew[^7/?}vǍ Mz|Tn~s?+Go#t#6t@W^Im|'L-w152SⓍ?,.2JF63 aqbn⎼&d\P=>.1{$e\k/s֤8oZVhZVhR=hSH%xG G2aE+EwV+ViPOŘa[GXn>aE^~wlǻ(iK5taF!mR,q,Ẉ|ypOoP?t[V-ѻ RC5)FDlOnMЭ9(gzz99c8ȯ#ckpk%K,婢ݹل_w{ @+ @+ Էgu)nǻڼEE&c *mQiͰ~XlYG:Ճ*1cA/PYWK3sx3b|{a~SG΂r?n}2Kڇc!5ҽƵFVe/sPmW|2+%xCcV|NsׯVsR3:kuME^Ok$~CwébXߑAf_s 7b,סj[qQ11xuXsdaxNG~◾^c/j_<ƣֱmOYx黤^[VhZVhZ)pąt-JXL5J ]tj\c _sgXGo*D뱠X_9.Ӂn!C;*+>~?yg:9?x{?S=d;?:k#y42l.J_餿XC9-|F2cI ;6Rq|ݳ$П{Prg/]x207U Y*X⢹pN犌c}/ָ)$~ALLaw띛9Opz~3#ƾ?|3gaRbiދb;vt F#2MYV t+9뚗%f?؇gWeC/z~z=x)ils-C ؋NRl8)U,"#7iB_}&n.kPzF֋8~}&j,j[ C5M ( ^{9s]sObs]@+ @+ (_F1//SJI9ťdP.VՃX U> 2-a⺘"Vp1d_?f0UBve2F3%sKN1sq3b{n{zp;ukAE&4!>|~-᝶$dg. |kGrŕpJ}CV4O{m,9~szk#\*x}v^ӷ_g&jle4='A59Gh~;M?;٪3W!K7l-+<(Ĭ>M(@+ @+ QK3*T*Y {_|bd{EƐeWOÉq* ) 䌡CI#E4g׶z xOn7_}/p;‹G&jl~c/ֿ|:R/2EI d IK9rK+_\/f2Mi|yۏOq$sZ)s/W+ZW@b\p%Fm.p %y'܌׈!k2eA:ŗcz-V|O qAg^x_LWp@+ @+ oxX"ҵaZY2Z7BDj@ E`t c쉵Qб@i1GvlD-?&sŗm߾7p?K+_i?o|&mRifCŶlX;yj˂㇠r, O%rJ# wX+F((19䓶ð~_]袼J7r׏M'AhnKqk-?@ @C%0W\ ӷ/$~$xdX`vxR+uc¡ZŸ3$Na Kvk+ @+ @+0/Cc-Ӫ7ݯaZ]"xUbZZc/YU+\a,{JeYX~']?<~1xɁI0v[n+~b~+24_j#Y 2f1~ K=7Ip7Rs(un#GSs7%/} I2 7\KcM9ZOYo*q^CW5;)΀5tTl;ql@IDAT#6jgpyc8s43u֮S 'k 4A zVhZVhZVFh[KEf=1?5 T+(|a)L`~2|®JwYɐf=TȂ nxzTޏK$+6 IBl۳ЍvnW/8Egܮ◝)@[ȽVfvdHUL#\6ЃMf#mH{|gpؿ_Z'|)Vc\X.# dPmޤqӿJ^}篾p%ϳ@Dw~Y᧜уJ0JevzZ !pp6If2'$^11N+vǀ--prBšaPOcy61~TL}r?رW^sFD?9|jbm_^ݷ@+ @+ V`)eTC+FY^@I'j_z u쉸B|Ɗ_cVHƜR ;esCՁ{I۝t۱G,֛w/waWc%4iHq跗_ж=E_U/5"ljOTQ8ï~nn?//.8/J(8I," 0T^'k[$EU>g]DPjLWI Z6rð84I8:DVHY]O*ص|c̶8z5zGk| \e 0ukZVhZVhV;*2,@P+ȬyTVF%VUz83͐ƨ%,|:67sLU2#nG4aX1qłmLns+o'gbWb[λqsҪXV5fhr6_xWqhg0 x+PObXz;~'>2]K}㷋_y^a8yzr h -{io#Ø\`quXėD3,ZD?Ώ}JSh3.*x^̊)#L&O qSy6~˞9dֺ@+ @+ (mGeׇR\RWQw.%i󴎿?_Qx^XEu.~Nl~xp"GnO~=59/^?Ń24h~XXDE3ϡ/%_xAye|Uy=wPHlqyn붣6;c{[ߕQ#X:5^ތ&;IS؋>]ćL2SVʚ3׺۽OT\{ugi>?EWoaGɿ2^HvX @+ @+ QdTkY ".S7P`v+pk \-<ˬSWY!;oab>Ms-v3]v 5/:SQ2MiXx<-GWy4^ADŮ+_׶}^y7F+Z>Ȕ\ K1; Vr=86k (3W|Fo HֈY/tg9{kJ(2 u X-+ RDX'q3|]Tu j^P6GXa q4W@+ @+ @*ptԨ\U.ZBs~>ӌ. sq𮾁ҧ1CS#ǚɖK:V=H\Q9?;F ^~{J.~{U_}Ǧ^:#χL&1~KO:3R}-G~&tU<1rW8E9+M|Ų7xb r\ZjL7[V7a'ύ`}_c']uvL&]U#<+\rHzǼ%2~7"\,ś@+ @+ 𷎫Xsmbr V|!K<Ø4賸McuLUM[qȥ>9?98".ˆT G/޹vp-obraOwS+qљk@"=-{#j|+NxGKY9K>Zf.Wc]{_j<ﮏ+6OW2O o頵܏\IǗ쵷x|2>&aki=;UKUqN=7owɊqc}( O޸[޹]w=^yZL~׹H“9n퇽6$&峳V:bNxCn{KW%q"X'\x>rwbq8(}c^A#aS||k5.9|Ҧ.1#b/ô75=$rhZVhZVGu[KR `F,X 7NRdA,?`1O83Ѱ< b!=m,E=lZc!@ÌrSX͹!CsI'gܿ磟~̳?q‡uj3|~&7Ƃ>Y'z@gbGoZ1|- } >Ϗ@9[g=c{nR1|7s{LJ/$hU>ܿgZ5t뜥C 4\e;Dz؉ʁC[kBOfY4GX){\ί}J|ӒTe@\}V;mش7z_ ;7f˨}}Z{q hZVhZV8Q.<) g!>U G`0Ϥ2 T u".{\ #f<. .hq:抟k r>(?em|p7.z73=\Gvcurf|cٺ,p5^KFlQT^] X%yN8*/k, f;>c{wşjל瞿;+B*(9\kAYlg1.J?zÖaT\ɓp4"B5f]6wƽ÷U ?gjn7%܀W=ߧT@X.#b"L]Mk٣͗^5vpe<?qXZ,5[k}+ @+ @+ _՗U@Vk*Τuni¯ ߵ8:m)_UOcqr'dݽP46r!>XŇmsr'k{}]KѮW_}On;x~S<]ZX +}tt$\v,$^1`YJi77!CZV (\n.ܲ{1G;|yS|veWl+ @YcrÕK&435cj3a\F6I:kWM{Ycz5{ t+u9ZZ{'EC[j׆6RfխhZVhZV8D2*|rTbUEJˀ͂5^Ԛ:j9&vyVp`:C=T|cAyD{䂻]hL0ķ}/C:Xb>58L,&@7à6䫘Q& g74U3q"Ȉ}ZYŇoYVg<[s;),sꭙ20ӯiLJ-V&ǎA4Ø9wIhZ OX&|CLiq%uX @+ @+ )p3WfN%MFi_<_ R򉆇8á& v˟XVGMbFBP9#\hƼDqIrŗ-1⋴;W֬9n9I`"AGW6Gx:k.x 4+1?[\ǯb 1Yq x{sL# )Yccsٲ敫\̃OS$.'ㇵ+rhd{ 4TfJDKԜS)9_͇ƍqAwkpCVU||8+m'O8e[`*>=Gxv Ukܶ_y0O.Ubg N"W/aD\ 6 Ȥgtw<ɊuO].yҐTvæK쥚1wڧ$#mb׆KLk~d?ԞsBIG=GR%S{ׯA~>W?'~dBapT^Ypxo2X0=lZVhZVhR#sLMEBAu=,xgCa3 $0*m&Kc.NKG Xf?pzrŌ(NPө/{/p=.;o5 O8[x 8GVGIKG_qqyZZ+@aӽ1k¡^s[Ρے`&u~\ ⥿%j/u _a і51ײ.؆h=L~tE"K+ @+ @+ AB/ NYmTK:*rZvQŠ 6kٴa(quQXpYҚ*w-LX pU&DOŭ^YQYXu ?į\^qqh_RU>JFӞXKcݿC;? ̗_Z'ϋ[ǹrftOgM}kS3=G?7N'Z'd|"[OSUqZ^ִ&&s.^g8Scگx^D#2!\zo#c^NNt2a͘$dmQiQa^M^gLk/dHT$sj6cZ%<֥bzo,7aȼJ##y؉SۡG0.;[bj<0s.RSf3xR @+ @+ Q9V}PLףZ]+Oդ*YrTk QǪv-<]a(rL:ð$bs,i8a:dǫY/v*Υ+0fpǗCx74fpqk"?-1zl ~g'-aX{]Y~q_LB\c\C(ڝ͌_Hp*̱_FJɪ%_6.9V\8w܈̉,@zBkDE? F"d!G@+ @+ 85 |U9g!:()×"[lp;PmԬYº Cܬan"$ZHdpp`=*,½̣j33=iP?Z匯nʰgR9F+E xx'RhqK'PKXg{C XxssiQ4X^/-ˁ/#'ƙ,9x_?1ga qk|սfeq!0uFJD%eK&}ϧ܎Y߲v"m;sɞLJ}"Nb"|ʻ5Wz @+ @+ @v(㋢F"]fSiL(ҋ@Z죫r nD"nY|s>!8LLv8id*U;Md~xґc\\j\xհAOm/dyS^{Nӷ{d`t *p|ϊ_ 19{8Ői,Ûy\*"?H Uk0aK:T|c}~V*7PA֜X^ m3Z)5N2Oa3L~FH@V?H @+ @+ GURԫRT9VJb=XJUp%ŬE5eY$1%gY'^{]bLN>־?ܗnI$J0aOpw'_z:WFj(ל̚%eykR`D;?cG7uۉno~^|eueBf$xx]8MNU(N>|{ϼ]uu'>ye|]]tŕ>.мҁqf`C~n#ԦFJ}WÕ%W^]\r]uw]xQ Ntz~wlcnHz,3-mZ¡\M]վţ>C& <^D4ݵ@+ @+ GJV(k%աP_ۏO;AV N:w|xl#I#l;?DӚ'AD|xm"~vMa2į(<?mO?^~{W> %#h3j |BNp2/>{S'naiǷۯ[ǃxlZvs(vw}?-ۯVJ=2+m7G']= )< e`jKNau+U4C!z.ZFj= @+ @+ *pTEJMJר, ?7uh]ΥJU49hq1Ȭ3Yyd1bx(wylkB'NqnD6p{v!'܏wAMgu?03f}6w M ~^x0x՜B9{g,|1)nydwv; ܺ_)*19?<ɑN(YLj%;&c۶kn{?ɧ?SSN+>?r7bk^ziJ[os)ocVG_mj0c~ͭ veFhZVhZVX_U(,UςUXTx\кHoje*E, 梘W,cw|!RN !@C'`V{XTy?-yWlMmboW8m#GxҗN;.n]xm#JUf%_Px\ˬWu_;,44K\YU;_tvI|06џ9efO>scs9;fU\ɫoM*Hݶ[{}u'ʷ ';l_{{ll{C|[QL-iYY53>Ç,q{ Y2ɰauľxt."Gۺ~G^_$u?{ٓk&UGR#kǟop_[VhZVhZ~**6 Qi8=oUT|ѪXה(k!LhB (Wp#}'3@'i`öq: Ow;_,VJ<߶onVӳt+%mȪd Ռo"\rNsx`QydbœW ~vYp:o{OM'&4QJq).Db8Yץ8bGKLvu _cͯn7ىnO}E9Ґ[QY;p FOL]s߿x7PCW3gT3s<9 3jY)<^bgߏ̸߈}#37@+ @+ S=h:b"s U7eu.Ӵ˚Ž={|ʶ"XYjSn WcTF v1 Ͻgrzk ;{vߒb\W{I⵨AS5K$: xSK͘1޻?>FJ 2{Aj?;n_ޞ3㿲cޡMs4|pR{pwWQ*=XP4E'(D`&Q4xM͓{}LrC򤷉Q&A **N)tEO՝1X{HZwZk1Ɯk;}%W[t0t^FO`@1+ʥJFk `~ʳlc5.?/gRK'E{:'~DOzFL_~{V{^դU$fDhu@D :u?Ђ KOW;Xm^K9oM3S*********0WU[5nzVSL&,A2(d b2mseD9-;c,[;咘9I x鉏yypČϾ]ByOW|e&)e!6̸mOxnYoNvƒQaRy_}^;;Ιdu"[`8.JW3y|9j9>i>_XYO}K]I_K6~|q_recky5mE5v{ݻ=[M/30Sg!TVEB)9:E h>ŗ8_v#UenVvohڞ́_ukUz XXXXXXXXX8@utegUc[56қ2pzǕZ]] n롧lf5 XZIXq|9gd rI9L[p 7[cr˷roxs&e2B`$r~/xp$.8߭>}4iW,ww|7.@h6So|g^1K,zgFQE+~_t|_r#<"*>DKo(9utヸm[+xf0k?A_l)ci:RO?k|o7nqOb}x{5>-uXԯk_a4s\(,ҹ,K]W:ߩ}{W*yXzO<~į?ï lzܖ|õLyqu~+ Ъΐ{S \ɩ(GNZRUF6D8S; Cu7k9G8{UUUUUUUUUVR޵jiXkF;d7s% M+ߍkiK͟?H7t ㉀P\D@B{ٛ>^}OM5Q4-Us;\}}:Kl׽y__6uSZ7~ףXBO~vT?-_=.(~Gj_=gn?{P~ö/!Ͽ>r^?|M)pYǽz_s?\?2<'{{y<%1A^}D Byfٯ FPdj8)2>?ήtx>>1F^*߿se|0R@ku]@AV6#<0P?A^hUUUUUUUUU3x4Jսe;N7c8Ӑ5C;8 }gI.(iq 4[d{$){0pvy瞢_4ۿzު䱟;)w?}ѧic,\=;l[߶__p}TgZ}W^=agg֪ͧW?ۯ> >d#Ac$m t.mٞ߼}C=&)}~qK8SޅWMkg튃Ϣu/fk9g]G {3>7 @(buj!#=y04wϝA _j, Tkg??׵3ڪB9/=?:8o mp'?˽~7=*|BM}?Gn>Oy&Eߨbdk#=o荧GާPTeafaT=l;6=={K_SVᭋfYH n+_sΥ3+֨ʧw2cqHǞ60Zu.’p >F״*********p|j}E*¨f347ݥcĵYXѲAN|T+YrXGì`k_=xkA ?nO?~ۓs}ʼ8zj;=}#t??ͧOg\Vn郪t"hmnó[⫷n6˶L|a\;3{{޸Ս?czy%w;WTwͭg 6Bun߬I?o̟o\EhRm/{5}Ƨ yU=b8 R7FN ;F߶^WHmwAb{A}0d+^WNb^jMJxv籟uH-Fെ/55׊qr[\KpVƓW)g7m7XU5gt:tiuKۃ.8sDf/UL| h8w^?}:]E>m>Pb@o{;pC^Qڧǟ"7kz0>΃'•}o>Hm?L3]@cx`9$׮YouHsV^kX[i㓾 Ec>SV?5wrHGl]9xTrl##ݟ:$8jBԹgy>7?ܫ~-n&NIa#q;3_ zpۏӞ\967ut&etSd3Y Z`H]̞{פ Qia/N`j>1XXXXXXXXX+p ##$k\X6Q֋ _]6iBeIVx g+d.o4Zni>$UI!rn]zi羮{ @>Gz75qmO>Y}zpx˹/ Կ;9~?m{[ʒsŇvx{߲=vsbcCߨ쟫RRlڬ;kkURk} _OQrGŇ{`ʷk{_q_'w?b܌{3`~3z?pW~cN<3r:oLVUG \׳ $$<ܮC(Jp#sCyU`U`U`U`U`U`U`U`U \nL190&|nb fq}jaF .ŗCcuCm;-B֜rﬨW %$Lקof{WWo⏶WCRqt$W~3OPN”qϋ'sw}C2шQ?[|1:;.jn&+ͯJ_M춏|Ɨԯ[ C G5:?ޚۯ|Wn_tɥ8C\WVgK9g2wp{m3nKd#!\;05'[6NUvbC~ck********* =Ss8Zδ 5i^VWڍ,i5Ք 6v߭[k]'XE znH9lyٍٯ}sA[/7~>x| ~Q:lO2k]DU;3X ~ѯx; ?8OzʳߏQv%a o3n>9Ub^}_yTv)~P+s<#0s(>v37_x^F}ן=_sF+ɨxuԽ~g_,DsB+|_dV7,}*y^/U(!w(̯mirm{s~/~d=~U'}#_EpD'"|uPKyZTsFpgs4ζ9c3V)32Xky{?Nm@IƔSݤϴfOK pUVDTj5;8?@IDAT^ |XKY|@֊ .cgM{Z[_S }a1P=fzo6: Ο|#ogz!ww6w28ZZ3?.||j;L^mKZvB%:ʣsf8u.SOPR[Cɽ1 %ofd" <𺦦ɤW:f븘kLel]`yt.׶ ޽?{̿Ԝ/e`Ω w2ȃ2o_aXFƗ5cpl{? #W{9mtPk*********uՔw*UY;vӻ7gT1yl- }E{H=Tx 6pf Ϻ]x"ppϭ٪EX/>Z8SˇG늃]KwbX9|)] C,>Al:1v!. 0 7w`ڶp߾!4"pr P]:Nyʉ7?ҤIl;{g~lP^Ɵc]^G[g򱗭z4igS )đVy|[l]wAr.#*-eeN#^v%g> +wgڏ_C81] ~c<|YgO=~j1 w;)9מ=2zge&>Al [dC0T@ ژ 0 C#SA%s }:'XvW3R^~۱Uӑ=iuANN^iwSx&ή@z!up^-s gG˿^w9$XRI:66*;m,*S,k+! KI|;`<fr*[|GdΚWVVVVVVVV džM$M'MqZq**4 hx@,iRS\ ))|j,V[X>E;5]S *0'~t퉏JMҾ{Ŭ_ȬB~m?η鸒W>OyGFbcǕ+zeJS7;_ ?e$78ŏQ3YyVw$>7vyZHE (_jI<։+5eAz[{{ !p ڔxC|hp0'5ug;&| R=uܹ~n/ʿ`O.hk{{]8VkLvkɳjU`U`U`U`U`U`U`U`U+hW=4S+fu|e`,.Xq[=x;\K  :?| 8W.Q_r]El9u7~6lБ8a"fYBIP^3~xBrNT1r6~{"r/KfcxMMfjs#KS_Y.QIyy-`ACO~aygz;~>$Y;xRO`:䰟|dzʟ†"8+iR,m֧|k LЃ6(IJҠ496f:`9XjjضW]_M|` \Y5|V AwC}Kv<>qO _'oOM?wG~N~?Qctڗ?>o0/Yp03~i/b~7,ԗ~{Y8K{Cvy=VI^'zcw(i>ɟo]?Hvg0eՙOgePqTahK>3˓k~y/s.'ؿЯ&3J1ˤW]pսOϫLˌ>W29LG/(=x߰l)8e[$>5=QѫJ^nq*4yd/.Q|RD@v *l83s[0܁g 0{pьך7I<>zk T@ѦtK˚ h2ƧͪShr`ٚ1ҘZe#iv~pk ; UCG\Ylgca s׽e{5 ?}J&sԏ;?G?GZ8eįھWRIy|Sm]};j]5«|U!Ӈxo1Yz|2SNny7kyھse~:E\?lkFQsҪ;=[4_;̏Ͽ-1eN0va6x[׿.ھ T~TȓR>lpI)~5m>N8!F"tLH0ƪ9CfQ= =) |k7 뱑 zCZn0>޼9"˓!_IXOgT+^2N~yQ?JOG7GL&_/?Sg۟](AW=w~+|v_zJ=})$=/\w(f} }іN~G1|#|og[{׿ wrGo#Oo_}㗜 czu$לf-o[!C(^8L-{7m_;}W|c++H}ėCE>>x=7_3lZ>/i Ɍ*|UGG7Ѿ|طeʎ ~lj?Tk^XXXXXXXXD}uj]4id?zX(e+E x}4؅[u\b9#OS Y7*92\sz~^Sb >DcrpVV-{7|WtAi?>>~׷'_< oǯ$~Z%: )mԂ`]wX;w< m0/zIs1;Cr}_74 _} xAn ê*[֏Y5%*?n&7n{nיYp>(opj񔗿nC](f>Y+A.fZno%Jϭn}}=_>zؒRp:EɚGS0)G-Re![+ g?)q!~* >̾`5{5 *PO4n(nMid74w뫆l7h]smtQ9Y>ZpZR~ĺƷ\Fi R3ż.{k'Oqu#G o_= ro~IrZ>+֏>hm7p4_~;l{ԕW~ Ezg䐳~)vsFñG~ʿlo_|?}fZm/mw|v\ޅϬ( ?hWJ#gm[6W_PaG~ۯNd]#%#7>}N6?7na#Q{HIɍ{E)A eϯl^?-*DF|7 -*v_t ŪTr!v[pqiFӻ5u7ꂇ (HeW[F{zv !â"yl~[>|"mC~w_} 7 T=Ϗe~zJǶ_پ{MO#Zs^_}gGKb,Es6P%'/xw#ߧ>wlo}sڋx|}U{?y;8§4r߼f}[zFkrf=E v%ׯ?ߟkj_׬LDb2E7vx{˳߱_l_-))u=ON?uZ|jwrն4jCj,ƶuWcwom&+~Gf׏Ԏn|}͛W]j:Deǻ*C=EWoފSvB+>Ju+ a/~۶W֧1v/fmnoߑ-udaM?>leof{ɛ&v'm8KEXss]QOF7o_^[?BO< ^0ZVw|{\#eF5io}߽&=f_NZV}F')ɟEW?-m?ɵ]_o|S-xsŖοmIտV=z?|S-_ݰ&|}lCA!UX;DG&jrP_Gg3ah9;ymo 1yH:XM)M fԖƞ>f=slVve{Sw!F$>f6;mǫCUdh—)֌G>s^B|m2kV!ԃE_"q̧R^:sN5,~3kٻޤ/0%~v;9kQ\~^rEfU9 gWk;85X z&/б;ݾgpn@FTٚ':ȋA"[C6Ud} ɘʀ _dzu+|ß*{kV3d h`GсewG|Njɞb)c ꜓Xkɿ)r5NP%4p秮$5-ۄ N e??:nU`U`U`U`U`U`U`U`Uw֨tK&MoaE7sM+"=-[~lSnnƝksR'ff`׌AN8{|y(~_3)2u-hN [YkZh,bjC9=_3J2`𲉇FZx!LǮ)>:UpIQ{܅J6}@_Ԡ.tIzv@i+,Pk [;>r6DZ 5x8Dks$|vx+/U KHdزs|r݇G-/܅O0r4IK>Q7eM \ jO~>s8lҮ֟%> l`̧cEnZExF)ʷ #74irD|gh6Xy ìːk>d߱) #ʧ4h$*H·Lޯ~HѾZsW^7U1Y9@ߚiyq9~SPu]W;U ީN 8 Xsl+_M(Vm'USD;OSNh\wI8ėx)N㞓* Qm(Ulg:wRb9I寳3SRB5 t.LWVW}ylie`jޣwNk" |t|+V'# 禸Z! bYw5֝*ཝO7_'P2_HI&[`$|`s1jEZY@p_^_mTHy~\%XVE2GQ8U>stMIekKwt( q@J`9'Q3QV=Cb9~L6It7bo_񻆧yI|{Y:?120>s\ ֩s1B;)B=CJ״*********0*p_iLߛWu^xn<񇯦K;5beѿsM9qɭ|b_@ə4dk[h <ӑ||?^ZzZ% Ś{MMk:i6P*|69|!{ů9iR>j!7n\Jڇ'ڍu6&=#wjC#*ya)oTZ$=;Ѷc)Ξgߗ@CIS ݿWՅu3/=ʾvPv`-3|j,NpG ~YS}qUUUUUUUUUcƇ9Asp &t`O',h?#Ry{<2tL Q?Nu 80xv@~MDpW +^[8␨j\z ;e8,ɖUij{ސ :yeg6i>(͙Qg|@jk V@Q%$-dnx/Ll6TMh]ܷژ: 0>KYMkbbP}vvQK7Zy7샏qa*>kꪆ k= ʳ{!|k"-|#{ o 5bBp|k N-~aI 9˞=?,f{s5G`cO}׏SsΝZkV,ݛk㘎3[%fCZerUkyg&O u0g=5<69~Ǻ;?kRSoUUUUUUUUU*pquf5ݸPFkƶ4|u_U&0~XhM98Ό,6$1;~fYYo4"-<:@裌h ZFwJy@$41&~[&I#૎XK<ʨQNVʊu1^Dݷ|LjM$Ŧ#I┟KS9)'yK)öƪ\Vn|yv)ʽ"ene~hl4V x: P;F-:k\X5Hr:i!wpG <@tSIڞ7v+r&sAAK[0g|"%I"s@=;Yҡ9ӯv (>ؽv#?^\ChQcgc9aOΔÍuSҎ=jNV!j190 ćˡUYNuG|pׁsO˯HI1d7G(gkRmHq~¼_m>5VVVVVVVVV6},M3 gWM%j-vg(lp3ƺop%>Am7&,s^I)l#e5֜_v6)6&bWiΟ~RG1`*.CGA$kYgֺ써QV;9F }8wn9QWPB^r'M8wl R+nfۃ1,vAάbtV֚ao}~d9ufH6h1ڱ[L0Cz:#!ZeoxOkht+ղ)GY$wr}.BC»O؎?0Hأ>r0IrEiY8\cU`U`U`U`U`U`U`U`U`\RZf&<ܡK0ܼKpHUDI8!@ SA1FЕ]66GY'?$Y;5FG{S{&tY i[ Gr !`^>^=g|W#Z=̓H4$64 lO" c@|%"φ8*P/Y&;ׂ<Vk?bcDu6^xMimQb^ܫG{~OJ{N%{c?↏D}t`LI̱ v3fۼ.4bU$~l swmu4j'nc{ݶ?@O {b_wdBt% 78%~of>>D0;l2qVkF}պ)Yi&Ey85 u9Xu9G/7<;Q<Hq"Uï`GD^W!ZKe@2ξw T4tBeGjz 4 ^1 :RвhֲAAvS=tKNs9T.]b`~^kjS*wάpXHoL3?Հɮ*********pNA]ԗ z}b;K7:8~ lKc\;$N2~3n>G98֜۹!*]_c|CHq#>B`GDq%R{μFgRI缀Be8ZS:Sxa qZpT#y:9C$ Ɉb^"Gw=쓼gGT/>{k@9眼c,"J9 {2=-M蚴_GC3,-po,2SeKSZUUUUUUUUUQK0vSᄈ:OMZe9Ԙ)k4Mh50w] 8K ΤT9W#DxTR='ho8-=H;Zj<8zzį=9HBҘgy%GI8,ބ1gw:i~ߡ)S[zGk'O @s]yZb=rR@FmA(lt_ S{ t[alۋEq0iW:V?&R7AdܘfGb@YKy+n~tZEHSl;fGԿ[}ɲϬ1bo6f7D 0r][mQF`-ȃ_[/ED }g9+A?t6iSU6i:lFIzBůypkYX[߮ ]գc6~e"}mT2PN\j>45;B^=հ/~rΧPcJy>#1ClG&qL [$F|Nx=u\z("7\M5 Lhcq97 'M7 )2ݴ;Xi- @j~54 n-> @çl5#5u#tmiM+צm= I*/d(B_-%|8wVخn>caf*\8QΖg޽V=s~$J-+|׏YTjܚL)zq5{]WM:-pz~J良}HbJPv+^|D7-#\+Եc xcQ[؝0ƹȣJϔsmDfXXXXXXXXX \BM-S7~jiHMiL`xZA͹㾨ky[k\E2ʬ0Bu>Bvnpl_gPrpUQF̮sAEt2K.H5,Y>soie~EHWTKk]:G"Q!OMD ^+3idٕb[+I"QcE KToT:f/H۩תk-2A{_r䆯#w=8AG1N| T*_E܀v`ɟZk߯5 \^r4S#;aNfnغ9 6 ꈘ K#M.F6/G;4g;$v\:?~2и:9g/R!u3]ׁܳ/gf DK}~y38p6:A#Tܶv;{7utwZIy]ZPinR~~Ԗ4a0Y~2*Kcʎp-.}qYӰN=8+ŗ\gY" gt쩿+砘TF9+XF8}'?X+$ >\UUUUUUUUUӏy% m7Ӑ!DZ롅faSn:\?-܇ƒ#c 55>1x d`[L-H+F7-]_ Z{+Y:H;#_!db7 =Su+ &Y}3[k_H5v2T>8bPK>Tr:آcc* ח>뇘yQ^+VvؼN~yLtaHcN _#X>. [ٓԊcK׶9Oȕ?Y{Uر6G!Z kuUY`k]WVVVVVVVVЃt=Sn3n<݌t7i 7<s8?GRv8Z+}8E]sCmy=DRM==W&F|ix 'Vc4}'19c%sw4j#)>VZ!PqJf&fVG׵ ~Orb{Clbڇ 5K~񦖁+TY0é/Yle`'{YK.[~GWҒXK@Ŕ~0X~%Q|gOtןYQd6d4Q.RZRхT[ҽ&]$&Ț`GzK҄ .Ik-,p=fm@mˤ]ךX4VW9u DK͗pd _ ŗ`P#~Gt)9_jJcS׶ZybC;>"&o2{ bV []s,8{riͩn{ m&vv׃3ֳu%9\tv \6$g qZߏ>oɝ!ΞκQֺ q]tg|e#/~gWNCVhϾ3̬+H"0z,K e)l{ u)wHs~ p#.0[p>?c'vG`V熍!M/u^!a. Iw̹+:[-sTC_CįsEG?r\—nEDb28>*ˑeGIA>#X_IU GXXXXXXXXX*P'SG7̶Xz{+nyӜס ?pWq;HyB(Q & v[_ :|+q?k5əNs(;/=!t7ZkWMƼq%B,lGq*CpavM6LAwN`ǵtF>.O?zɿp q~μ[xC|+NPZs9ƜkNz}u.sZl(r8?$Ŵ~mڌ$^iFRgteVZ8?⌜MMͷM)ܨr2ZbJ)%??~xv?eF{ҽ]ӪUH@IDATiƯrL2nͪftԠC4_fZf&6ע ̰_ W\|z0C|9$Hb#ʺܘcpf'Hpda@״gbJ @*v\;uuqs -&w(x@cC-Zm;3\sҫ>И2wڴg2cU\Skc*tRO 83XĢ 9y(趍 Sts:8gW)Mk[Z јe35ğGB_sın ]X&&H3q?1KyUUUUUUUUU*pƛrjDJ^A.QSD<έ1V7~Tr57(kxʖ6puh@cN9!d_+aF;@֯<z$Ndž?F~O8g6س8 NsOq4NT-DFK'ymIZp,s[DiA.sW\9ϵ+O9zXy"3NOb4 񁐣vmiaMg1evZnuXFQ qXkqzΙ ߻Z)?sAT⸼89@;p>mrU`U`U`U`U`U`U`U`UHԫdSEV@{,t!n9Va3c.y4s" pL_6Ha:=>Ldɂ9v}8Kx~g)S & ITiI.sTJ`>˚V4| "X_? (tOo>[^3y`L|VuV3)t,9^^}cT0'T `4<X7g$ 5n:{HF.sN5yZLNܱjᘅi1 7+\!`!SZ|\gB w/:\kƪ=ژG7n~T[ {U^#OzD[V۰NnfV,uOcz_@4VW:<  p8K>?YIYWs/Xgq@T&ior?޿x zaFQ2xVp +|],{'`FVhY 3/<sT&zVfo(&>wp{{^ 2>9V4pPE$|%g᥮|٭ i?xQp;l ?:k^XXXXXXXXhSY{H/nb!~QhtMCz'4|hVMkPXq.0<0ߚkf+f%G|V媡S3/*}ӛ ECqzʣWm˚ebHfx ʦIޞuAРt[@!è1&*z&_YTV.y=v|HyV" '6$ViXE+ 'uN~;&LvZa{]`H<.M^H"5xˠ _5%pK|uΥK*c궻J6N^$]M?7,+gIܼfV^_|>k\`n/R&o/ƪIGǻ, hkO?Vκ0t٘t>tլY҈`K=)߁N|ɤ8$?8a7h?+ 燍QN$4w]̤GOFb-CuSJ\ϼ8{B $:[4=W8#SySI%n# e})~d?$:N (%~Sb>&枭*********p~G^]' SFT}3lM :7Jj߭rq O~_8N@S],s) ?tX=t`46[DgD:vvن߼#A'xS|iN|VE8ԊJDF]:9 oke OOĝ/o^/cF2ʋ R@@sD2-zŐADtt^ H2'qzh]a3?^'VۢŌi.GY\1Ok$7ɜ9~V> h8LS+Y#n9A7 oSéXˏAhyU`U`U`U`U`U`U`U`U+Oq4ݙꁅfɹ*k qeB]l؟<\5G'[xӘ % hkS|5޵W|i&C]2qX^9F7wRh|rΏo=Pn`s|ϩM01Wqfc 8!xѳ};z:j/i"d~nE{UMSklb\S@cngB~+ ) w?h{FO2bXxD8STk4{1HI~˂<51{$Ơ+yQolہv`;hAi?P [c{'.׺Hsl><8 z'cYPᩥ!F, $WTO6ƫ|X$a'v}|'4ZElîpNrҪQWGωJr;Q|n/04n㢹(= a9P}*)O0ZwT)ߙ֯w$'s֭)ŁRƦ7bs87J~wgdw:p8rg2 z/㿌oہv`;lځ?z`)X.l \pZP6NYXΏ0 Iø9?gتj{s }n~CWf>d[kg@МSx?ÏR)sH9뎚gc) kG7s ~ iUHZXcF7#M/'n.̹K7zvz0֢*hk;lہv`;݁?|i֧An >|s`Ac~=+bz %ksWhS$i{yaǴnr%GS@!_}Z,,r_f }#v>]W_Z"y+Ryi}aK\_KR}9>tDMoVw]~~ǫuQ§{AM2sRN# /ϸWNwr1aui cx}__5 "ptͺ=sqbH"q_Ԑ _o`˜Hݭ"%dYh!Jk:xe+kz'JGXKF^HJ=^_" wf$B 튠'/ҡuL? gNFvƻv}__~>OEƦy .rlW.g_S?k<[O|};lہv`;=aΤ!WAdé!tV0fK3vqHح>.bB8Q}lgrZ8w~l2wda&SSsQԅΩĜGι3&,/8K^ V[?{4rGԹe7j)'jᯌ)<`)@> S\}S{9btr.n;~!w|y1NN,xucIszb1 r2x_?nہv`;lNEۇL:ReOA-r8`}p5;xh͸(>L>7jsr~}؂ߗ(V~_\_,nP{ѿ9qBgJ%oV}t_c!D܌ǁ)U#nw?C$gcsxVqўx/ď>{s2_x-W_~_Vo%;7*WcC ,|+[Su>=?kokL%~jv`;lہvt@tٗ p$z3,s(+0{jh,o Wu͑]tdzK*=Lzu~)e&3ԿLLa%m%zh !Tg5y;%\TT[H9j"[??#N.>^ߕ*`{U) 3Gګ1;_M}a_O:~$nDkj=z(oC)<x|nvW bx:4\ϙus,N.h]JM^x<[JC?olpi 95Q֌B)9Ew$9/s!$\z=ShIS\\Ƚ/Dޘ[A{rվ0_k=_v 4@Wgv`;lہeh{&%郭>>RSܞk~*3G_bЧΉbgΉx}c4~\ أS3C ؿg+\ڒs@ `0̒#*Y'YVL^Ƀ TNTqM 4c x_\c̹(jA0蒁¨ϊo,`#,G kn"#˵~Y=FG[1pٟ5 I^C:+c5yEytR/(+:;/mہv`;l>:?ў0uѳXz'0ǹsKx{@w܇#% 6>G 8Ussb+Q3wݘ-\?? @.&LóDe[{p x,H 폗={]p'!R\X~ʼn-h')jk+6E%guBz\$mbEȤb@Pǘcƹj-混D1 f-/)yY__)F?Qlہv`;{3Gsr:C+]ω{N:pҿy= 启8,ۃd%7?tU D 7F=>UH#Σ7Wl|ׁ^\ڿ՘œӝO=+.$>n _VkW\J#?$oX^8wI^hܠN ͩK9qFh97k.iFW@2B-*;?" ;YB0E$b3@Ar7wR1o=g@5NNt3U}S-{} j.dBnqߋHQ7kہv`;lہ3:N󩏬}9 $p _C$0`U!HGHsH02W ~ ʹػzY)f)9gVsZ 9OLpڿ2Qx4 ÿ"h'y'i"(_?N]?߼djZvЪ?ѐ\G8JSQA[YGe|PJ sz3@'=K 8}z%I#fiO7KIkŏ?)߿"|v`;lہv/C!ίs0'/Ͱ K^FVg/! FNm<?EiUxYOɞ̜x[;P4\JwlVa 9( r7ݵ%ֹ>g,'X֞`{QG&q^'gdRy~lWcu_-Cj|=D}i)\dls†_vIQ~Tg)\AmbΌ2_R"0WuTU+k.jB@:'7ik|^Kq;lہv`;E'BS?΢:9 hϳ=VBbO^kUf=g h˚KXj(FbkD?4Tr&#33흃\;g #Uo{h'[Y»{@r⼵EϦѳ(2zi<|쮏ԇ_Q>zTabw_i\Q@+T?X[s P <}B?}(PMkЙNŞXM}~(S1?yuj(<3?YRωM6'꠯C|/űr {%ȁ< R\ @W{Ё[&I%V/iYn\5|(4#qT_5YZĵ,Mb6ѭ䌬]◥ӿa| LDʽԡ?E=`T ̔i>Ҫk&~YK/wEU= q>#n#=B KV]'{-'kF?op)QhۋPȃٝ9!<^"gmKRR'xKdoہv`;lہt`24@z:37B^g'Yk4:"|!?` #Y(n@1{vZ'ys*ױ#O~e7HsՈdq*ڒb?!h3ߟͣ$Ě4Zfm?'"g%`(.w@C&}s}-nMvUӺs'@Џ+ \loہv`;lہ?4G̜uCjX| j=T;z%ȴj0 Y991xJ9c-j8GBk!ZAk/q'/y'2gtm6R-%2j],-Vz.jf \WsDG1oYQ/ fg[\.%/%POi?X؏[uDυ|T??9Sg zeMcntfCKD @<5kʼ 3ϐfx_bZawcn^/>{lہv`;o:pˁs0IU^l} Ưs!vLzw.KHy! 0_`DʭgONĭ}_nXߥkť<$/s!GWOhGabTߵ(e^|P_Y?T=)_sן:^+ёPy÷R>b@%Hӱo#%ϵ Vv }2'M_\{&:YO^\]@3I&b|c %+x.VC =æ~u&H?wo|0{mہv`;l:u\f|SGҞyE'ωJRTid*FOq$tZɣOPh!ݹKxi\r9+rTۊ?ĞuUF,#>\w^J gRtn( r2w-/,]K4^mӓWkyH'ge* JH'"eZ<2Sɺ* YsVg5 \A[Q'Bg/'ʬ,'\לՋI$صpN4TW_Ш/[ *_ہv`;l{)Ѓ+s$%i_0l{al;}!kyô(SWJ$oX8iGќ8Xp^c}tSAKD:ҽqt@ tMMiL!9e'<PM}E Uc/r OeQt]CʫDkJDVk@ b>xjObӉB'Oؘ̎D|ܕZ!ՕW:x4'~Oץ囀kwyf TS|W`h[\Iԯ|9m;lہv`;h+%LNgڃ)kќD5'@/G8bјk[+[} ;~}.)׀} jm$>ZDNRk]h]g~Wt'̹젺,)Ô|q&g~w!m֩b|?UWcoST & ̘m[ ]T>FVǤ{uj)nDPo|&YNGw"[GǢeoہv`;lҁ `' :P ӱTYMD]^p,*Zɐ<^*uk:?g՞y?\r<鈾{(U3_o~@{]DŽKCN)z2DJHBg.23~Uw$Q'uYy ̌sTfV<(3:lLV%7y6>#އx*'ݙ!CCO5j9`(P@V8gj4hT s֘v`;lہvv@m/uBN9ΑC ΄C0?`u=t+^عNL0 K΄K.>Zy,T88^F v) soݣװA&令㏢:w?ӳT>W͞urFt]|`x7g|#Bؘ!! j{ˎƸ^QAQQ>hc\T G?ɽf6iB#jY{=?|)ayu6k2 |N/^sX{o'/aeob;lہv`;(9HsPB9mr G 0jHts?/V͊1pX 0:"ޥ9g5ơ~XGWuĨ5GG($:23/O#9h =hڌڄ vͦ7\G4%і (v\d5\ O|;79y̺#Ϗr&+}{\}aEï4l="#'>|_uyD=fݏ@8^i}vW̽o0@იb/Ǜ_"m;lہv`;tvAӷӿ kmƏപCr֑<R7Pܨ? stQl0BC`z]κs^6&T`;>|#QΟן{'8IodϣZ[+V$Wˏ7%|#6;H@۟<ɧ7iPڷ?+ ~kNSa3y'͝Ls_{b:1q OK@@?߫|rv'2gN\~7{ہv`;lہ'7'LPJD(_&˓:/%=L҂- ͘wJeTYU/ۗہv`;lO衒/}I-#.N>@zbs_jr@ҹ#W DAYed8?ddlAZ(jb>1(4YkosqFBH=Ev0,b\:_3*['[ˬW),T܈sr\?nsBU>jIC5%Oߞ@:KW}hN6V6Y|ϜfPJ;w#{ ]ˇ& 17ss>!p`T$g`o}yr_x?/d0%4vv`;lہ\ k΄4Y%9*-q^J0}.}-ط]/Y=WjOj~^{ۮYG-#_os푟j1TuJ2,u!j/`!+2#/w.y{#Yk| ؗaw Fcnut\%6r].*Rԁܛ(ϯjjkǜRkBΉzW&|>O>8P6} nEssw7se.7 _{lہv`;<gyqK|xAZGKgKY>9A~5xaʫ)S8^\Z`?齶ہv`;l?:s])S9rZ5s 3 Mu3Lqef K>$[P=8\5GrrCrZɓU+FB'Ą@Zρq6StkCu;9ZX]?LԿiMfW#'xK;,k7eM-3?FiKg ՟75 IulYw2^4kդycV|r2f|0>L[!4']>_g]S{,ww|r{mہv`;l;09prǗX}pnn7q{(? A!: s w['箥I}-C7@a4+>'eɠ@֕uD>ɾu>bqf{v3+ZQ% Ի͹D&9\F=uIY=&:/Xoq'ǯy{BAc~}Sbm?6^U&pz~'i⹾~{*:Y+;M Ov`;lہghsԇ?9DgfF=y 07 (lBQv`_ptT0"7o?)AOpk.`j$~ݏ ~ +sUm|F8t4 8ǔ5ۺ$0(MnbGAnA"#ȟ5nQaTlnRjJs5*47*@#yhQXMHuFV/K8uuIRb3Wxo}`[!5'j{T55f{ğn]Yxzj@I*?8x˟yD@r rmv`;lہeh}`TCj>iރg3 L}+ fyW'WE7 9=!S_X S =sTP7ƨ?,@i||jIJY@IDAT) ŀkV{"l1f<èLn-/kLgѿ,T:2gV-@\YX3֟Z_ɝa/&qHS%`]xuVp)KeYKϱg_x } HGIX{^ZQGv~yھ1F#! BbvR-#K!v`;lہvOr큷eΜ=`r欩PF:Uesp%?Bh=r{fk($dO X/1cχυ\OF`UFvuqZdv{ߢ"ֺՏj'2|=GZA/ڳ> ('h+jͲ՞FGZ<׉'ݵ?h;ɱ`nПrM;XNBwyz8(p9C/[$9GYfվv] 3߱"^7ʬn;nہv`;lw{0~_||AydR:vX|-;y_3UFߢ,H?`^;G,:ڛR9^3"\?G,>[Zp̓X֊#/A(΋ʚZ ? ?xJNbwn%Vy]|Ւ91E>Ł9Md]wޫ>⒡dMוZO.A3`~g-s:wMMU~ngnf@p?l;p!uf*|;o_Ԏہv`;lہہ?tZY'KNs?*;1]|rPXӆs>e`+̋CeCtF #u^M?|W5ML#P/1:d'OJU2/\̾^;A]7uňu}}w4Ds刺)xv 5?qw4Ke.Ou?g83U3ik;%H,xx]9])L~Q6xp[?o]@tH($67Vppnwuv`;lہ@;Tu1R/s 892sE88@1U.1bѫ Hg=0kG*j咲 W[oWUi&o$T7$&E҇V贤jd&?L2h NWkz(8:"5BƦ]4 RW_?):e9UyΠ )o.w`"x%edoskq㺚.{oR\=I,/ҭe+lq%{㭃kskgʾR)@&F`$f!ԭ`T}f,7X }w{֚qKg5Xoğ۰@{lہv`;<GYN> 4[\üN9 U2=(h*_/r}.X\]3Bp zx{Ш!8ܙn8=S\{`2g*D}/'/s gw$DW?U>'Z폀%QR[NܓׇƠ=౿}!CM`SdnijFS>ЋY0qA0JbȞNm-|Fs5"Lbg_hu]pf?:S҃~c~}$vlہv`;P?F.gLb3zj{ܜ񑸹 Qi[$ZՄcC9X j@EԮ|~yrv#xμ 5)aF2h˘7VqU+1>- PuPgLʫU~PnΚ5>c[j~ߜ˩ԹtlG5wrC3_tأ$H)#ͤ! ߕgb^[3ުh@f5[`V*=!9+sSI?4R|铟_d yg7aOscr/Z@/Q}q~8b=)zL{=62+\$ odЛWcNjov`;lہv?>zuՍy&P̪d8L}N{E}("=TOV \HOueAD׵7&1Dל[>i-þ 5_=5)?\_\o2yKO\O :T#/;%'>J؅Z13mٗ=`\5*R~DZ̪v\> ]m,L/qp.y8?EjExv`;lہj:!ќi}Tb'GV'ZOVy8 8p]/ꀫJ)Gqy!H ojNtf~0Onޥ"dz?|3;g?r\,r=SxӘ:{:U3R&'>TTsk檧KXťVBr@*_.ɿ5Jt݌T2󭀓|O'ܚ[3"4f%LH=ΩQ@h^OΞOM>NZEo޷ہv`;lt`^s qX':B} Ids~:@(T,k ՛#sB=I=q0h1r>@7Gb_gaQurʉ;⋤9Iw+.pB L\U9}I_s% p=o:'9*9ocpVOE_ R5]|;HϒT ۚ:އ0909f-G|4X̅t9w;8tNOS9_Jn~-4/z)y&1ܿ/.[Fc;?ɡĩAa;lہv`;/Cӡ+NLt=P:N Pp}VF+w!:ˁ_kiMFd*>3KHA|0lu8_ }gWޯtmfޞ1?ZrW=02΅V!^ZN9r>'t׮\ϑy= \Kg?ޛFMX8UBTݿtƖJ"_` _R$am:~/O3ё#WA$-li@ry>p=ݹn~%M͍bZ9罰+ 9P3q\Stb{}~=SK2Rwn]fj; ֘f;IxM|Zہv`;l:u|N:3D94gDt /peqN]+F|^IR۽sY>POU`aC7a<59\Py|(kO(ƹy,#]xFi2 V%kW߼qפ~$c ="cMͲS?ZEKރ`Su܉A.fX#k>B xF=5κϺ{"KJuߙx))>r^~W-Gx>`LyNG0{ySIE#߹E}Jȗ=,^~s;nہv`;lځ?z$q9I:pLR^8ʒC9y3z|۳ ;b72ΑSCr끢Eg]禗kpPnHr(H,*}<@ѳ{,zOAuS} Su yĩv ]WH%wLEQ9ϿUr|㟋:>_ hZo~R-S~ \_t?Af.1Bo*B~Rq6d;~T0 6gߒlہv`;,K`|zx =OYO[J.-UJR.^\290KaH/,¤[o"F'pJc"^ڂ8@yVUfbF0E#~ҀሕD}3O|.ϧJҋa4M[p]]c>]p,{|^Q1?с|?ʊtȺ=@=입ʜ`0ؙhe!9{m *vô|UG@ލ~SGCsA0 25onS=r'<͹1½k4?nS 5}=(/uFО(%+򞨦xG2#%OTR*uO1i:>yĞEE{4}%IV1&&6kڿ |YоSxp~[xB{tJKN_skG\>dGq{뇺v`;lہv#g+9XC$G\qNğg3W9?JFsHsƽ9r`!/{0'3"C!꺞P>вõMĭOI]N M!;$B (xw i-1IQ%ud'k\Eݗ<dk[`\g\~(lwƲl#ł;뙜‚3(7nd $e^ZmMS'2|n8mM^|3ҟz޶.{~gfWہv`;lہ?D9s\O>,?Wh#5)/6O7fYE.jń+4IҺO{Dk JufD= /`?'^ X,}>IjD*|@2aO?|"rj9*Z@Ͽޭ\jQy.}Ab #q}ƃ3O~Hlہv`;mN:AԇTB}˦G :l|RƩ]t5)L{<^C|a=O(J f]b*[cQQ'&.pOµuLxeRv!asrW;r9<쵑q#7Ѫ$|YnζrXֆ?nہv`;lm095l>9XbdGz7(P G\fu@e *ņ`4"p<>f^ucYsdb}ǬCxGyy@E%*,uaҨ@Ĕ/]+_9\z9z'U'N #a=1X^O xD5!=5hZ.9֮Ȕ+ހ^>St 8!1Qnnp̜E>0nnϳlT\ފb)AuJ[+>rM{5)WҺ?mv`;lہWZbFlJAȁ /8Kh.! z3O!>4[j=d=WP\U0&X٣J@s߫XZհSC䎐.祓oKn12dg{XoB)mh ŷ ~q\#gi5Է"GA]pWkJu?7WGNϿ#1i*5hWʎq;2? pG"5>L]jB^ޔ%Qu{?-@؜XW%SW7Zh<ہv`;lOh{%urSsry!C":K0ȁ1~-aNLtWq4 BXʒa_ՑêK _UZ,s 2+0o<Ǔ3U%P9y*z y>P/*6'MS\#3>@n c qPgLZK3rQ W?Qʳh3㉫r7OyYYJ̔!>r.F#~!ǯnT9}Sf*}ƻo,q!y*֫?+񁒃3=|yD3O(8g=OGuk;lہv`;v :U =GT/s7|װfйcW]#HNQg1RԁPU,@a;蚯Ԭ93ڧѪ \gR%cdg]/GE_s 35l?!pfo8G4aZ7,R~v ~ԯl2Wn3;?[uهĚ[Z,4'~,n2,|D 7 ֔ sqJ<,[ZY L vn+?O.66L A} Gs{mہv`;l~@^C䉓)<5aS9dr>|zT: 7BΝC4 ^3A@)Bg"r0n6ey<+x?֊dXM}O C7&&\fܱwxvZ@$JDytR$/=K=溵/g("!Xg_ =pj~88w8Z2"2$h|rIBCv8g/_wܩUS\!w? -`Y)k4v`;lہv`:~+PCq^gL*1Ps hI6{b!v ;ZӲ;:k<[bZ9xtp6H%Np&R4@(0d2u Zü|`<ɣd)d4 n(r]uwɕd#:3*wtON^&{M4#~D/2gIΐ~gR燗"'Jك,pOD#@~W-Dv]dO&"YT GF2O\+`/HcYG/(?Ų*A>?Ӗ/mہv`;l:aL~=09x"AXrջ]4"ɚkr\BQo{HWl?ejvԟc&H7z=ӟ:ܻze,+]_3Wb2Ԍ0gHcϚ$ń sU+@ϼ<}q3ƗO183 X[w0OpJ2FɚŸbvlہv`;q:9> sOttO eP1OE[3ہv`;l 3ᣃ&gΥ,4h8 lMquO*΋2G)?KDLVX骞 mT:LW*^33?KV|3B=(QpDJcl (\ S4D"t"p=F}zY}KkN嗋TԻ9bJK{?{WKi?dg;y~1<bW&oW.z(rr ޭ#4'Uc[P܉5:\u[v`;lہvh<9GJh=щ08z07.yDl=>tK'Kp^$l;Յ7)+x(ciK_q|VSY8u> |֪%uurs]3hr](P'9>(~ȭ9%8_u3˚z<"a!2|!&&Q_1yfsg^3;^NSQG=3abh]0DɃwЉ'' voLsW՚ω\p7֕]H \,V#kvÒN/ѫO/#^gOvNԳO~HƘ42{=s?_p+hI>מo׽oہv`;ln{%-iڇ=g |_7rD{H| pFzϖh8߱g_%.|=QdfQgF|Zt#0o]ԊO=qx޺ F QnY/jS9(DNuz@=u<;IxLbL\oqgLZC}S>x.#ͬR'UHFr };|qr'~92zo^ہv`;lw?Du8L9:4Γ=*S!D 'H WmEZƬ|::M^|`ɟ呤M'73]H_9{/a]W"י~ r%aXv4&n69Wuƞ=>}(vh#ꐯJMpn_h33{hm;@Z^<{RvQ8K{-Iiwĥdr㳃gVQ#wpwpn!rlsx4:iM֦֒hI%IO޹ݼR'3KMcSIsR3DE7@+r1p% !gϐnLDcJخzBEOx!ꊯ{e>-Ӿ9DŽ*{ H8.//~\ҘքjTnk.>IӀQa'XZ&D{+SϭNc߁<ుG1ڳq.6?/sl|W|jHO$vlہv`;zfPs;z'8 >l#>q x0GflQY#y*6-6c,0gl<;,!2rsea,~~{W8CX≯c|ԚB[p,gXQk(GtIm6~;4o~U ?6oQ'{UcJwCs6alJ>yc̱(;m=LBOtLccoQs5Zz%{v}UKaϒy\.cS3S5ouہv`;lu@ 3ev,΀y3ܫ/qɬX1f yX=Qm  Rxcy!.5i,8-IIcN^qiAT{<%:s+?7]9+ `wNmЪ>NC|ZPIh9+5J'-O ;={rkɍ2̷q㪀;MR߻9VWX^y:7Om5ҊtIc<⣓>)X~ݜSq^˵ K<×޶ہv`;lOɞACG?'{_$lF .c/bX4lKyqPaI=d?B[9]1ݜػ MDO|b@|` - ":嬭Ϟ͑;9hu5!ߤv+,y??bFl~q?#:C^At+.h~K'9s|lہv`;8.׎}-88a=DWu4Ee[] kVGJiW|T~vl+g@汽{;x*/NV~B\_k w'& rU!#suo÷޷ہv`;lhw@e U,ìZ\g28H{ϰBw0$g܆_:43`S>[_NV WUrg%-RbGtߞ%V4O}׬V)I`Ġe\1;56$k,xpr_Wa#TTB$iUF7G2\5Ŋ\mP^bmc|B`&ԯb0 [orqJHc`k t4 <|d'/O@^o]ہv`;l6gxξ$p(C/v$ח!Ҝ}XiYg6~pJJ|1#W7^u&nt>~+ZQ_X78M8,ZwaYg>ko֏ⓣSad7yrEϙ}ql Y:⢸grΛ[[H1ۓL>?i]84 9aRFD>zt-`9?yT됳²OIc{d>>`_Õ>;[V+ہv`;ltF{Ng~Y~ 1{:oDEW.-P_F ¼0p/kJ",PV" 'wX`T/gaOj\VlȦ87u9HL!5\AO59{^8V?_,F,c-H~<֍ ?/@kg1'zVH(tzSn?7?%>&kq6mnwGpΐ3wailfqkDT?F'hG- ;'6@IDATBaQ^n97>=ð*S|qH7Wiwہv`;l^$3xf~b`:׌a/Œ*%pO _ щ9n$F5q@Nlޏ^ O' q~<A~n/_(lWퟕ]GZ|\sVr-v0ǧ@V (k l_L䐼r8g2Hj9ttݓ]ꟊ/'ؓk5UgihoʗmW>'|!,}# dsWc5)~gȬg-؞cm;lہv`;p:p~G[ͧ 3xj4.^4 ]zWwhF$: >/]frCWv9%dUSFGx!8[of4zjC/cVWK[j +>zzw6G.shQ( ep+Ys5'6/E@7}SO.Gs~6EuLn> `4r G-7+7/ǙK'$u~l?/zb 6F|y  (.:8f?+~_YY{"Ç=\[a;lہvb/̢gé׼?Ը~p_@] 7Bp.}!apwsgùz{e/9aX Bt[qˀT,1ܽ_ssf[GfX2xEo?`ZsT}ͦ| 9myIR_u9=FƯK~-}l%|㫶+Fm;B?aTE= 7v㇫~9U+X{9WƩyac9Oc/k}n؎=sL?K G~;c9I zv`;lہG˔RS~ Tv1uFs `x`=.t!ñX;PcO/@HwGqɳ=3w_"zn!ՙKi˷0y|q^}i1ׄ-G2ou~?oډ Wi`pi?-X>)$Y_>-PO?y3jN.~>1s6so\[_ #_‡.7^ہv`;lہtDVC?bdל ʛi[^qF8PZ ұf_*5Ι.;,;?<-6KlbUw*$>XK ÛcϿău\*2$)*8o^i..,0/[U>&rGfChq[QKƚ# W`e}~OmC ޜ>*[SMשP5*$}GǏk>B}Rkp0ōǧ_ڟVd?S=/E39JgV7JaَܤAKJn98S>Ulہv`;X=2mf8}v>_6CeKC-Ηϙa<"?=^-pGN9AKǥlԄ 0FGNbNm,/Cw e~qWO\D|/Nn,WOlԀ3WKp5@^ݗ Lo~#4v# 8lw]op*wNCtgyRr_gJ{y"/ؓF:MW_o~lہv`;ᡓ$wd 'kvˍy=آmq+~́T 0Pɑgx/W ŷߴ6+76c#oYs`G!:+q_gׇij (ys~YK=X"/LC`򜏋E!Y ,:)G;*w1W>G:\ N(c%l1ږU:-|6~:NhP˖d;*/\r׀:+hx@᯿aMINh'/&Fo)Df<@מS"F?ƀyQ6ʱe`?jm X?چ'78\uFK׶_ X|-GWL7[ȽBz[{Չ&؆Fv>?M=ڬix9ďwNM|g/&k_.ہv`;lہӁ222di5)fV3?ݱ5txfgW1$^ G @tı8h~ V{cTə=~4Ua|f(8iQ޼^$7BG2Rdѿdsc\E92ŘKuGQ AͱA ^aQ!٨ `\~"i[N|Nq? KC.6e= oY4 ={|jT !_e_@RH.ԝGٻ4W>`kQ% p{>ghcqso}8I8gt%S߬p3߿oX[ QMcov9)\@;7.J|o|ЁܜU*;h~>vF'y8 ?qCsV*\="G sJVGqB5:Cipsp"G7X+uP%"636ﯭoO\Thb3c^[?ܤiZcuߵ{C'C'Gɒ.@\2zÅO8mdn?Inw ?Ig[ہv`;lہv@Ci:Fyp87:Zy1|@!qwjɖbDbg(qs}N Q@$u>d?X'L{g>3G;k'JuprPe(BW,H+"')0Ԧ_=Ҩo]o=kbVbO{J~6Ίꊕ>oMl;ycӯkwR·U&uv^N2*ϯ*TFn07_ظ.&]>ہv`;l_ 6p]5A=~ ;KJǧ-~Xʌk(:5F.?8A\O}&%:$ 켽yψsa+Kz)7Un2S /ꔟL!^9%?G'o>Py.pl3= sn"7/ş{g.gbpG;'\/>>cPkɷcV$g1E|1-ס^ØCw.٪8xh$0A~#=誀χ_3.as_?Rm7 ǃ^ax6Z+i=_& ):ʿ~15q_cw/ܡEN_vj??Dc&j\^}VG>i*~Q:T5o0jB1bVc)+om 7v`;lہt |N>To `̣5kG',:b"{qw'E̍3jsVO^H{r~8輺} tH`_b/\}^}?vRS1-EyW?XzEv`;lہvDŽ<Ɲ_=wt;?~I0Jy:3z6UCǠlO XbiJX/,ؕ#/<$w7\GK7Ͼ`/y*wnl1V;gCl1不&k 8lq'(rpdxKyh\PnlK-xN\CCjxm4(O9֠Řh,J*gnE->[T)ѢͶMW]#{3}\gW>[1]GG2ƙ~xCGd/\~Wz{lہv`;|u`0s` WC'_`Yp/vlw4 ֜3CbG9zY=_UזB%_Qlo*s@_}(.c{J/;><;9ETrl ̛jEZ?` #T.4dt쓗oO}b碴'6⊓qD ̗g/kC'[R.|{ig?\^}=cl(ͻWLӯs^ H5G祏W1H`?+޶ہv`;l}té>5f00Q)k1iܳAm<Ŋy@l4\^e96 x?&Us#M*Q-^JUro bu֛u?hIJWklD$Wym]$طrv-Aɿ<jR]B_cr@A掏^^u, '=sxFsBƲSj_j(6Mw|kZ7%=V|)z/*q5/Ĉ-kqlہv`;0~_)^2 3ۺ}غ~XmX ?o7'żut&kxoOBҤ]]_{FE=yډY+}3 v?y1MMUG> 0-D/*nM-iVDnh3JosW{"̵UOH>(hD9ڿK~OVrxk&+Um;F>kb= +`ꟊ$\/O>cԧ,<Ӆ0 v`;lہtF{6Cufۏ:$p1qnMk(%Řf3G3r{_ݺėh|?RGI9yy&M%hN^F.YNDr$켎qS̏Y3+6׈aW\{1񉇲[n~N 8sg Q>AL+ݷgc/p]I\1w(T$Oⓜ(W`#א!QUpוi>, N&CuO Pϝ"SKXbwsz6ߌoہv`;l>;.hdjzEzPYpbpP=_[Q<|.ZrğALmh^\ddesq;v6#C3 Loa/)R!qFl5gܳw?`Dڂ]…_?kQbw|cGI7?sI}ߓ)Jb~872غw-7<Ѕ|g\':Bö3r|'SSc}ޅݘsF˱DKm zv`;lہVlcbt|5𞁔Lg㰱Z23jF 򲂬M/:0~́nb-ژߘ~\F %\_*رDlc(W8 pc.N'n[ӛ?2}柗U^AoҬ |KÛ?~A %޵p?_rv|᷎޸8Yx80n577q'H?oyK=u蟗h!4f ^3"^SG^oh/vlہv`;p`d1`jw_q2<#<}bUxrɈۜ=GЉ@bd]t|9>gi>+u&0wp@}C{r<5lj8>V8Yts_z%2#|UAz2- YVTW>rcS p< 64zJƎ# :9q-1_daj- >?gwibQ/6[騮?b4dsV+i,X_dHhpacCR~h7/s$횇OϯYݛv0{!q'poہv`;lہၕA%0Fyq΋ƣPL!rc}mZK|koryqkn#8 Gj{:~F[K E `?I_vG}gӰ(_bߊܝMz۬\~~Flr5G+÷XzrM,ԕSԏr+#,~Y&zs"% %+=Ukko,a% ˎeܞ̮6"!tW Ws֦[# $lb16?v,p^Le Z[B'G8m壏&Ke?A:?lہv`;h3q֠ ^ũެ;pw?E>> :A:p?:8r9_lj&&֞m~?Ѱ+b"*!`78R;qsLI'>! &<ԀI}vq17<#`{KdoIo'+Uݻ?fp5O8 _蔛bG-y$֝9gzQެ4s'qR nr›_]U7y_L#oUlہv`;ҡuJE$Yuz ~_vEہýWqy HYq6>>F=oq~m%l++XXf߾5'%JNؕ@u`ށpSGҧ I\j*&m~ͩMjm@n>QNʗ6E#:ťtI5ۣ5H/ү?2ZՄҔ}`@HV'~Rې{V 7\jPrH7@xˀ>ֹn uO*~ 3՘j6|f8|o~u;lہv`;@0sKhb>]b燋\=<6W yA})&?C"o快G GCw~y1Br; /l@khA%PX]gչY _Vm邉POdk l|O|-pv?~jl%ҐÏ95| G8?bYbz|boGߺ"viY: xrЂKqbHLS[ht_{}?%d_vl^ہv`;lہEfϷ>;+2tj~ݳ4fkb=j~E4^! zY/F`iYfG%kkV 71v %DU-hVsG)٫>]=>VTB_,kg+Ӊ~ָ89lہv`;Gۙ<5jΡ3g̛kaTCWQ QY ićcƭK!}++4L[C_@" ׼uM{No[vn/roNpJ=^fu|?wb8GEԕSy׳fZcLbvձ9uW|寺?W]¦>#yq=9]N^fgtr0·rTHY_7XdϞ,)Gޒz>o|@W0ŗ "C/%dګ/36|~˹v`;lہ<\Q񾄜!ras^@zEHFX πlWVV dXOHӧKkաs7ep]k}>uQy#vdw&֏?Kdҗ%yοO#v's*J $G-^L7޷;w<'ͿYi=J 'bx1=|穿ڬ'}R^W.V\|mƱƒ#$ (lہv`;2?iAR<2Tw33~! ^e, b/pt:5CndǍ/qr[S@+g??\Wf r I@j^#ıvT}50Lj Dg;KJbp5'>aۇ`'v{ U6Ο}ʪ\Rِr /VjM\4e n.j?ۿ*\>h_?썫@Zh.40\>O(z\т9*=g _mہv`;l:m=`v`mg GV=kW]ufwl6^X1 םͳ؆?3;nzY*=ϰ@늬Zl Kcŗ9p'A oFwX{?y\>M"~Z>ɿ=PiXN5GYY!S~ 3/BɫDk^に%;?S:8|XK>߉`Ȁ 8eJ.c}nul,ي0#S#ysq_gldU g懾v`;lہvz1drbΤi!~TJs91h]lD;1ӞW EJ׆'Osj3&K 9+s4^{.6#hՄUtnnzyV~\7#@ 3{E{YxmI@ ; e(k<8̥594гAu"'DĚg| JRքk! K|IR&tCh;.?"|7~b"k\>7vl_⏣Ϥ|?BFW{28:qv`;lہ@; ͇wȴE/ g0O}a}Tt0Yx!㎋WNص3rw YӜ;oy3TM&!u'|ۿ4H//c^N\16<5яMz`ZSn0B&fnH)/YPR69ɣ$*7lo77P[16nGo^m0uilC-ڼ *d6K`KJꊄ/~u;lہv`;S/H̡3\zWxW <Ȋm,hR +cڃı'&8I+06\gϼlJ/!~\cx4qJGg}Nӻ~$2?bj_ȟ,56BIzj}fB$l>xo=oGHgCLA$^awDak?H`Ln L̹Dd f7v6@UtU{޽z*M!%1CqHQ<9+@g\NS@j'O/\mHJkn1nEalNpɓs< EC)lb v`;lہWT/2212_raրcgπ+/5x<3zŮW$A5ěoW!(؊7/Y0 v餋͟/V/ruO9H*GCqnCHH"p1(+o|Rq:W=?P̪o*lQU1#X"lvC2:{fkenP"zO2ه7ǯ>Y.߁ ~7u~' }ůb_;:(s"|>%NAW!8S|alہv`;eh̋Lx{83f Iv3BF¿Ԝwi#[o2͒}@΀i_VUy{b?Kyq|=!H?|pt$ L@HmM`DT/OAB.d~ :8EhCNJu-Uc''ܾɖz`6gĻg^=훭y*nS?E N? O2KϪq_Q$NV@oLe#o73E# s1$S=( ճѼUMRk/vӽwZ6'Cvo.j}XÏ =$(_- v`;lہh3Xj~F jpV/ 0Pk0}og(WWOj%$n-hcL;gn'^.0W͹HmLOO1Q Y8ZVRaNpi5;Hk&C :NL]gO ~#n>l柈z_Q'{K^U8Ȟ:۪ghOύ_*,_t˓K%=^?nہv`;l gpQvlsٙ{0Cɰl9Y5j`Ǎ)x7XXgp\5n384Bqh:>ύc = ުG*ot!˕!}[ƏppΉqa: o۹?)4n`[_~G繽ům7|v:c_Fܷ曰z/=OSbRޣ]k5~Ř}tc}lSNf_pz/k ?"c}/uv`;lہvNwSf9tIvh0w}W=)Qs10T_`ً۴S/S6ed@g0| ?oMV_Š12U>Z z^}] jԦZ h~}(#Ύ,2)uw_@IDATԩ'}xe[_#1dB7NrVi@ 6V=?V`6ll[4NG7!qu#F;iqDv%UC98IN<5pM~|+/yĤֲ̏v`;lہ@R;Xz_pwni9 +h1^ћSp`0/=~+ ǏM@rSXC:Rf!P8T#k>:q @k;pfա6{E}W|[? t9gS1V =Ct;{VG v\B&}=hǚ=K_ZI M'+`$Sc =%6)ጝ}‘UM>\_:_5tg؊oi]ہv`;l3}2K ˃):^=cy8I^Ʒ ;^}a&hI1m MmTS1zmS0%!mZ>5H{R[?RK^ e ( ^Õņt曫ZD\mV}Dw> ''Yp g%'>gWMߣc%]ہv`;lt@8/,\^g?gh|:Z9e f)Ͷs?t. `#Z`cLsX#jNb k9Wm󫶻sL~./ciAA$' ?|;/NیL˻Gwbh?o.h9{}Wkn)3w[^]r=Ws}ׇE1ń?S|2+񫯺#!U[[ Z?J" <.8\D?ureg,rD`uvs~u"P}8n8~ ?Z\~lہv`;h8Z!y? cs5b=C2: sU"/3 `u"\?jt䌵{$Kw2P|2g9/^E*1"@FOc0/H9Z?&P&8{m=xO}O9ٝGrk;lہv`;Khk ; c8Sfm;63މ<`a]' iyz&'%>RW3|묒{uNOm?bK㵉E(D3HV BpG#/Iݜ|D Kb]DOOaL*ڃ=r+SS%4OfUd`izxW6nHuű~yqqĦ| ̹r6X l| zZ6NY>i.|@oc9޷ہv`;l{Cp4t36QvAkY/Cb ge#~0̻s/WùZh)ףycܰXE-`'!+}`ÈBF:Z2<>V` 2\2}s.ןúk=+,7^-6DyluzѺxi.20BmP{eKizTus:% xm ɇm>=T:v.l/~4jc臯 PMji|mX#s^:?,V!N >T5{T9Sj-S\ދ{v`;lہ@;OϠg`١[a3A/ZW Q2l_PG| v.v8y4>k{q]Z V0u y"nLM/T?W_FY ׽h >*ϒ?E \VЊe޳ɋ *X'2?Ϲ]>˿X-v`;lہ -=@z zb#h89XOVOՀub>v '9V̳웋L2 w烴Hee hYce |,e?LD%7(+0Y^rKoR!O ^|˜Uz/! ;>pkMA]y ^Mx)z@GSG=[$*G߶YwxH]{;Xs0?Wgh%2ģUr+8`@n|\˿䏎rEfNgO^ہv`;lہE{/Wk@[:6~ :gpcfVVkuH\ m, hEE'1/d6WK܂͟s??{p%Uq骨IP#Ħ=D]nh`͈8+pXFoMϙ+ٚ ",Zn(ls.aW| n9Iί [6t~D FZ۸OZsݘ}IΈhGG[S'ÿ`Ycv`;lہvV. Κ`.Vv0f&+m?~ktֱgBfItoeUq:sx7:qux7X3/¦6r.*ERcA*cR%|o79Rz i(肽ћs]7߿ =(_wLTc@#ݚ3y:u1iݫ>֦`Asfכ'i {Zu\\;#پg|8 >ʀj|Sq(=迣y/б-_AMr{lہv`;|t{ij09Nv.չK8OL2ҿxt=?=K^!~{)}&Bo>:?.|Sh#pԫ]}=7UG$׊}|Ύƻ^^^^^^^^^~chqpY;КzěV4swRO5]Zp~}rZ qN"g-^y Sqkx]J)P=^F >lFv6?( \$I 311O:&cxZ=k\+bZέuVytUk\nr8ٷO Lkޟ埐5;Mh9O C=Ao^ۗ3+kyn]_&H^^^^^^^^^~j oJON=3>3/>oTtH:|pDkse7>amsQz0L3QuzLrdF,(6fvίjq'?\>ן?郃?`4>k4&h_0IgK= 2y?|{)sr>+ܞwՅA6kTjs6N?_W%G-s&szSϑjѥ7{~+_ ֘ެm'v_w梞mPt枵 +B|OW]iYk2SK*g[_^^^^^^^^ځ?:kA6C3kꉹwVt< : 8YG8)Gc4_vYjG~\1J P*.9ueQzaR=%@r8Za8O5OP8'P;لڸZVrۧgŧny'|m8zSwUO^gJ/ulҵOMwV''?v;E#cd[(uű{uHs[>vq&y\Qu<|Ekl1 7ı NK=}ׁׁׁׁׁׁׁׁׁvG_362jzG"@ѼLt`-/,x/G'7TNB6'G5p-4]M翵>LGf ??#CGc&%z1XJXVՉu΢©yuhco͆kY?1,9=IWۮo"ȣn6ɟ lX.w(ju_U])#=AV^h(#s:~J&| ]s b&`2mN\ vQ-:Ӷ̽V~kl:::::::::Ձ 33Ţ [o!j\X֗0:C15G`sϵ>RίOZJ?+Sp+q/ŧ$b' DϧyXqC`<\lהƹl=Q͑rm)TͬkےzŎÏ恞ځ]x3>WBrGK|vhqrIOZ'CϬ>kVwRRKR0{8dض @\u?uLxgֵ<{|Ͽ/p-_ЋF =v2@<WD\];pEJXhg_B+_C|7\]l>]]4ј t~.ٓWw.ޖ1d?\Z}~Rg$' VZ΄ϕ:NXKAr>$9MYΔJeH(9忊wk>GU хBQН]~dn`W_|\^ …7޵bo}`n9o}xxxxxxxxpAz&3Jv`|aEKSeFW/W c  tnBGpUĴ)Ta[j䩳z,Z_>QB:#Mqu: !X&I9{ul+QWJhnN28>Q3]w`r{*x)[K3:V#Zռ$VVGV@E}sl/삷N>|t*5fw6Y="AYV96Tl_tũ3(sN,_jM}X})L˷u-LN9=oԞUE3pFd}6d ck \ xu[\Ir(~Ѷ8h6:vsOM~r cfC_,Y7oͩD:]yЪ#f9zgȇ;~{;<;? [ΟC?*$D,ӧ& dF$B?joOimux UyE|/3k̭1ci*pPcy^OL9KpG[j-DKZ//ŷ}xxxxxxxxPyP;nvb3?=dT2 CqYs[k`|y/7םW~W3'`D6x?#TJTԠިs3_-mUlv bbUu ~jWVP1,.,)zq tX=Ϫ1c)//+:5d [WS3׷QŜSGx8$W`y'~tKk[%׉1t~6 Jmuuuuuuuuut@;~`X&K[ (Zm=ؾ$ CV寄_ þ-Bhf?j4Lrru\nWK&* 7B`ͭlG\$?ZvOtvTSjL z6j%l7]Oj# ޸>YDI6Zo!5qتXIP[VS⛴uwDZW_}]g&ִ}_n}/kus]8?pD`g[2\Yhk在~ ZpՏF{h|W΋C= :ϙJ2H#5~;٘f&hn Zcd΋02+~RS`#qFSo“Ҷf6o9ѥ).7љ֋C5SY.I. K> AVhl^kqJ? ՏnGw<~ƉmUqU]b<,挋tNb07s.=YWzcLUJ[M_? 8?L`wep-=9 hVGF=&-ѱb.x"dz}1:vuoW GS~t]O{$) M@" HC-Ro5s>ݳVq~ǟH8z?'"h?Cowa R +\=~ķ5W](gG#],:op#kO]M!?gs-޻}+$ ~:::::::::pw òG[Vfb_|峍!?8g@8Ipvo~7Ӎ-Iu _zP%|l qQNpO o?p?rF=ao=ipxR_΄ulS>#*R?>\̷MLt)h^_ ld09%8b]=]se]GpO7pu \A([JEX6=C')FDYkn~sTa-?#F'  >/8Ǵ8'2WO|&vw^52xNC`ͣd(T{ps? :\5,+ f5[>oLgd:2@̈́f_JBQ+|ao^Dvq$5g=gYvC S i\?dsx{+}¡צ79^WmPN XI^vDIKChuy_/!1]d}WԪk8zmN(v?93?sى' Q&5rfUI\:o8ٷVpuׁׁׁׁׁׁׁׁucϴaY(S(qT&kjr B@>?{ B.@kg`p O~.r^5؍}ꍗZZsWW~袓*%UۗT2Wp_vMp+z0HNg 9[RFΞYvhHhpkf=՛Md8yʗ&VdݞEǖCS9YAbşO4تڌ[5S5jQpK;6>B+-!x OӋ$??E2G`Ͻ|_Uλ|u`D[/(!u^M? vZg-_ heUd-dWʏ=?I+jI~եi_FG Z[&_0#g;oJZr{6w-خ4rTԾxh o{v?C ͼ+peQAɂ<`"o}_k("gCqJ6x Gu֗ϖBEHXSWMLͭDm#nϭ+83;gU*c?6k{:٬iJ;?×_t4'=*P]˿:::::::::րwQFROQ.jh:, $XgA po{nKJy4НFq.|fR&ý]0|鹤_)5|N+<s7e8 ܿ[[i=V9`$8ړgS~30umHeLa~r#UBc>l e?%P.?FS2`ϟ!UÑ)O[J?uaӗî{O֣u ;9||'O [:2>?ɯ[_^^^^^^^^E5|2ʣ!bkJës˴fA"Buptr$&.pgCmP$!\/mnlespJѪ ݵxW9(c~?{jj/>sr,b(e>[^y5D+bE3X@R[sh++'%jڄt ~:s+(Yoo-*}D+Wl6q9 _ @(:97FB܊co5az3 Ǔ>'Hcx"逎*-}~^jOՐk:JS~ׁׁׁׁׁׁׁׁׁt@G})`YtgNAڡqkՀaqe~=w`.l|D ʯDM A~BDY,ƥ$ سkO}RZIoe%bU65>֟}- ?M_kJz9t uj/ gM~'#ao>2 ί=%N66nʝa8qɏ|\w=ok1Zc4Kl|w>gWl[%ypWv>"i<9!%W5.}; >zxxxxxxxx> 3=f"L}Xـm\ :`OIb JLҷҭZVg焨Rb. !eyiŞr3|L_8}N>` V/ӝ9/%-Q?xʗ>du> }sM&(Nz O=IJI2n"J (ލ?}aΙ /@ uF#qoMsy_P+so&EKp›uq̢netKׁׁׁׁׁׁׁׁׁtM>j@5A=av3+ï[4k96rrp R"ܢ9k5S<ٓ5G| m κ>e5#;YUæ:5X56>W_#%H*6눹ѠUߩ;wenhR<9 Grpx~E=~t{[R +OW:::::::::pu`:lj vB;Ay9l˛ׅ1M.t1 =!*M ^/%:X7[[vUSl\k67͠Q:K74_9^RPGV@ʮsեֵCSkzr|WoF~+nU j VKC:4~}9Tz> nEQҁ/Hwo |~n>j<0ۆ[ =uuuuuuuuuo;w_5>33ñpg])Ʊ\~cq~P~(ё^. gEճ1Z{~mFO/(&y+9}0į/©)cn2XG||0Pg9]y,?/JӹЪ|w~Qzz`G="N]6o\krO8ӾPneΌczV :3cͥȣ5gIZmD\9T\?";,U~Z瀊&rJ@y'iѓ =IJT<9ِ+|J`sso/*piy?žuuuuuuuuu ؿ18fNn2!7Rb?qFkF.x/]epgIRw2uH} ?ddYl'+ !_cD&)zs=_6V['Gnqq_mj=lqk]W7xTivZ{+ݘk?D;rJ ps,B o|* ~zk&|CKM|'agg]y;<POpqo^^^^^^^^^bv^{g:I2u5dg=tσWC'6tUH9Aq*B&ѹT-0R8/ֆ53R"*;?']qZb=)`΅xjK3"~P*[9ky$O2cC-]K`[;w}G}ǜWg%ܪ;֐Y>q/թgcr0WS簓bŅW'~pVLg\CTUZ4m B[OHm$^&J7΍LWw{xxxxxxxx88=CfT V;$ː:kퟚ: ߓcð-[F+s0fu43r>q EbOܟ,j眗Xuj"lzHzPeѡ6GW1A&lb:q܊Znl kgg]ɽgJ8t9Xgdz'O/C}~s-ō}%}Vg8w9?wLqt79&vb5!>j7S]µ+m^^^^^^^^,5d3Ѝ/*_%tngV3%18T=_#sEfk(QE]g& HͿEpȯΗ 3 tރ䅄5 (7JӭQ{\=Qy=^u}~O]4E[SZсf8x:#cGa]}ܡvb}1pBo|dci Wq_6+ ʸerg{z3ιS!Fb({Ԧ# \ ";7]ĝ~"z|I݃.jĨkL~}B֑{k7_j:>:C)FX >]:::::::::0).J/chVd0pgb.^:Vs)3D[|^:ֽ5CJPW&0!!f$X57 G/y|[7/M:Ӻ76W- ~>IŔOHg7Ϲi8`ݳ9o)p8?Cݻ$=|>+"a+KjP"rU[?Oh+jw|r0q~﫟[Z_5*Q?-߰;8}𹬋?V@֠r';| 8juuuuuuuuu ~i9)3jr*5M ˴v"!l3K(韝[0|5yz" qrfBxRt~öO=*|lybQ,XvCX"s>J.'S]) km.=_z~5\sy; wc ;1_TF5_> a amy6UqAAvP^^^^^^^^^::ང w6GDGhbh݋9k5V/3CUhAp$ kb>e~)A5^5'mzZAunq$NS?s:U?:[3}E]xL'?\}ֽSg(ӿSgJR1x\ŎO$c'ZE`*φ6\W)R+_[CDRZFHGS.UcH ֏ Ffc~pr$:::::::::p:LnD??Co}a;բؒ˗4lr6OڷɤK'L{s9ǏZ}Zʄ[3{lU, $N`>Ro6ēO&/b穫S\_t-sU?o/|ENY6sOpCO^جˆ/cֆs(o׿-y/SԬB^/ 7X 2Oj:Ľ:J6-P4 [~BK 'ڇg鏚MT vL~ Oa9$%֢~v\[ .@4Z]ךNJ%No~KkPh1Ěu˒E_Gm??7W36}VKKjc1X]m3]w܉&)϶ꄌ,3)$LGF}ꙵ Z*~tW|/"ׯssсoȯ[_^^^^^^^^AxۮwSð&`\hq8?l rwbGXx+bru(OƌWԥ vo$8 {3gi~|W:\tK%rDm`.?zi̞5m^![Hݫ8IWulKڄV`M?kIq~)!j͟_ s T8x?~LR-Uax%=|q凙Q(N pOO׹б&2>&'"u>ӿO;sSXQ&qɯBGCx>RUz2`??gw8 ] F~Η/Μ.5Q6xs%Ǵ+Y>kh}KG7^>+|4XGdjU 1H OBqe|_S88!D%I6}6Acܭ_*^6qg'E-qqm]w' v̰VGp;785옌ؽ2vX&//ߗ}|>R7njZ]5pI_qgusT[|瓭jUz9*@pOe dsoFsX85C~Kl-NE)bIY6W==qrB.d}Փ<,lC?Wѿ*)5.V9m\% vr) -ߦ7{!:ir;䌶͏/{VsR^v;R,<uuuuuuuuu2wո[ zì+0um{wPX}lI{ 9~8խo}'<̆*2LQ-BTy-s-͛\kYh5j_fxq%T0P 2oNjXe \x|ilouͻOߺ[Әc&Gx.ʨu]129Y}SQZ>[=!h#\{.@T_ 6[.9WZs}u> 9Ak2]6R ]n`*R]{`I&uW.ǹJ0vU[/~ oyxxxxxxxxP΀O?wuD˩7$S[Z3LkAC)lPR ˟B+mX7Șr|9:N϶J[APlha dTG }FSi|Pջ$>\Oe0-3}>gXYsT*䑥m6{~q qXs]_j1>6Rqr\ь k7֤|~$`ET ̾~w5l\jŰ^5]Zaܽ35֬_go0wIa:m| fX]ҁH&FQ#5 |fhSp6Cp-3e[OTN/Y$h{d_BPrIuAjN`J]L6F*\cOxJv4>_-6V@X@[hMM +:'>'֐s?RO^RO nG\Ql'K2j5w^i>pi8fd\W+_wQL,̭CjV bxڸfbOם[G|2^>z$zKMkEbϞn:wq(y_ "s)]+lbk]AEH~-YLkuo[ՉzO>YHU _?=x3)|G\eۓ{⛋=V¥e ,<=q8{v&3qaGO+xO]W9Fb~s$-6q1\0֧N_>uZ:8`Ugoһl=9vHS4#lБ~[b`nv?!O&,rV_+#.^:#NnM Mn|l& BG R{w`>{~쾴l$8@kScx%|s/10̍^U__Ά6\q,`:J?kr*Θ{~ ||s[h{-[lVƉg/za s|4kDmW>Vw&|/W$㓛E-A[^^^^^^^^^ԁ>s :$Y;je3N} ֟q42 r ͨ9FX^<7\.v jG7k=AAvgF"= ʡŢ[څ69{^Z2 TׄMXꗶ{8r`眊+SwICG~2#p+SD9(`֬o@ğJ9E:=\r|{枘UYf͊]}-U1sQ02̥,}8}MD؃wm^lM]B'SRTjBԣ{*Wm9"z݁?43D2ΟuH^vNՠ:h/LC1Juo>b=@'+ā.z1?ƈ.ŒZx'cu#RCգ@k8`TUAQ?oZl~s5dJʍܛ ~7^='.z6ź,Տ>ei,y.*,p*}$jh!Cq/c8Xk8~:ʯX4-@OB^gYgS:PFl / e_L*تCg#H̿7Yd;(,z1@"~՟bGUl>⡄G!XsuW]86Qsܨ/,'w|iܳ8ᆿk7 7_"쑚dPHvnV~ A 0 ?=SylVg6>v\'m<\9Y[ZS'K6xW#;PR!/=8E\@䣶߻^^^^^^^^^h{r&M7;AaS׬`ChZ2dp`;HDž-ǩ%֡Psʣ48qwQ f. L-*Yq*"G5"9±w[a5> 夠2 p?+y]m>WIG=)Zq:jxk̚K~]#E{ NiaϏ{Xj"f\7Sq 9յ"Ձ?V5v VH@˰?b3z^SZG` 5(+q 7#1?PN@ac~d{#7҈69%^8IzܫJx?!ф}%e6>#!P7t]Y'/ѩ& hݬBpD7{$KCUuppFܵ 5CWP?R3j0=Jy-+y6KJ I =uIBþ.%AK7g-aNmK6uh~B(>nx|kޮ韂p ~aeb{鳐 XD̶Jkߚ?WpZ?b?_zbۣ'뱭TҲE.]_7C^U~T8Ͼlӷ{&V\^cd84CW# /=b?xԭ7Wq]?ђ5m^ į|%^¨z>~/)km|;3|t˥.yȤ}<1[2VDlyЦw>ľf㳗ۢU|Ҹ"^F^sZgGBy'ӓ?{鎞&_[i]^ξ]oRTP?Y_/aO?pc}j fO^.k||Wv,ti3Go:::::::::h?b@PkX~\s`*4!Y?2ξ@[:Vy]g~(51|r \!e*8OkQtI7.2xr'7 Sk?YBW=;’Xt}F)TcsAB55V[ыOŶvd SKz *-ݏO & ([ӞM7Hu&tGc+8M%KPb'G1O/ ǯom|Q~h/}ׁׁׁׁׁׁׁׁׁӁyfS/\J3렋$}•&>>*s!g~'ʡZ|e~䯾I dN /L=wb\h\ZSsf9掝_;~IP1X'^W/R?RO `;Om% M ~}6o]{AR92zZfR̶:C_8|Ҏo< zկ=7r7@/k9^_שI:<峪`D+[Z(AsY\fsk`i f_/w{xxxxxxxx6^3d2δꡛ(6KT}\FވܭwU;G]xo7?Z/]zeߵxe@q#OlC*oIJ8,P+WfÖpPUH[mBcc-T+azy6CO2_Qoz~ۀˇh?q] 9|]?|Zԑ'яH]< Ӯ 1%f}ruB*/@eLE+\~k,>9=Y+T9[^^^^^^^^^ߺ\.,YPܾɳKԄC8?B:::::::::pw@^84DbhbY5!P=88Q̩xєwv%̕Xow~1J$4lc$K~+psJ&'g_͘{\3 Y;kӚrߟSX۶*#?+rݭ G@pߐ;w36U!ܴmӳ|h( %wuߘQMZ_>|y]o|>ݳRLh=|<hsOW޷_[_^^^^^^^^>;w ҙ*52XW3N``? ЉɜCsSi}a{i0'>##AiyفƩ>B?f 򣳾gۓ[ۺQ]?gRF-(u5Ws~*/(L2õ+y܋[9#`RK<- {&o'QLu]jK3NϦRn~dA%X}F|?Lr}zí4\:jBؽ8Mva uJ3N ZTzq_l?U+,<$ym3 bۗB31`ǽ+I-yu|a wc FXySDn~{c|媆s ro^=P9LͭXUVͥBW| /xK ZP5Q JqċO{v=c(JCqa}9A