summaryrefslogtreecommitdiffstats
path: root/libmariadb/libmariadb
diff options
context:
space:
mode:
Diffstat (limited to 'libmariadb/libmariadb')
-rw-r--r--libmariadb/libmariadb/CMakeLists.txt526
-rw-r--r--libmariadb/libmariadb/bmove_upp.c33
-rw-r--r--libmariadb/libmariadb/get_password.c172
-rw-r--r--libmariadb/libmariadb/ma_alloc.c196
-rw-r--r--libmariadb/libmariadb/ma_array.c172
-rw-r--r--libmariadb/libmariadb/ma_charset.c1603
-rw-r--r--libmariadb/libmariadb/ma_client_plugin.c.in503
-rw-r--r--libmariadb/libmariadb/ma_compress.c113
-rw-r--r--libmariadb/libmariadb/ma_context.c728
-rw-r--r--libmariadb/libmariadb/ma_decimal.c479
-rw-r--r--libmariadb/libmariadb/ma_default.c370
-rw-r--r--libmariadb/libmariadb/ma_dtoa.c1924
-rw-r--r--libmariadb/libmariadb/ma_errmsg.c131
-rw-r--r--libmariadb/libmariadb/ma_hashtbl.c580
-rw-r--r--libmariadb/libmariadb/ma_init.c87
-rw-r--r--libmariadb/libmariadb/ma_io.c224
-rw-r--r--libmariadb/libmariadb/ma_list.c114
-rw-r--r--libmariadb/libmariadb/ma_ll2str.c70
-rw-r--r--libmariadb/libmariadb/ma_loaddata.c265
-rw-r--r--libmariadb/libmariadb/ma_net.c595
-rw-r--r--libmariadb/libmariadb/ma_password.c162
-rw-r--r--libmariadb/libmariadb/ma_pvio.c598
-rw-r--r--libmariadb/libmariadb/ma_sha1.c326
-rw-r--r--libmariadb/libmariadb/ma_stmt_codec.c1362
-rw-r--r--libmariadb/libmariadb/ma_string.c163
-rw-r--r--libmariadb/libmariadb/ma_time.c65
-rw-r--r--libmariadb/libmariadb/ma_tls.c237
-rw-r--r--libmariadb/libmariadb/mariadb_async.c1946
-rw-r--r--libmariadb/libmariadb/mariadb_charset.c74
-rw-r--r--libmariadb/libmariadb/mariadb_dyncol.c4369
-rw-r--r--libmariadb/libmariadb/mariadb_lib.c4979
-rw-r--r--libmariadb/libmariadb/mariadb_rpl.c2099
-rw-r--r--libmariadb/libmariadb/mariadb_stmt.c2636
-rw-r--r--libmariadb/libmariadb/secure/gnutls.c1438
-rw-r--r--libmariadb/libmariadb/secure/gnutls_crypt.c77
-rw-r--r--libmariadb/libmariadb/secure/ma_schannel.c629
-rw-r--r--libmariadb/libmariadb/secure/ma_schannel.h87
-rw-r--r--libmariadb/libmariadb/secure/openssl.c786
-rw-r--r--libmariadb/libmariadb/secure/openssl_crypt.c88
-rw-r--r--libmariadb/libmariadb/secure/schannel.c567
-rw-r--r--libmariadb/libmariadb/secure/schannel_certs.c852
-rw-r--r--libmariadb/libmariadb/secure/schannel_certs.h53
-rw-r--r--libmariadb/libmariadb/secure/win_crypt.c162
-rw-r--r--libmariadb/libmariadb/win32_errmsg.c138
-rw-r--r--libmariadb/libmariadb/win32_errmsg.h2
45 files changed, 32780 insertions, 0 deletions
diff --git a/libmariadb/libmariadb/CMakeLists.txt b/libmariadb/libmariadb/CMakeLists.txt
new file mode 100644
index 00000000..43ed67b4
--- /dev/null
+++ b/libmariadb/libmariadb/CMakeLists.txt
@@ -0,0 +1,526 @@
+INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include
+ ${CC_SOURCE_DIR}/libmariadb)
+
+ADD_DEFINITIONS(-D HAVE_COMPRESS)
+ADD_DEFINITIONS(-D LIBMARIADB)
+ADD_DEFINITIONS(-D THREAD)
+
+IF(APPLE)
+ ADD_DEFINITIONS(-D _XOPEN_SOURCE)
+ENDIF()
+
+INCLUDE(${CC_SOURCE_DIR}/cmake/sign.cmake)
+
+SET(MARIADB_LIB_SYMBOLS
+ mariadb_cancel
+ mariadb_connection
+ mariadb_convert_string
+ ma_pvio_register_callback
+ mariadb_get_charset_by_name
+ mariadb_stmt_execute_direct
+ mariadb_get_charset_by_nr
+ mariadb_get_info
+ mariadb_get_infov
+ mysql_get_timeout_value
+ mysql_get_timeout_value_ms
+ mysql_optionsv
+ mysql_ps_fetch_functions
+ mariadb_reconnect
+ mysql_stmt_warning_count
+ mariadb_stmt_fetch_fields
+ mariadb_rpl_open
+ mariadb_rpl_close
+ mariadb_rpl_fetch
+ mariadb_rpl_optionsv
+ mariadb_rpl_get_optionsv
+ mariadb_rpl_init_ex
+ mariadb_free_rpl_event
+ mariadb_field_attr
+)
+
+SET(MARIADB_LIB_3_3_5_SYMBOLS
+ mariadb_rpl_extract_rows
+ mariadb_rpl_error
+ mariadb_rpl_errno)
+IF(WITH_SSL)
+ SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} mariadb_deinitialize_ssl)
+ENDIF()
+
+SET(MYSQL_LIB_SYMBOLS
+ mysql_affected_rows
+ mysql_autocommit
+ mysql_change_user
+ mysql_character_set_name
+ mysql_client_find_plugin
+ mysql_client_register_plugin
+ mysql_close
+ mysql_commit
+ mysql_data_seek
+ mysql_debug
+ mysql_dump_debug_info
+ mysql_embedded
+ mysql_eof
+ mysql_errno
+ mysql_error
+ mysql_escape_string
+ mysql_fetch_field
+ mysql_fetch_field_direct
+ mysql_fetch_fields
+ mysql_fetch_lengths
+ mysql_fetch_row
+ mysql_field_count
+ mysql_field_seek
+ mysql_field_tell
+ mysql_free_result
+ mysql_get_character_set_info
+ mysql_get_charset_by_name
+ mysql_get_charset_by_nr
+ mysql_get_client_info
+ mysql_get_client_version
+ mysql_get_host_info
+ mysql_get_option
+ mysql_get_optionv
+ mysql_get_parameters
+ mysql_get_proto_info
+ mysql_get_server_info
+ mysql_get_server_name
+ mysql_get_server_version
+ mysql_get_socket
+ mysql_get_ssl_cipher
+ mysql_hex_string
+ mysql_info
+ mysql_init
+ mysql_insert_id
+ mysql_kill
+ mysql_list_dbs
+ mysql_list_fields
+ mysql_list_processes
+ mysql_list_tables
+ mysql_load_plugin
+ mysql_load_plugin_v
+ mysql_more_results
+ mysql_net_field_length
+ mysql_net_read_packet
+ mysql_next_result
+ mysql_num_fields
+ mysql_num_rows
+ mysql_options
+ mysql_options4
+ mysql_ping
+ mysql_query
+ mysql_read_query_result
+ mysql_real_connect
+ mysql_real_escape_string
+ mysql_real_query
+ mysql_refresh
+ mysql_reset_connection
+ mysql_rollback
+ mysql_row_seek
+ mysql_row_tell
+ mysql_select_db
+ mysql_send_query
+ mysql_server_end
+ mysql_server_init
+ mysql_session_track_get_next
+ mysql_session_track_get_first
+ mysql_set_character_set
+ mysql_set_local_infile_default
+ mysql_set_local_infile_handler
+ mysql_set_server_option
+ mysql_shutdown
+ mysql_sqlstate
+ mysql_ssl_set
+ mysql_stat
+ mysql_stmt_affected_rows
+ mysql_stmt_attr_get
+ mysql_stmt_attr_set
+ mysql_stmt_bind_param
+ mysql_stmt_bind_result
+ mysql_stmt_close
+ mysql_stmt_data_seek
+ mysql_stmt_errno
+ mysql_stmt_error
+ mysql_stmt_execute
+ mysql_stmt_fetch
+ mysql_stmt_fetch_column
+ mysql_stmt_field_count
+ mysql_stmt_free_result
+ mysql_stmt_init
+ mysql_stmt_insert_id
+ mysql_stmt_more_results
+ mysql_stmt_next_result
+ mysql_stmt_num_rows
+ mysql_stmt_param_count
+ mysql_stmt_param_metadata
+ mysql_stmt_prepare
+ mysql_stmt_reset
+ mysql_stmt_result_metadata
+ mysql_stmt_row_seek
+ mysql_stmt_row_tell
+ mysql_stmt_send_long_data
+ mysql_stmt_sqlstate
+ mysql_stmt_store_result
+ mysql_store_result
+ mysql_thread_end
+ mysql_thread_id
+ mysql_thread_init
+ mysql_thread_safe
+ mysql_use_result
+ mysql_warning_count)
+
+# some gcc versions fail to compile asm parts of my_context.c,
+# if build type is "Release" (see CONC-133), so we need to add -g flag
+IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_BUILD_TYPE MATCHES "Release")
+ SET_SOURCE_FILES_PROPERTIES(my_context.c PROPERTIES COMPILE_FLAGS -g)
+ENDIF()
+
+IF(ZLIB_FOUND AND WITH_EXTERNAL_ZLIB)
+ INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
+ELSE()
+ SET(ZLIB_SOURCES
+ ${CC_SOURCE_DIR}/external/zlib/adler32.c
+ ${CC_SOURCE_DIR}/external/zlib/compress.c
+ ${CC_SOURCE_DIR}/external/zlib/crc32.c
+ ${CC_SOURCE_DIR}/external/zlib/deflate.c
+ ${CC_SOURCE_DIR}/external/zlib/gzclose.c
+ ${CC_SOURCE_DIR}/external/zlib/gzlib.c
+ ${CC_SOURCE_DIR}/external/zlib/gzread.c
+ ${CC_SOURCE_DIR}/external/zlib/gzwrite.c
+ ${CC_SOURCE_DIR}/external/zlib/infback.c
+ ${CC_SOURCE_DIR}/external/zlib/inffast.c
+ ${CC_SOURCE_DIR}/external/zlib/inflate.c
+ ${CC_SOURCE_DIR}/external/zlib/inftrees.c
+ ${CC_SOURCE_DIR}/external/zlib/trees.c
+ ${CC_SOURCE_DIR}/external/zlib/uncompr.c
+ ${CC_SOURCE_DIR}/external/zlib/zutil.c
+ )
+ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/external/zlib ${CC_BINARY_DIR}/external/zlib)
+ENDIF()
+
+
+SET(MARIADB_DYNCOL_SYMBOLS
+ mariadb_dyncol_check
+ mariadb_dyncol_column_cmp_named
+ mariadb_dyncol_column_count
+ mariadb_dyncol_create_many_named
+ mariadb_dyncol_create_many_num
+ mariadb_dyncol_exists_named
+ mariadb_dyncol_exists_num
+ mariadb_dyncol_free
+ mariadb_dyncol_get_named
+ mariadb_dyncol_get_num
+ mariadb_dyncol_has_names
+ mariadb_dyncol_json
+ mariadb_dyncol_list_named
+ mariadb_dyncol_list_num
+ mariadb_dyncol_unpack
+ mariadb_dyncol_update_many_named
+ mariadb_dyncol_update_many_num
+ mariadb_dyncol_val_double
+ mariadb_dyncol_val_long
+ mariadb_dyncol_val_str)
+
+SET(MARIADB_NONBLOCK_SYMBOLS
+ mysql_autocommit_cont
+ mysql_autocommit_start
+ mysql_change_user_cont
+ mysql_change_user_start
+ mysql_close_cont
+ mysql_close_start
+ mysql_commit_cont
+ mysql_commit_start
+ mysql_dump_debug_info_cont
+ mysql_dump_debug_info_start
+ mysql_fetch_row_cont
+ mysql_fetch_row_start
+ mysql_free_result_cont
+ mysql_free_result_start
+ mysql_kill_cont
+ mysql_kill_start
+ mysql_list_fields_cont
+ mysql_list_fields_start
+ mysql_next_result_cont
+ mysql_next_result_start
+ mysql_ping_cont
+ mysql_ping_start
+ mysql_reset_connection_start
+ mysql_reset_connection_cont
+ mysql_query_cont
+ mysql_query_start
+ mysql_read_query_result_cont
+ mysql_read_query_result_start
+ mysql_real_connect_cont
+ mysql_real_connect_start
+ mysql_real_query_cont
+ mysql_real_query_start
+ mysql_refresh_cont
+ mysql_refresh_start
+ mysql_rollback_cont
+ mysql_rollback_start
+ mysql_select_db_cont
+ mysql_select_db_start
+ mysql_send_query_cont
+ mysql_send_query_start
+ mysql_set_character_set_cont
+ mysql_set_character_set_start
+ mysql_set_server_option_cont
+ mysql_set_server_option_start
+ mysql_shutdown_cont
+ mysql_shutdown_start
+ mysql_stat_cont
+ mysql_stat_start
+ mysql_stmt_close_cont
+ mysql_stmt_close_start
+ mysql_stmt_execute_cont
+ mysql_stmt_execute_start
+ mysql_stmt_fetch_cont
+ mysql_stmt_fetch_start
+ mysql_stmt_free_result_cont
+ mysql_stmt_free_result_start
+ mysql_stmt_next_result_cont
+ mysql_stmt_next_result_start
+ mysql_stmt_prepare_cont
+ mysql_stmt_prepare_start
+ mysql_stmt_reset_cont
+ mysql_stmt_reset_start
+ mysql_stmt_send_long_data_cont
+ mysql_stmt_send_long_data_start
+ mysql_stmt_store_result_cont
+ mysql_stmt_store_result_start
+ mysql_store_result_cont
+ mysql_store_result_start
+)
+
+# handle static plugins
+SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${LIBMARIADB_PLUGIN_SOURCES})
+SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${LIBMARIADB_PLUGIN_LIBS} ${ZSTD_LIBRARY} ${ZLIB_LIBRARY})
+MESSAGE(STATUS "SYSTEM_LIBS: ${SYSTEM_LIBS}")
+INCLUDE_DIRECTORIES(${LIBMARIADB_PLUGIN_INCLUDES})
+ADD_DEFINITIONS(${LIBMARIADB_PLUGIN_DEFS})
+FOREACH(plugin ${PLUGINS_STATIC})
+ SET(EXTERNAL_PLUGINS "${EXTERNAL_PLUGINS} extern struct st_mysql_client_plugin ${plugin}_client_plugin;\n")
+ SET(BUILTIN_PLUGINS "${BUILTIN_PLUGINS} (struct st_mysql_client_plugin *)&${plugin}_client_plugin,\n")
+ENDFOREACH()
+CONFIGURE_FILE(${CC_SOURCE_DIR}/libmariadb/ma_client_plugin.c.in
+ ${CC_BINARY_DIR}/libmariadb/ma_client_plugin.c)
+
+SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES}
+${CC_SOURCE_DIR}/plugins/auth/my_auth.c
+ma_array.c
+ma_charset.c
+ma_decimal.c
+ma_hashtbl.c
+ma_net.c
+mariadb_charset.c
+ma_time.c
+ma_default.c
+ma_errmsg.c
+mariadb_lib.c
+ma_list.c
+ma_pvio.c
+ma_tls.c
+ma_alloc.c
+ma_compress.c
+ma_init.c
+ma_password.c
+ma_ll2str.c
+ma_sha1.c
+mariadb_stmt.c
+ma_loaddata.c
+ma_stmt_codec.c
+ma_string.c
+ma_dtoa.c
+mariadb_rpl.c
+${CC_BINARY_DIR}/libmariadb/ma_client_plugin.c
+ma_io.c
+${SSL_SOURCES}
+)
+
+IF(WIN32)
+ ADD_DEFINITIONS(-DSIZEOF_CHARP=${CMAKE_SIZEOF_VOID_P})
+ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/win-iconv)
+ SET(LIBMARIADB_SOURCES
+ ${LIBMARIADB_SOURCES}
+ ${CC_SOURCE_DIR}/win-iconv/win_iconv.c
+ win32_errmsg.c
+ win32_errmsg.h)
+ELSE()
+ IF(ICONV_INCLUDE_DIR)
+ INCLUDE_DIRECTORIES(BEFORE ${ICONV_INCLUDE_DIR})
+ ENDIF()
+ IF(NOT CMAKE_SYSTEM_NAME MATCHES AIX)
+ ADD_DEFINITIONS(-DLIBICONV_PLUG)
+ ENDIF()
+ IF(APPLE)
+ ADD_DEFINITIONS(-Wno-deprecated-declarations)
+ ENDIF()
+ENDIF()
+
+IF(ZLIB_FOUND AND WITH_EXTERNAL_ZLIB)
+ INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
+ELSE()
+ SET(ZLIB_SOURCES
+ ${CC_SOURCE_DIR}/external/zlib/adler32.c
+ ${CC_SOURCE_DIR}/external/zlib/compress.c
+ ${CC_SOURCE_DIR}/external/zlib/crc32.c
+ ${CC_SOURCE_DIR}/external/zlib/deflate.c
+ ${CC_SOURCE_DIR}/external/zlib/gzclose.c
+ ${CC_SOURCE_DIR}/external/zlib/gzlib.c
+ ${CC_SOURCE_DIR}/external/zlib/gzread.c
+ ${CC_SOURCE_DIR}/external/zlib/gzwrite.c
+ ${CC_SOURCE_DIR}/external/zlib/infback.c
+ ${CC_SOURCE_DIR}/external/zlib/inffast.c
+ ${CC_SOURCE_DIR}/external/zlib/inflate.c
+ ${CC_SOURCE_DIR}/external/zlib/inftrees.c
+ ${CC_SOURCE_DIR}/external/zlib/trees.c
+ ${CC_SOURCE_DIR}/external/zlib/uncompr.c
+ ${CC_SOURCE_DIR}/external/zlib/zutil.c
+ )
+ IF(MSVC)
+ SET_SOURCE_FILES_PROPERTIES(${ZLIB_SOURCES} PROPERTIES COMPILE_FLAGS -W0)
+ ENDIF()
+ SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${ZLIB_SOURCES})
+ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/external/zlib)
+ENDIF()
+
+IF(WITH_DYNCOL)
+ MESSAGE1(WITH_DYNCOL "Dynamic column API support: ON")
+ SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_DYNCOL_SYMBOLS})
+ SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_dyncol.c)
+ENDIF()
+
+SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_async.c ma_context.c)
+SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_NONBLOCK_SYMBOLS})
+
+INCLUDE(${CC_SOURCE_DIR}/cmake/export.cmake)
+IF(NOT WIN32)
+ CREATE_EXPORT_FILE(WRITE mariadbclient.def
+ "libmysqlclient_18"
+ "${MYSQL_LIB_SYMBOLS}"
+ "libmariadbclient_18")
+ CREATE_EXPORT_FILE(APPEND mariadbclient.def
+ "libmariadb_3"
+ "${MARIADB_LIB_SYMBOLS}"
+ "")
+ CREATE_EXPORT_FILE(APPEND mariadbclient.def
+ "libmariadb_3_3_5"
+ "${MARIADB_LIB_3_3_5_SYMBOLS}"
+ "")
+ELSE()
+ CREATE_EXPORT_FILE(WRITE mariadbclient.def
+ "libmariadb_3"
+ "${MARIADB_LIB_SYMBOLS};${MYSQL_LIB_SYMBOLS};${MARIADB_LIB_3_3_5_SYMBOLS}"
+ "")
+ENDIF()
+
+
+IF(CMAKE_VERSION VERSION_GREATER 2.8.7)
+ # CREATE OBJECT LIBRARY
+ ADD_LIBRARY(mariadb_obj OBJECT ${LIBMARIADB_SOURCES})
+ IF(UNIX)
+ SET_TARGET_PROPERTIES(mariadb_obj PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
+ ENDIF()
+ SET (MARIADB_OBJECTS $<TARGET_OBJECTS:mariadb_obj>)
+ELSE()
+ SET (MARIADB_OBJECTS ${LIBMARIADB_SOURCES})
+ENDIF()
+
+# Xcode doesn't support targets that have only object files,
+# so let's add an empty file to keep Xcode happy
+IF(CMAKE_GENERATOR MATCHES Xcode)
+ FILE(WRITE ${CC_BINARY_DIR}/libmariadb/empty.c "")
+ SET(EMPTY_FILE ${CC_BINARY_DIR}/libmariadb/empty.c)
+ENDIF()
+
+#* create file with list of functions */
+FILE(WRITE ${CC_BINARY_DIR}/manpages.list "${MARIADB_LIB_SYMBOLS};${MYSQL_LIB_SYMBOLS}")
+
+IF(WIN32)
+ SET_VERSION_INFO("TARGET:libmariadb"
+ "FILE_TYPE:VFT_DLL"
+ "SOURCE_FILE:libmariadb/libmariadb.c"
+ "ORIGINAL_FILE_NAME:libmariadb.dll"
+ "FILE_DESCRIPTION:Dynamic lib for client/server communication")
+ENDIF()
+
+ADD_LIBRARY(mariadbclient STATIC ${MARIADB_OBJECTS} ${EMPTY_FILE})
+TARGET_LINK_LIBRARIES(mariadbclient ${SYSTEM_LIBS} ${CRYPTO_LIBS})
+
+IF(UNIX)
+ ADD_LIBRARY(libmariadb SHARED ${libmariadb_RC} ${MARIADB_OBJECTS} ${EMPTY_FILE})
+ SET_TARGET_PROPERTIES(libmariadb PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
+ELSE()
+ ADD_LIBRARY(libmariadb SHARED ${libmariadb_RC} ${MARIADB_OBJECTS} mariadbclient.def)
+ SET_TARGET_PROPERTIES(libmariadb PROPERTIES LINKER_LANGUAGE C)
+ENDIF()
+
+TARGET_LINK_LIBRARIES(libmariadb LINK_PRIVATE ${SYSTEM_LIBS} ${CRYPTO_LIBS})
+
+SIGN_TARGET(libmariadb)
+
+IF(CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+ CMAKE_SYSTEM_NAME MATCHES "kFreeBSD" OR
+ CMAKE_SYSTEM_NAME MATCHES "GNU")
+ IF (NOT WITH_ASAN AND NOT WITH_TSAN AND NOT WITH_UBSAN AND NOT WITH_MSAN)
+ TARGET_LINK_LIBRARIES (libmariadb LINK_PRIVATE "-Wl,--no-undefined")
+ ENDIF()
+ SET_TARGET_PROPERTIES(libmariadb PROPERTIES LINK_FLAGS "${CC_BINARY_DIR}/libmariadb/mariadbclient.def")
+ENDIF()
+
+SET_TARGET_PROPERTIES(mariadbclient PROPERTIES IMPORTED_INTERFACE_LINK_LIBRARIES "${SYSTEM_LIBS}")
+SET_TARGET_PROPERTIES(libmariadb PROPERTIES IMPORTED_INTERFACE_LINK_LIBRARIES "${SYSTEM_LIBS}")
+
+SET_TARGET_PROPERTIES(libmariadb PROPERTIES PREFIX "")
+
+#
+# Installation
+#
+INCLUDE(${CC_SOURCE_DIR}/cmake/symlink.cmake)
+
+# There are still several projects which don't make use
+# of the config program. To make sure these programs can
+# use mariadb client library we provide libmysql symlinks
+
+IF(WITH_MYSQLCOMPAT)
+ create_symlink(libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_LIBDIR})
+ create_symlink(libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_LIBDIR})
+ IF(NOT CMAKE_SYSTEM_NAME MATCHES AIX)
+ create_symlink(libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_LIBDIR})
+ create_symlink(libmysqlclient_r${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_LIBDIR})
+ ENDIF()
+ENDIF()
+
+create_symlink(libmariadb${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_LIBDIR})
+
+SET_TARGET_PROPERTIES(libmariadb PROPERTIES VERSION
+ ${CPACK_PACKAGE_VERSION_MAJOR}
+ SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
+
+IF(NOT WIN32)
+ SET_TARGET_PROPERTIES(mariadbclient PROPERTIES OUTPUT_NAME "${LIBMARIADB_STATIC_NAME}")
+ENDIF()
+
+INSTALL(TARGETS mariadbclient
+ COMPONENT Development
+ DESTINATION ${INSTALL_LIBDIR})
+IF(WIN32)
+INSTALL(TARGETS libmariadb
+ COMPONENT SharedLibraries
+ DESTINATION ${INSTALL_LIBDIR})
+ELSE()
+# in cmake 3.12+ we can use
+#INSTALL(TARGETS libmariadb LIBRARY DESTINATION ${INSTALL_LIBDIR}
+# COMPONENT SharedLibraries NAMELINK_COMPONENT Development)
+# but as long as we build on CentOS 7 with its cmake 2.8.12.2 we have to use
+INSTALL(TARGETS libmariadb LIBRARY DESTINATION ${INSTALL_LIBDIR}
+ COMPONENT SharedLibraries NAMELINK_SKIP)
+INSTALL(TARGETS libmariadb LIBRARY DESTINATION ${INSTALL_LIBDIR}
+ COMPONENT Development NAMELINK_ONLY)
+ENDIF()
+
+IF(MSVC)
+ # On Windows, install PDB
+ INSTALL(FILES $<TARGET_PDB_FILE:libmariadb> DESTINATION "${INSTALL_LIBDIR}"
+ CONFIGURATIONS Debug RelWithDebInfo
+ COMPONENT Development)
+ENDIF()
diff --git a/libmariadb/libmariadb/bmove_upp.c b/libmariadb/libmariadb/bmove_upp.c
new file mode 100644
index 00000000..599b38be
--- /dev/null
+++ b/libmariadb/libmariadb/bmove_upp.c
@@ -0,0 +1,33 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* File : bmove.c
+ Author : Michael widenius
+ Updated: 1987-03-20
+ Defines: bmove_upp()
+
+ bmove_upp(dst, src, len) moves exactly "len" bytes from the source
+ "src-len" to the destination "dst-len" counting downwards.
+*/
+
+#include <ma_global.h>
+#include "ma_string.h"
+
+void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
+{
+ while (len-- != 0) *--dst = *--src;
+}
diff --git a/libmariadb/libmariadb/get_password.c b/libmariadb/libmariadb/get_password.c
new file mode 100644
index 00000000..4a93a738
--- /dev/null
+++ b/libmariadb/libmariadb/get_password.c
@@ -0,0 +1,172 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*************************************************************************************/
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "mysql.h"
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include <stdio.h>
+#include <memory.h>
+
+#ifndef _WIN32
+#include <termios.h>
+#else
+#include <conio.h>
+#endif /* _WIN32 */
+
+/* {{{ static char *get_password() */
+/*
+ read password from device
+
+ SYNOPSIS
+ get_password
+ Hdl/file file handle
+ buffer input buffer
+ length buffer length
+
+ RETURN
+ buffer zero terminated input buffer
+*/
+#ifdef _WIN32
+static char *get_password(HANDLE Hdl, char *buffer, DWORD length)
+#else
+static char *get_password(FILE *file, char *buffer, int length)
+#endif
+{
+ char inChar;
+ int CharsProcessed= 1;
+#ifdef _WIN32
+ DWORD Offset= 0;
+#else
+ int Offset= 0;
+#endif
+ memset(buffer, 0, length);
+
+ do
+ {
+#ifdef _WIN32
+ if (!ReadConsole(Hdl, &inChar, 1, (DWORD *)&CharsProcessed, NULL) ||
+ !CharsProcessed)
+ break;
+#else
+ inChar= (char)fgetc(file);
+#endif
+
+ switch(inChar) {
+ case '\b': /* backslash */
+ if (Offset)
+ {
+ /* cursor is always at the end */
+ Offset--;
+ buffer[Offset]= 0;
+#ifdef _WIN32
+ _cputs("\b \b");
+#endif
+ }
+ break;
+ case '\n':
+ case '\r':
+ break;
+ default:
+ buffer[Offset]= inChar;
+ if (Offset < length - 2)
+ Offset++;
+#ifdef _WIN32
+ _cputs("*");
+#endif
+ break;
+ }
+ } while (CharsProcessed && inChar != '\n' && inChar != '\r');
+ return buffer;
+}
+/* }}} */
+
+/* {{{ static char* get_tty_password */
+/*
+ reads password from tty/console
+
+ SYNOPSIS
+ get_tty_password()
+ buffer input buffer
+ length length of input buffer
+
+ DESCRIPTION
+ reads a password from console (Windows) or tty without echoing
+ it's characters. Input buffer must be allocated by calling function.
+
+ RETURNS
+ buffer pointer to input buffer
+*/
+char* get_tty_password(char *prompt, char *buffer, int length)
+{
+#ifdef _WIN32
+ DWORD SaveState;
+ HANDLE Hdl;
+
+ if (prompt)
+ fprintf(stderr, "%s", prompt);
+
+ if (!(Hdl= CreateFile("CONIN$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING, 0, NULL)))
+ {
+ /* todo: provide a graphical dialog */
+ return buffer;
+ }
+ /* Save ConsoleMode and set ENABLE_PROCESSED_INPUT:
+ CTRL+C is processed by the system and is not placed in the input buffer */
+ GetConsoleMode(Hdl, &SaveState);
+ SetConsoleMode(Hdl, ENABLE_PROCESSED_INPUT);
+
+ buffer= get_password(Hdl, buffer, length);
+ SetConsoleMode(Hdl, SaveState);
+ CloseHandle(Hdl);
+ return buffer;
+#else
+ struct termios term_old,
+ term_new;
+ FILE *readfrom;
+
+ if (prompt && isatty(fileno(stderr)))
+ fputs(prompt, stderr);
+
+ if (!(readfrom= fopen("/dev/tty", "r")))
+ readfrom= stdin;
+
+ /* try to disable echo */
+ tcgetattr(fileno(readfrom), &term_old);
+ term_new= term_old;
+ term_new.c_cc[VMIN] = 1;
+ term_new.c_cc[VTIME]= 0;
+ term_new.c_lflag&= ~(ECHO | ISIG | ICANON | ECHONL);
+ tcsetattr(fileno(readfrom), TCSADRAIN, &term_new);
+
+ buffer= get_password(readfrom, buffer, length);
+
+ if (isatty(fileno(readfrom)))
+ tcsetattr(fileno(readfrom), TCSADRAIN, &term_old);
+
+ fclose(readfrom);
+
+ return buffer;
+#endif
+}
+/* }}} */
diff --git a/libmariadb/libmariadb/ma_alloc.c b/libmariadb/libmariadb/ma_alloc.c
new file mode 100644
index 00000000..d6db279e
--- /dev/null
+++ b/libmariadb/libmariadb/ma_alloc.c
@@ -0,0 +1,196 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Routines to handle mallocing of results which will be freed the same time */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+
+#define INIT_BLOCK_NUM 4
+void ma_init_alloc_root(MA_MEM_ROOT *mem_root, size_t block_size, size_t pre_alloc_size)
+{
+ mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
+ mem_root->min_malloc=32;
+ mem_root->block_size= (block_size-MALLOC_OVERHEAD-sizeof(MA_USED_MEM)+8);
+ mem_root->error_handler=0;
+ mem_root->block_num= INIT_BLOCK_NUM;
+ mem_root->first_block_usage= 0;
+#if !(defined(HAVE_purify) && defined(EXTRA_DEBUG))
+ if (pre_alloc_size)
+ {
+ if ((mem_root->free = mem_root->pre_alloc=
+ (MA_USED_MEM*) malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(MA_USED_MEM)))))
+ {
+ mem_root->free->size=pre_alloc_size+ALIGN_SIZE(sizeof(MA_USED_MEM));
+ mem_root->free->left=pre_alloc_size;
+ mem_root->free->next=0;
+ }
+ }
+#endif
+}
+
+void * ma_alloc_root(MA_MEM_ROOT *mem_root, size_t Size)
+{
+#if defined(HAVE_purify) && defined(EXTRA_DEBUG)
+ reg1 MA_USED_MEM *next;
+ Size+=ALIGN_SIZE(sizeof(MA_USED_MEM));
+
+ if (!(next = (MA_USED_MEM*) malloc(Size)))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ return((void *) 0); /* purecov: inspected */
+ }
+ next->next=mem_root->used;
+ mem_root->used=next;
+ return (void *) (((char*) next)+ALIGN_SIZE(sizeof(MA_USED_MEM)));
+#else
+ size_t get_size;
+ void * point;
+ reg1 MA_USED_MEM *next= 0;
+ reg2 MA_USED_MEM **prev;
+
+ Size= ALIGN_SIZE(Size);
+
+ if ((*(prev= &mem_root->free)))
+ {
+ if ((*prev)->left < Size &&
+ mem_root->first_block_usage++ >= 16 &&
+ (*prev)->left < 4096)
+ {
+ next= *prev;
+ *prev= next->next;
+ next->next= mem_root->used;
+ mem_root->used= next;
+ mem_root->first_block_usage= 0;
+ }
+ for (next= *prev; next && next->left < Size; next= next->next)
+ prev= &next->next;
+ }
+
+ if (! next)
+ { /* Time to alloc new block */
+ get_size= MAX(Size+ALIGN_SIZE(sizeof(MA_USED_MEM)),
+ (mem_root->block_size & ~1) * (mem_root->block_num >> 2));
+
+ if (!(next = (MA_USED_MEM*) malloc(get_size)))
+ {
+ if (mem_root->error_handler)
+ (*mem_root->error_handler)();
+ return((void *) 0); /* purecov: inspected */
+ }
+ mem_root->block_num++;
+ next->next= *prev;
+ next->size= get_size;
+ next->left= get_size-ALIGN_SIZE(sizeof(MA_USED_MEM));
+ *prev=next;
+ }
+ point= (void *) ((char*) next+ (next->size-next->left));
+ if ((next->left-= Size) < mem_root->min_malloc)
+ { /* Full block */
+ *prev=next->next; /* Remove block from list */
+ next->next=mem_root->used;
+ mem_root->used=next;
+ mem_root->first_block_usage= 0;
+ }
+ return(point);
+#endif
+}
+
+ /* deallocate everything used by alloc_root */
+
+void ma_free_root(MA_MEM_ROOT *root, myf MyFlags)
+{
+ reg1 MA_USED_MEM *next,*old;
+
+ if (!root)
+ return; /* purecov: inspected */
+ if (!(MyFlags & MY_KEEP_PREALLOC))
+ root->pre_alloc=0;
+
+ for ( next=root->used; next ;)
+ {
+ old=next; next= next->next ;
+ if (old != root->pre_alloc)
+ free(old);
+ }
+ for (next= root->free ; next ; )
+ {
+ old=next; next= next->next ;
+ if (old != root->pre_alloc)
+ free(old);
+ }
+ root->used=root->free=0;
+ if (root->pre_alloc)
+ {
+ root->free=root->pre_alloc;
+ root->free->left=root->pre_alloc->size-ALIGN_SIZE(sizeof(MA_USED_MEM));
+ root->free->next=0;
+ }
+ root->block_num= INIT_BLOCK_NUM;
+ root->first_block_usage= 0;
+}
+
+
+char *ma_strdup_root(MA_MEM_ROOT *root,const char *str)
+{
+ size_t len= strlen(str)+1;
+ char *pos;
+ if ((pos=ma_alloc_root(root,len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+
+char *ma_memdup_root(MA_MEM_ROOT *root, const char *str, size_t len)
+{
+ char *pos;
+ if ((pos= ma_alloc_root(root,len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+void *ma_multi_malloc(myf myFlags, ...)
+{
+ va_list args;
+ char **ptr,*start,*res;
+ size_t tot_length,length;
+
+ va_start(args,myFlags);
+ tot_length=0;
+ while ((ptr=va_arg(args, char **)))
+ {
+ length=va_arg(args, size_t);
+ tot_length+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+
+ if (!(start=(char *)malloc(tot_length)))
+ return 0;
+
+ va_start(args,myFlags);
+ res=start;
+ while ((ptr=va_arg(args, char **)))
+ {
+ *ptr=res;
+ length=va_arg(args,size_t);
+ res+=ALIGN_SIZE(length);
+ }
+ va_end(args);
+ return start;
+}
diff --git a/libmariadb/libmariadb/ma_array.c b/libmariadb/libmariadb/ma_array.c
new file mode 100644
index 00000000..d067aa4a
--- /dev/null
+++ b/libmariadb/libmariadb/ma_array.c
@@ -0,0 +1,172 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Handling of arrays that can grow dynamically. */
+
+#undef SAFEMALLOC /* Problems with threads */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "ma_string.h"
+#include <memory.h>
+
+/*
+ Initiate array and alloc space for init_alloc elements. Array is usable
+ even if space allocation failed
+*/
+
+my_bool ma_init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size,
+ uint init_alloc, uint alloc_increment CALLER_INFO_PROTO)
+{
+ 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 ma_insert_dynamic(DYNAMIC_ARRAY *array, void *element)
+{
+ void *buffer;
+ if (array->elements == array->max_element)
+ { /* Call only when necessary */
+ if (!(buffer=ma_alloc_dynamic(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 *ma_alloc_dynamic(DYNAMIC_ARRAY *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 *ma_pop_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->elements)
+ return (unsigned char *)array->buffer+(--array->elements * array->size_of_element);
+ return 0;
+}
+
+
+my_bool ma_set_dynamic(DYNAMIC_ARRAY *array, void * element, uint idx)
+{
+ if (idx >= array->elements)
+ {
+ if (idx >= array->max_element)
+ {
+ uint 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 ma_get_dynamic(DYNAMIC_ARRAY *array, void * element, uint 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 ma_delete_dynamic(DYNAMIC_ARRAY *array)
+{
+ if (array->buffer)
+ {
+ free(array->buffer);
+ array->buffer=0;
+ array->elements=array->max_element=0;
+ }
+}
+
+
+void ma_delete_dynamic_element(DYNAMIC_ARRAY *array, uint 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 ma_freeze_size(DYNAMIC_ARRAY *array)
+{
+ uint 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;
+ }
+}
diff --git a/libmariadb/libmariadb/ma_charset.c b/libmariadb/libmariadb/ma_charset.c
new file mode 100644
index 00000000..f6ed6f80
--- /dev/null
+++ b/libmariadb/libmariadb/ma_charset.c
@@ -0,0 +1,1603 @@
+/****************************************************************************
+ Copyright (C) 2012, 2022, 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 <http://www.gnu.org/licenses>
+ 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
+*****************************************************************************/
+
+/* The implementation for character set support was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef _WIN32
+#include <strings.h>
+#include <string.h>
+#else
+#include <string.h>
+#endif
+#include <ma_global.h>
+#include <mariadb_ctype.h>
+#include <ma_string.h>
+
+#ifdef HAVE_ICONV
+#ifdef _WIN32
+#include "../win-iconv/iconv.h"
+#else
+#include <iconv.h>
+#endif
+#endif
+
+
+#if defined(HAVE_NL_LANGINFO) && defined(HAVE_SETLOCALE)
+#include <locale.h>
+#include <langinfo.h>
+#endif
+
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* {{{ utf8 functions */
+static unsigned int check_mb_utf8mb3_sequence(const char *start, const char *end)
+{
+ uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 && ((uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int check_mb_utf8_sequence(const char *start, const char *end)
+{
+ uchar c;
+
+ if (start >= end) {
+ return 0;
+ }
+
+ c = (uchar) start[0];
+
+ if (c < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (c < 0xC2) {
+ return 0; /* invalid mb character */
+ }
+ if (c < 0xE0) {
+ if (start + 2 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40)) {
+ return 0;
+ }
+ return 2;
+ }
+ if (c < 0xF0) {
+ if (start + 3 > end) {
+ return 0; /* too small */
+ }
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 && ((uchar)start[2] ^ 0x80) < 0x40 &&
+ (c >= 0xE1 || (uchar)start[1] >= 0xA0))) {
+ return 0; /* invalid utf8 character */
+ }
+ return 3;
+ }
+ if (c < 0xF5) {
+ if (start + 4 > end) { /* We need 4 characters */
+ return 0; /* too small */
+ }
+
+ /*
+ UTF-8 quick four-byte mask:
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ Encoding allows to encode U+00010000..U+001FFFFF
+
+ The maximum character defined in the Unicode standard is U+0010FFFF.
+ Higher characters U+00110000..U+001FFFFF are not used.
+
+ 11110000.10010000.10xxxxxx.10xxxxxx == F0.90.80.80 == U+00010000 (min)
+ 11110100.10001111.10111111.10111111 == F4.8F.BF.BF == U+0010FFFF (max)
+
+ Valid codes:
+ [F0][90..BF][80..BF][80..BF]
+ [F1][80..BF][80..BF][80..BF]
+ [F2][80..BF][80..BF][80..BF]
+ [F3][80..BF][80..BF][80..BF]
+ [F4][80..8F][80..BF][80..BF]
+ */
+
+ if (!(((uchar)start[1] ^ 0x80) < 0x40 &&
+ ((uchar)start[2] ^ 0x80) < 0x40 &&
+ ((uchar)start[3] ^ 0x80) < 0x40 &&
+ (c >= 0xf1 || (uchar)start[1] >= 0x90) &&
+ (c <= 0xf3 || (uchar)start[1] <= 0x8F)))
+ {
+ return 0; /* invalid utf8 character */
+ }
+ return 4;
+ }
+ return 0;
+}
+
+static unsigned int check_mb_utf8mb3_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8mb3_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+static unsigned int check_mb_utf8_valid(const char *start, const char *end)
+{
+ unsigned int len = check_mb_utf8_sequence(start, end);
+ return (len > 1)? len:0;
+}
+
+
+static unsigned int mysql_mbcharlen_utf8mb3(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_utf8(unsigned int utf8)
+{
+ if (utf8 < 0x80) {
+ return 1; /* single byte character */
+ }
+ if (utf8 < 0xC2) {
+ return 0; /* invalid multibyte header */
+ }
+ if (utf8 < 0xE0) {
+ return 2; /* double byte character */
+ }
+ if (utf8 < 0xF0) {
+ return 3; /* triple byte character */
+ }
+ if (utf8 < 0xF8) {
+ return 4; /* four byte character */
+ }
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ big5 functions */
+#define valid_big5head(c) (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xF9)
+#define valid_big5tail(c) ((0x40 <= (unsigned int)(c) && (unsigned int)(c) <= 0x7E) || \
+ (0xA1 <= (unsigned int)(c) && (unsigned int)(c) <= 0xFE))
+
+#define isbig5code(c,d) (isbig5head(c) && isbig5tail(d))
+
+static unsigned int check_mb_big5(const char *start, const char *end)
+{
+ return (valid_big5head(*((const uchar*) start)) && (end - start) > 1 && valid_big5tail(*((const uchar*) start + 1)) ? 2 : 0);
+}
+
+
+static unsigned int mysql_mbcharlen_big5(unsigned int big5)
+{
+ return (valid_big5head(big5)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ cp932 functions */
+#define valid_cp932head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && c <= 0xFC))
+#define valid_cp932tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && c <= 0xFC))
+
+
+static unsigned int check_mb_cp932(const char *start, const char *end)
+{
+ return (valid_cp932head((uchar)start[0]) && (end - start > 1) &&
+ valid_cp932tail((uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_cp932(unsigned int cp932)
+{
+ return (valid_cp932head((uchar)cp932)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ euckr functions */
+#define valid_euckr(c) ((0xA1 <= (uchar)(c) && (uchar)(c) <= 0xFE))
+
+static unsigned int check_mb_euckr(const char *start, const char *end)
+{
+ if (end - start <= 1) {
+ return 0; /* invalid length */
+ }
+ if (*(uchar *)start < 0x80) {
+ return 0; /* invalid euckr character */
+ }
+ if (valid_euckr(start[1])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_euckr(unsigned int kr)
+{
+ return (valid_euckr(kr)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ eucjpms functions */
+#define valid_eucjpms(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xFE)
+#define valid_eucjpms_kata(c) (((c) & 0xFF) >= 0xA1 && ((c) & 0xFF) <= 0xDF)
+#define valid_eucjpms_ss2(c) (((c) & 0xFF) == 0x8E)
+#define valid_eucjpms_ss3(c) (((c) & 0xFF) == 0x8F)
+
+static unsigned int check_mb_eucjpms(const char *start, const char *end)
+{
+ if (*((uchar *)start) < 0x80) {
+ return 0; /* invalid eucjpms character */
+ }
+ if (valid_eucjpms(start[0]) && (end - start) > 1 && valid_eucjpms(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss2(start[0]) && (end - start) > 1 && valid_eucjpms_kata(start[1])) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) &&
+ valid_eucjpms(start[2])) {
+ return 2;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_eucjpms(unsigned int jpms)
+{
+ if (valid_eucjpms(jpms) || valid_eucjpms_ss2(jpms)) {
+ return 2;
+ }
+ if (valid_eucjpms_ss3(jpms)) {
+ return 3;
+ }
+ return 1;
+}
+/* }}} */
+
+
+/* {{{ gb2312 functions */
+#define valid_gb2312_head(c) (0xA1 <= (uchar)(c) && (uchar)(c) <= 0xF7)
+#define valid_gb2312_tail(c) (0xA1 <= (uchar)(c) && (uchar)(c) <= 0xFE)
+
+
+static unsigned int check_mb_gb2312(const char *start, const char *end)
+{
+ return (valid_gb2312_head((unsigned int)start[0]) && end - start > 1 &&
+ valid_gb2312_tail((unsigned int)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_gb2312(unsigned int gb)
+{
+ return (valid_gb2312_head(gb)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ gbk functions */
+#define valid_gbk_head(c) (0x81<=(uchar)(c) && (uchar)(c)<=0xFE)
+#define valid_gbk_tail(c) ((0x40<=(uchar)(c) && (uchar)(c)<=0x7E) || (0x80<=(uchar)(c) && (uchar)(c)<=0xFE))
+
+static unsigned int check_mb_gbk(const char *start, const char *end)
+{
+ return (valid_gbk_head(start[0]) && (end) - (start) > 1 && valid_gbk_tail(start[1])) ? 2 : 0;
+}
+
+static unsigned int mysql_mbcharlen_gbk(unsigned int gbk)
+{
+ return (valid_gbk_head(gbk) ? 2 : 1);
+}
+/* }}} */
+
+
+/* {{{ sjis functions */
+#define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && (c) <= 0xFC))
+#define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && (c) <= 0xFC))
+
+
+static unsigned int check_mb_sjis(const char *start, const char *end)
+{
+ return (valid_sjis_head((uchar)start[0]) && (end - start) > 1 && valid_sjis_tail((uchar)start[1])) ? 2 : 0;
+}
+
+
+static unsigned int mysql_mbcharlen_sjis(unsigned int sjis)
+{
+ return (valid_sjis_head((uchar)sjis)) ? 2 : 1;
+}
+/* }}} */
+
+
+/* {{{ ucs2 functions */
+static unsigned int check_mb_ucs2(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+
+static unsigned int mysql_mbcharlen_ucs2(unsigned int ucs2 __attribute((unused)))
+{
+ return 2; /* always 2 */
+}
+/* }}} */
+
+
+/* {{{ ujis functions */
+#define valid_ujis(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xFE))
+#define valid_ujis_kata(c) ((0xA1 <= ((c)&0xFF) && ((c)&0xFF) <= 0xDF))
+#define valid_ujis_ss2(c) (((c)&0xFF) == 0x8E)
+#define valid_ujis_ss3(c) (((c)&0xFF) == 0x8F)
+
+static unsigned int check_mb_ujis(const char *start, const char *end)
+{
+ if (*(uchar*)start < 0x80) {
+ return 0; /* invalid ujis character */
+ }
+ if (valid_ujis(*(start)) && valid_ujis(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss2(*(start)) && valid_ujis_kata(*((start)+1))) {
+ return 2;
+ }
+ if (valid_ujis_ss3(*(start)) && (end-start) > 2 && valid_ujis(*((start)+1)) && valid_ujis(*((start)+2))) {
+ return 3;
+ }
+ return 0;
+}
+
+
+static unsigned int mysql_mbcharlen_ujis(unsigned int ujis)
+{
+ return (valid_ujis(ujis)? 2: valid_ujis_ss2(ujis)? 2: valid_ujis_ss3(ujis)? 3: 1);
+}
+/* }}} */
+
+
+
+/* {{{ utf16 functions */
+#define UTF16_HIGH_HEAD(x) ((((uchar) (x)) & 0xFC) == 0xD8)
+#define UTF16_LOW_HEAD(x) ((((uchar) (x)) & 0xFC) == 0xDC)
+
+static unsigned int check_mb_utf16(const char *start, const char *end)
+{
+ if (start + 2 > end) {
+ return 0;
+ }
+
+ if (UTF16_HIGH_HEAD(*start)) {
+ return (start + 4 <= end) && UTF16_LOW_HEAD(start[2]) ? 4 : 0;
+ }
+
+ if (UTF16_LOW_HEAD(*start)) {
+ return 0;
+ }
+ return 2;
+}
+
+
+static uint mysql_mbcharlen_utf16(unsigned int utf16)
+{
+ return UTF16_HIGH_HEAD(utf16) ? 4 : 2;
+}
+/* }}} */
+
+
+/* {{{ utf32 functions */
+static uint
+check_mb_utf32(const char *start __attribute((unused)), const char *end __attribute((unused)))
+{
+ return 4;
+}
+
+
+static uint
+mysql_mbcharlen_utf32(unsigned int utf32 __attribute((unused)))
+{
+ return 4;
+}
+/* }}} */
+
+/* {{{ gb18030 functions */
+#define is_gb18030_odd(c) (0x81 <= (unsigned char) (c) && (unsigned char) (c) <= 0xFE)
+#define is_gb18030_even_2(c) ((0x40 <= (unsigned char) (c) && (unsigned char) (c) <= 0x7E) || (0x80 <= (unsigned char) (c) && (unsigned char) (c) <= 0xFE))
+#define is_gb18030_even_4(c) (0x30 <= (unsigned char) (c) && (unsigned char) (c) <= 0x39)
+
+
+static unsigned int mysql_mbcharlen_gb18030(unsigned int c)
+{
+ if (c <= 0xFF) {
+ return !is_gb18030_odd(c);
+ }
+ if (c > 0xFFFF || !is_gb18030_odd((c >> 8) & 0xFF)) {
+ return 0;
+ }
+ if (is_gb18030_even_2((c & 0xFF))) {
+ return 2;
+ }
+ if (is_gb18030_even_4((c & 0xFF))) {
+ return 4;
+ }
+
+ return 0;
+}
+
+static unsigned int check_mb_gb18030_valid(const char * start, const char * end)
+{
+ if (end - start <= 1 || !is_gb18030_odd(start[0])) {
+ return 0;
+ }
+
+ if (is_gb18030_even_2(start[1])) {
+ return 2;
+ } else if (end - start > 3 && is_gb18030_even_4(start[1]) && is_gb18030_odd(start[2]) && is_gb18030_even_4(start[3])) {
+ return 4;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/*
+ The server compiles sometimes the full utf-8 (the mb4) as utf8mb4, and the old as utf8,
+ for BC reasons. Sometimes, utf8mb4 is just utf8 but the old charsets are utf8mb3.
+ Change easily now, with a macro, could be made compilation dependable.
+*/
+
+#define UTF8_MB4 "utf8mb4"
+#define UTF8_MB3 "utf8mb3"
+
+/* {{{ mysql_charsets */
+const MARIADB_CHARSET_INFO mariadb_compiled_charsets[] =
+{
+ { 1, 1, "big5","big5_chinese_ci", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ { 3, 1, "dec8", "dec8_swedish_ci", "", 0, "DEC", 1, 1, NULL, NULL},
+ { 4, 1, "cp850", "cp850_general_ci", "", 850, "CP850", 1, 1, NULL, NULL},
+ { 6, 1, "hp8", "hp8_english_ci", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ { 7, 1, "koi8r", "koi8r_general_ci", "", 20866, "KOI8R", 1, 1, NULL, NULL},
+ { 8, 1, "latin1", "latin1_swedish_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 9, 1, "latin2", "latin2_general_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 10, 1, "swe7", "swe7_swedish_ci", "", 20107, "", 1, 1, NULL, NULL},
+ { 11, 1, "ascii", "ascii_general_ci", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ { 12, 1, "ujis", "ujis_japanese_ci", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ { 13, 1, "sjis", "sjis_japanese_ci", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ { 16, 1, "hebrew", "hebrew_general_ci", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ { 18, 1, "tis620", "tis620_thai_ci", "", 874, "TIS620", 1, 1, NULL, NULL},
+ { 19, 1, "euckr", "euckr_korean_ci", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ { 22, 1, "koi8u", "koi8u_general_ci", "", 21866, "KOI8U", 1, 1, NULL, NULL},
+ { 24, 1, "gb2312", "gb2312_chinese_ci", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ { 25, 1, "greek", "greek_general_ci", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ { 26, 1, "cp1250", "cp1250_general_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 28, 1, "gbk", "gbk_chinese_ci", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ { 30, 1, "latin5", "latin5_turkish_ci", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ { 32, 1, "armscii8", "armscii8_general_ci", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ { 33, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 35, 1, "ucs2", "ucs2_general_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 36, 1, "cp866", "cp866_general_ci", "", 866, "CP866", 1, 1, NULL, NULL},
+ { 37, 1, "keybcs2", "keybcs2_general_ci", "", 0, "", 1, 1, NULL, NULL},
+ { 38, 1, "macce", "macce_general_ci", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ { 39, 1, "macroman", "macroman_general_ci", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ { 40, 1, "cp852", "cp852_general_ci", "", 852, "CP852", 1, 1, NULL, NULL},
+ { 41, 1, "latin7", "latin7_general_ci", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 51, 1, "cp1251", "cp1251_general_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 57, 1, "cp1256", "cp1256_general_ci", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ { 59, 1, "cp1257", "cp1257_general_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ { 63, 1, "binary", "binary", "", 0, "ASCII", 1, 1, NULL, NULL},
+ { 64, 1, "armscii8", "armscii8_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ { 92, 1, "geostd8", "geostd8_general_ci", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ { 95, 1, "cp932", "cp932_japanese_ci", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ { 97, 1, "eucjpms", "eucjpms_japanese_ci", "", 932, "EUC-JP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 2, 1, "latin2", "latin2_czech_cs", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 5, 1, "latin1", "latin1_german1_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 14, 1, "cp1251", "cp1251_bulgarian_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 15, 1, "latin1", "latin1_danish_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 17, 1, "filename", "filename", "", 0, "", 1, 5, NULL, NULL},
+ { 20, 1, "latin7", "latin7_estonian_cs", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 21, 1, "latin2", "latin2_hungarian_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 23, 1, "cp1251", "cp1251_ukrainian_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 27, 1, "latin2", "latin2_croatian_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ { 29, 1, "cp1257", "cp1257_lithuanian_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ { 31, 1, "latin1", "latin1_german2_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 34, 1, "cp1250", "cp1250_czech_cs", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 42, 1, "latin7", "latin7_general_cs", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 43, 1, "macce", "macce_bin", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ { 44, 1, "cp1250", "cp1250_croatian_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 45, 1, UTF8_MB4, UTF8_MB4"_general_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 46, 1, UTF8_MB4, UTF8_MB4"_bin", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 47, 1, "latin1", "latin1_bin", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 48, 1, "latin1", "latin1_general_ci", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 49, 1, "latin1", "latin1_general_cs", "", 1250, "LATIN1", 1, 1, NULL, NULL},
+ { 50, 1, "cp1251", "cp1251_bin", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 52, 1, "cp1251", "cp1251_general_cs", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ { 53, 1, "macroman", "macroman_bin", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ { 54, 1, "utf16", "utf16_general_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 55, 1, "utf16", "utf16_bin", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 56, 1, "utf16le", "utf16_general_ci", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 58, 1, "cp1257", "cp1257_bin", "", 1257, "CP1257", 1, 1, NULL, NULL},
+#ifdef USED_TO_BE_SO_BEFORE_MYSQL_5_5
+ { 60, 1, "armascii8", "armascii8_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+#endif
+ { 60, 1, "utf32", "utf32_general_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 61, 1, "utf32", "utf32_bin", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 62, 1, "utf16le", "utf16_bin", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 65, 1, "ascii", "ascii_bin", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ { 66, 1, "cp1250", "cp1250_bin", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 67, 1, "cp1256", "cp1256_bin", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ { 68, 1, "cp866", "cp866_bin", "", 866, "CP866", 1, 1, NULL, NULL},
+ { 69, 1, "dec8", "dec8_bin", "", 0, "DEC", 1, 1, NULL, NULL},
+ { 70, 1, "greek", "greek_bin", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ { 71, 1, "hebrew", "hebrew_bin", "", 1255, "hebrew", 1, 1, NULL, NULL},
+ { 72, 1, "hp8", "hp8_bin", "", 0, "HPROMAN-8", 1, 1, NULL, NULL},
+ { 73, 1, "keybcs2", "keybcs2_bin", "", 0, "", 1, 1, NULL, NULL},
+ { 74, 1, "koi8r", "koi8r_bin", "", 20866, "KOI8R", 1, 1, NULL, NULL},
+ { 75, 1, "koi8u", "koi8u_bin", "", 21866, "KOI8U", 1, 1, NULL, NULL},
+ { 76, 1, UTF8_MB3, UTF8_MB3"_tolower_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 77, 1, "latin2", "latin2_bin", "", 28592, "LATIN2", 1, 1, NULL, NULL},
+ { 78, 1, "latin5", "latin5_bin", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ { 79, 1, "latin7", "latin7_bin", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ { 80, 1, "cp850", "cp850_bin", "", 850, "CP850", 1, 1, NULL, NULL},
+ { 81, 1, "cp852", "cp852_bin", "", 852, "CP852", 1, 1, NULL, NULL},
+ { 82, 1, "swe7", "swe7_bin", "", 0, "", 1, 1, NULL, NULL},
+ { 93, 1, "geostd8", "geostd8_bin", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ { 83, 1, UTF8_MB3, UTF8_MB3"_bin", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 84, 1, "big5", "big5_bin", "", 65000, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ { 85, 1, "euckr", "euckr_bin", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ { 86, 1, "gb2312", "gb2312_bin", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ { 87, 1, "gbk", "gbk_bin", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ { 88, 1, "sjis", "sjis_bin", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ { 89, 1, "tis620", "tis620_bin", "", 874, "TIS620", 1, 1, NULL, NULL},
+ { 90, 1, "ucs2", "ucs2_bin", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 91, 1, "ujis", "ujis_bin", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ { 94, 1, "latin1", "latin1_spanish_ci", "", 1252, "LATIN1", 1, 1, NULL, NULL},
+ { 96, 1, "cp932", "cp932_bin", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ { 99, 1, "cp1250", "cp1250_polish_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ { 98, 1, "eucjpms", "eucjpms_bin", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ { 101, 1, "utf16", "utf16_unicode_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 102, 1, "utf16", "utf16_icelandic_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 103, 1, "utf16", "utf16_latvian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 104, 1, "utf16", "utf16_romanian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 105, 1, "utf16", "utf16_slovenian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 106, 1, "utf16", "utf16_polish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 107, 1, "utf16", "utf16_estonian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 108, 1, "utf16", "utf16_spanish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 109, 1, "utf16", "utf16_swedish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 110, 1, "utf16", "utf16_turkish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 111, 1, "utf16", "utf16_czech_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 112, 1, "utf16", "utf16_danish_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 113, 1, "utf16", "utf16_lithunian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 114, 1, "utf16", "utf16_slovak_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 115, 1, "utf16", "utf16_spanish2_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 116, 1, "utf16", "utf16_roman_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 117, 1, "utf16", "utf16_persian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 118, 1, "utf16", "utf16_esperanto_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 120, 1, "utf16", "utf16_sinhala_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 121, 1, "utf16", "utf16_german2_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 122, 1, "utf16", "utf16_croatian_mysql561_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 123, 1, "utf16", "utf16_unicode_520_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 124, 1, "utf16", "utf16_vietnamese_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 128, 1, "ucs2", "ucs2_unicode_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 129, 1, "ucs2", "ucs2_icelandic_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 130, 1, "ucs2", "ucs2_latvian_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 131, 1, "ucs2", "ucs2_romanian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 132, 1, "ucs2", "ucs2_slovenian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 133, 1, "ucs2", "ucs2_polish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 134, 1, "ucs2", "ucs2_estonian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 135, 1, "ucs2", "ucs2_spanish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 136, 1, "ucs2", "ucs2_swedish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 137, 1, "ucs2", "ucs2_turkish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 138, 1, "ucs2", "ucs2_czech_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 139, 1, "ucs2", "ucs2_danish_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 140, 1, "ucs2", "ucs2_lithuanian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 141, 1, "ucs2", "ucs2_slovak_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 142, 1, "ucs2", "ucs2_spanish2_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 143, 1, "ucs2", "ucs2_roman_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 144, 1, "ucs2", "ucs2_persian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 145, 1, "ucs2", "ucs2_esperanto_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 146, 1, "ucs2", "ucs2_hungarian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 147, 1, "ucs2", "ucs2_sinhala_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 148, 1, "ucs2", "ucs2_german2_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 149, 1, "ucs2", "ucs2_croatian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 150, 1, "ucs2", "ucs2_unicode_520_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 151, 1, "ucs2", "ucs2_vietnamese_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 159, 1, "ucs2", "ucs2_general_mysql500_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, /* MDB */
+ { 160, 1, "utf32", "utf32_unicode_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 161, 1, "utf32", "utf32_icelandic_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 162, 1, "utf32", "utf32_latvian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 163, 1, "utf32", "utf32_romanian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 164, 1, "utf32", "utf32_slovenian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 165, 1, "utf32", "utf32_polish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 166, 1, "utf32", "utf32_estonian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 167, 1, "utf32", "utf32_spanish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 168, 1, "utf32", "utf32_swedish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 169, 1, "utf32", "utf32_turkish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 170, 1, "utf32", "utf32_czech_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 171, 1, "utf32", "utf32_danish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 172, 1, "utf32", "utf32_lithuanian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 173, 1, "utf32", "utf32_slovak_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 174, 1, "utf32", "utf32_spanish_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 175, 1, "utf32", "utf32_roman_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 176, 1, "utf32", "utf32_persian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 177, 1, "utf32", "utf32_esperanto_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 178, 1, "utf32", "utf32_hungarian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 179, 1, "utf32", "utf32_sinhala_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 180, 1, "utf32", "utf32_german2_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 181, 1, "utf32", "utf32_croatian_mysql561_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 182, 1, "utf32", "utf32_unicode_520_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 183, 1, "utf32", "utf32_vietnamese_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+
+ { 192, 1, UTF8_MB3, UTF8_MB3"_general_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 193, 1, UTF8_MB3, UTF8_MB3"_icelandic_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 194, 1, UTF8_MB3, UTF8_MB3"_latvian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 195, 1, UTF8_MB3, UTF8_MB3"_romanian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 196, 1, UTF8_MB3, UTF8_MB3"_slovenian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 197, 1, UTF8_MB3, UTF8_MB3"_polish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 198, 1, UTF8_MB3, UTF8_MB3"_estonian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 199, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 119, 1, UTF8_MB3, UTF8_MB3"_spanish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 200, 1, UTF8_MB3, UTF8_MB3"_swedish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 201, 1, UTF8_MB3, UTF8_MB3"_turkish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 202, 1, UTF8_MB3, UTF8_MB3"_czech_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 203, 1, UTF8_MB3, UTF8_MB3"_danish_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 204, 1, UTF8_MB3, UTF8_MB3"_lithuanian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid },
+ { 205, 1, UTF8_MB3, UTF8_MB3"_slovak_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 206, 1, UTF8_MB3, UTF8_MB3"_spanish2_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 207, 1, UTF8_MB3, UTF8_MB3"_roman_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 208, 1, UTF8_MB3, UTF8_MB3"_persian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 209, 1, UTF8_MB3, UTF8_MB3"_esperanto_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 210, 1, UTF8_MB3, UTF8_MB3"_hungarian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 211, 1, UTF8_MB3, UTF8_MB3"_sinhala_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 212, 1, UTF8_MB3, UTF8_MB3"_german2_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 214, 1, UTF8_MB3, UTF8_MB3"_unicode_520_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 215, 1, UTF8_MB3, UTF8_MB3"_vietnamese_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ { 213, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 223, 1, UTF8_MB3, UTF8_MB3"_general_mysql500_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+
+ { 224, 1, UTF8_MB4, UTF8_MB4"_unicode_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 225, 1, UTF8_MB4, UTF8_MB4"_icelandic_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 226, 1, UTF8_MB4, UTF8_MB4"_latvian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 227, 1, UTF8_MB4, UTF8_MB4"_romanian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 228, 1, UTF8_MB4, UTF8_MB4"_slovenian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 229, 1, UTF8_MB4, UTF8_MB4"_polish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 230, 1, UTF8_MB4, UTF8_MB4"_estonian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 231, 1, UTF8_MB4, UTF8_MB4"_spanish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 232, 1, UTF8_MB4, UTF8_MB4"_swedish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 233, 1, UTF8_MB4, UTF8_MB4"_turkish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 234, 1, UTF8_MB4, UTF8_MB4"_czech_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 235, 1, UTF8_MB4, UTF8_MB4"_danish_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 236, 1, UTF8_MB4, UTF8_MB4"_lithuanian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 237, 1, UTF8_MB4, UTF8_MB4"_slovak_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 238, 1, UTF8_MB4, UTF8_MB4"_spanish2_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 239, 1, UTF8_MB4, UTF8_MB4"_roman_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 240, 1, UTF8_MB4, UTF8_MB4"_persian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 241, 1, UTF8_MB4, UTF8_MB4"_esperanto_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 242, 1, UTF8_MB4, UTF8_MB4"_hungarian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 243, 1, UTF8_MB4, UTF8_MB4"_sinhala_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 244, 1, UTF8_MB4, UTF8_MB4"_german2_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 245, 1, UTF8_MB4, UTF8_MB4"_croatian_mysql561_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 246, 1, UTF8_MB4, UTF8_MB4"_unicode_520_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 247, 1, UTF8_MB4, UTF8_MB4"_vietnamese_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 248, 1, "gb18030", "gb18030_chinese_ci", "", 54936, "GB18030", 1, 4, mysql_mbcharlen_gb18030, check_mb_gb18030_valid},
+ { 249, 1, "gb18030", "gb18030_bin", "", 54936, "GB18030", 1, 4, mysql_mbcharlen_gb18030, check_mb_gb18030_valid},
+ { 250, 1, "gb18030", "gb18030_unicode_520_ci", "", 54936, "GB18030", 1, 4, mysql_mbcharlen_gb18030, check_mb_gb18030_valid},
+
+
+ { 254, 1, UTF8_MB3, UTF8_MB3"_general_cs", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+
+ { 255, 1, UTF8_MB4, UTF8_MB4"_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 256, 1, UTF8_MB4, UTF8_MB4"_de_pb_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 257, 1, UTF8_MB4, UTF8_MB4"_is_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 258, 1, UTF8_MB4, UTF8_MB4"_lv_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 259, 1, UTF8_MB4, UTF8_MB4"_ro_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 260, 1, UTF8_MB4, UTF8_MB4"_sl_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 261, 1, UTF8_MB4, UTF8_MB4"_pl_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 262, 1, UTF8_MB4, UTF8_MB4"_et_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 263, 1, UTF8_MB4, UTF8_MB4"_es_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 264, 1, UTF8_MB4, UTF8_MB4"_sv_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 265, 1, UTF8_MB4, UTF8_MB4"_tr_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 266, 1, UTF8_MB4, UTF8_MB4"_cs_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 267, 1, UTF8_MB4, UTF8_MB4"_da_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 268, 1, UTF8_MB4, UTF8_MB4"_lt_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 269, 1, UTF8_MB4, UTF8_MB4"_sk_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 270, 1, UTF8_MB4, UTF8_MB4"_es_trad_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 271, 1, UTF8_MB4, UTF8_MB4"_la_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 273, 1, UTF8_MB4, UTF8_MB4"_eo_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 274, 1, UTF8_MB4, UTF8_MB4"_hu_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 275, 1, UTF8_MB4, UTF8_MB4"_hr_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 277, 1, UTF8_MB4, UTF8_MB4"_vi_0900_ai_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 278, 1, UTF8_MB4, UTF8_MB4"_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 279, 1, UTF8_MB4, UTF8_MB4"_de_pb__0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 280, 1, UTF8_MB4, UTF8_MB4"_is_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 281, 1, UTF8_MB4, UTF8_MB4"_lv_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 282, 1, UTF8_MB4, UTF8_MB4"_ro_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 283, 1, UTF8_MB4, UTF8_MB4"_sl_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 284, 1, UTF8_MB4, UTF8_MB4"_pl_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 285, 1, UTF8_MB4, UTF8_MB4"_et_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 286, 1, UTF8_MB4, UTF8_MB4"_es_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 287, 1, UTF8_MB4, UTF8_MB4"_sv_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 288, 1, UTF8_MB4, UTF8_MB4"_tr_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 289, 1, UTF8_MB4, UTF8_MB4"_cs_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 290, 1, UTF8_MB4, UTF8_MB4"_da_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 291, 1, UTF8_MB4, UTF8_MB4"_lt_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 292, 1, UTF8_MB4, UTF8_MB4"_sk_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 293, 1, UTF8_MB4, UTF8_MB4"_es_trad_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 294, 1, UTF8_MB4, UTF8_MB4"_la_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 296, 1, UTF8_MB4, UTF8_MB4"_eo_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 297, 1, UTF8_MB4, UTF8_MB4"_hu_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 298, 1, UTF8_MB4, UTF8_MB4"_hr_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 300, 1, UTF8_MB4, UTF8_MB4"_vi_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 303, 1, UTF8_MB4, UTF8_MB4"_ja_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 304, 1, UTF8_MB4, UTF8_MB4"_ja_0900_as_cs_ks", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 305, 1, UTF8_MB4, UTF8_MB4"_0900_as_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 306, 1, UTF8_MB4, UTF8_MB4"_ru_0900_as_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 307, 1, UTF8_MB4, UTF8_MB4"_ru_0900_as_cs", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 576, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 577, 1, UTF8_MB3, UTF8_MB3"_myanmar_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 578, 1, UTF8_MB3, UTF8_MB3"_thai_520_w2", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/
+ { 608, 1, UTF8_MB4, UTF8_MB4"_croatian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 609, 1, UTF8_MB4, UTF8_MB4"_myanmar_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 610, 1, UTF8_MB4, UTF8_MB4"_thai_520_w2", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 640, 1, "ucs2", "ucs2_croatian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 641, 1, "ucs2", "ucs2_myanmar_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 642, 1, "ucs2", "ucs2_thai_520_w2", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ { 672, 1, "utf16", "utf16_croatian_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 673, 1, "utf16", "utf16_myanmar_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 674, 1, "utf16", "utf16_thai_520_w2", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ { 736, 1, "utf32", "utf32_croatian_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 737, 1, "utf32", "utf32_myanmar_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ { 738, 1, "utf32", "utf32_thai_520_w2", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1025, 1, "big5","big5_chinese_nopad_ci", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ {1027, 1, "dec8", "dec8_swedisch_nopad_ci", "", 0, "DEC", 1, 1, NULL, NULL},
+ {1028, 1, "cp850", "cp850_general_nopad_ci", "", 850, "CP850", 1, 1, NULL, NULL},
+ {1030, 1, "hp8", "hp8_english_nopad_ci", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ {1031, 1, "koi8r", "koi8r_general_nopad_ci", "", 878, "KOI8R", 1, 1, NULL, NULL},
+ {1032, 1, "latin1", "latin1_swedish_nopad_ci", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ {1033, 1, "latin2", "latin2_general_nopad_ci", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ {1034, 1, "swe7", "swe7_swedish_nopad_ci", "", 20107, "", 1, 1, NULL, NULL},
+ {1035, 1, "ascii", "ascii_general_nopad_ci", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ {1036, 1, "ujis", "ujis_japanese_nopad_ci", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ {1037, 1, "sjis", "sjis_japanese_nopad_ci", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ {1040, 1, "hebrew", "hebrew_general_nopad_ci", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ {1042, 1, "tis620", "tis620_thai_nopad_ci", "", 874, "TIS620", 1, 1, NULL, NULL},
+ {1043, 1, "euckr", "euckr_korean_nopad_ci", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ {1046, 1, "koi8u", "koi8u_general_nopad_ci", "", 20866, "KOI8U", 1, 1, NULL, NULL},
+ {1048, 1, "gb2312", "gb2312_chinese_nopad_ci", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ {1049, 1, "greek", "greek_general_nopad_ci", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ {1050, 1, "cp1250", "cp1250_general_nopad_ci", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ {1052, 1, "gbk", "gbk_chinese_nopad_ci", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ {1054, 1, "latin5", "latin5_turkish_nopad_ci", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ {1056, 1, "armscii8", "armscii8_general_nopad_ci", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ {1057, 1, UTF8_MB3, UTF8_MB3"_general_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1059, 1, "ucs2", "ucs2_general_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1060, 1, "cp866", "cp866_general_nopad_ci", "", 866, "CP866", 1, 1, NULL, NULL},
+ {1061, 1, "keybcs2", "keybcs2_general_nopad_ci", "", 0, "", 1, 1, NULL, NULL},
+ {1062, 1, "macce", "macce_general_nopad_ci", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ {1063, 1, "macroman", "macroman_general_nopad_ci", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ {1064, 1, "cp852", "cp852_general_nopad_ci", "", 852, "CP852", 1, 1, NULL, NULL},
+ {1065, 1, "latin7", "latin7_general_nopad_ci", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ {1067, 1, "macce", "macce_nopad_bin", "", 10029, "CP1282", 1, 1, NULL, NULL},
+ {1069, 1, UTF8_MB4, UTF8_MB4"_general_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1070, 1, UTF8_MB4, UTF8_MB4"_general_nopad_bin", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1071, 1, "latin1", "latin1_nopad_bin", "", 850, "LATIN1", 1, 1, NULL, NULL},
+ {1074, 1, "cp1251", "cp1251_nopad_bin", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ {1075, 1, "cp1251", "cp1251_general_nopad_ci", "", 1251, "CP1251", 1, 1, NULL, NULL},
+ {1077, 1, "macroman", "macroman_nopad_bin", "", 10000, "MACINTOSH", 1, 1, NULL, NULL},
+ {1078, 1, "utf16", "utf16_general_nopad_ci", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1079, 1, "utf16", "utf16_nopad_bin", "", 0, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1080, 1, "utf16le", "utf16le_general_nopad_ci", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1081, 1, "cp1256", "cp1256_general_nopad_ci", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ {1082, 1, "cp1257", "cp1257_nopad_bin", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ {1083, 1, "cp1257", "cp1257_general_nopad_ci", "", 1257, "CP1257", 1, 1, NULL, NULL},
+ {1084, 1, "utf32", "utf32_general_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1085, 1, "utf32", "utf32_nopad_bin", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1086, 1, "utf16le", "utf16le_nopad_bin", "", 1200, "UTF16LE", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1088, 1, "armscii8", "armscii8_nopad_bin", "", 0, "ARMSCII-8", 1, 1, NULL, NULL},
+ {1089, 1, "ascii", "ascii_nopad_bin", "", 1252, "ASCII", 1, 1, NULL, NULL},
+ {1090, 1, "cp1250", "cp1250_nopad_bin", "", 1250, "CP1250", 1, 1, NULL, NULL},
+ {1091, 1, "cp1256", "cp1256_nopad_bin", "", 1256, "CP1256", 1, 1, NULL, NULL},
+ {1092, 1, "cp866", "cp866_nopad_bin", "", 866, "CP866", 1, 1, NULL, NULL},
+ {1093, 1, "dec8", "dec8_nopad_bin", "", 0, "DEC", 1, 1, NULL, NULL},
+ {1094, 1, "greek", "greek_nopad_bin", "", 28597, "GREEK", 1, 1, NULL, NULL},
+ {1095, 1, "hebrew", "hebrew_nopad_bin", "", 1255, "HEBREW", 1, 1, NULL, NULL},
+ {1096, 1, "hp8", "hp8_nopad_bin", "", 0, "HP-ROMAN8", 1, 1, NULL, NULL},
+ {1097, 1, "keybcs2", "keybcs2_nopad_bin", "", 0, "", 1, 1, NULL, NULL},
+ {1098, 1, "koi8r", "koi8r_nopad_bin", "", 878, "KOI8R", 1, 1, NULL, NULL},
+ {1099, 1, "koi8u", "koi8u_nopad_bin", "", 20866, "KOI8U", 1, 1, NULL, NULL},
+ {1101, 1, "latin2", "latin2_nopad_bin", "", 852, "LATIN2", 1, 1, NULL, NULL},
+ {1102, 1, "latin5", "latin5_nopad_bin", "", 1254, "LATIN5", 1, 1, NULL, NULL},
+ {1103, 1, "latin7", "latin7_nopad_bin", "", 28603, "LATIN7", 1, 1, NULL, NULL},
+ {1104, 1, "cp850", "cp850_nopad_bin", "", 850, "CP850", 1, 1, NULL, NULL},
+ {1105, 1, "cp852", "cp852_nopad_bin", "", 852, "CP852", 1, 1, NULL, NULL},
+ {1106, 1, "swe7", "swe7_nopad_bin", "", 20107, "", 1, 1, NULL, NULL},
+ {1107, 1, UTF8_MB3, UTF8_MB3"_nopad_bin", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1108, 1, "big5","big5_nopad_bin", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5},
+ {1109, 1, "euckr", "euckr_nopad_bin", "", 51949, "EUCKR", 1, 2, mysql_mbcharlen_euckr, check_mb_euckr},
+ {1110, 1, "gb2312", "gb2312_nopad_bin", "", 936, "GB2312", 1, 2, mysql_mbcharlen_gb2312, check_mb_gb2312},
+ {1111, 1, "gbk", "gbk_nopad_bin", "", 936, "GBK", 1, 2, mysql_mbcharlen_gbk, check_mb_gbk},
+ {1112, 1, "sjis", "sjis_nopad_bin", "", 932, "SJIS", 1, 2, mysql_mbcharlen_sjis, check_mb_sjis},
+ {1113, 1, "tis620", "tis620_nopad_bin", "", 874, "TIS620", 1, 1, NULL, NULL},
+ {1114, 1, "ucs2", "ucs2_nopad_bin", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1115, 1, "ujis", "ujis_nopad_bin", "", 20932, "UJIS", 1, 3, mysql_mbcharlen_ujis, check_mb_ujis},
+ {1116, 1, "geostd8", "geostd8_general_nopad_ci", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ {1117, 1, "geostd8", "geostd8_nopad_bin", "", 0, "GEORGIAN-PS", 1, 1, NULL, NULL},
+ {1119, 1, "cp932", "cp932_japanese_nopad_ci", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ {1120, 1, "cp932", "cp932_nopad_bin", "", 932, "CP932", 1, 2, mysql_mbcharlen_cp932, check_mb_cp932},
+ {1121, 1, "eucjpms", "eucjpms_japanese_nopad_ci", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ {1122, 1, "eucjpms", "eucjpms_nopad_bin", "", 932, "EUCJP-MS", 1, 3, mysql_mbcharlen_eucjpms, check_mb_eucjpms},
+ {1125, 1, "utf16", "utf16_unicode_nopad_ci", "", 1200, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1147, 1, "utf16", "utf16_unicode_520_nopad_ci", "", 1200, "UTF16", 2, 4, mysql_mbcharlen_utf16, check_mb_utf16},
+ {1152, 1, "ucs2", "ucs2_unicode_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1174, 1, "ucs2", "ucs2_unicode_520_nopad_ci", "", 1200, "UCS-2BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},
+ {1184, 1, "utf32", "utf32_unicode_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1206, 1, "utf32", "utf32_unicode_520_nopad_ci", "", 0, "UTF32", 4, 4, mysql_mbcharlen_utf32, check_mb_utf32},
+ {1216, 1, UTF8_MB3, UTF8_MB3"_unicode_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1238, 1, UTF8_MB3, UTF8_MB3"_unicode_520_nopad_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid},
+ {1248, 1, UTF8_MB4, UTF8_MB4"_unicode_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ {1270, 1, UTF8_MB4, UTF8_MB4"_unicode_520_nopad_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid},
+ { 0, 0, NULL, NULL, NULL, 0, NULL, 0, 0, NULL, NULL}
+};
+/* }}} */
+
+
+typedef enum my_cs_encoding_enum
+{
+ MY_CS_ENCODING_UTF8MB3= 0,
+ MY_CS_ENCODING_UTF8MB4= 1,
+ MY_CS_ENCODING_UCS2= 2,
+ MY_CS_ENCODING_UTF16= 3,
+ MY_CS_ENCODING_UTF32= 4,
+} my_cs_encoding_t;
+
+#define MY_CS_ENCODING_LAST MY_CS_ENCODING_UTF32
+
+
+typedef struct my_uca1400_collation_definition_st
+{
+ const char * tailoring;
+ const char * name;
+ uint16 id_utf8mb3;
+ uint16 id_utf8mb4;
+ uint16 id_ucs2;
+ uint16 id_utf16;
+ uint16 id_utf32;
+} MY_UCA1400_COLLATION_DEFINITION;
+
+
+#define MY_UCA1400_COLLATION_DEFINITION_COUNT 26
+/*
+ UCA1400 collation definitions in the order of their UCA400 counterparts,
+ with IDs of their closest UCA1400 counterparts, for character sets
+ utf8mb3, utf8mb4, ucs2, utf16, utf32.
+*/
+static MY_UCA1400_COLLATION_DEFINITION
+my_uca1400_collation_definitions[MY_UCA1400_COLLATION_DEFINITION_COUNT]=
+{
+#define COLDEF(tl,name,id_utf8mb3,id_utf8mb4,id_ucs2,id_utf16,id_utf32) \
+ {(""), (name), (id_utf8mb3), (id_utf8mb4), (id_ucs2), (id_utf16), (id_utf32)}
+ /* Block N1 */
+ COLDEF("", "", 192, 224, 128, 101, 160),
+ COLDEF(icelandic, "icelandic", 193, 225, 129, 102, 161),
+ COLDEF(latvian, "latvian", 194, 226, 130, 103, 162),
+ COLDEF(romanian, "romanian", 195, 227, 131, 104, 163),
+ COLDEF(slovenian, "slovenian", 196, 228, 132, 105, 164),
+ COLDEF(polish, "polish", 197, 229, 133, 106, 165),
+ COLDEF(estonian, "estonian", 198, 230, 134, 107, 166),
+ COLDEF(spanish, "spanish", 199, 231, 135, 108, 167),
+ COLDEF(swedish, "swedish", 200, 232, 136, 109, 168),
+ COLDEF(turkish, "turkish", 201, 233, 137, 110, 169),
+ COLDEF(czech, "czech", 202, 234, 138, 111, 170),
+ COLDEF(danish, "danish", 203, 235, 139, 112, 171),
+ COLDEF(lithuanian, "lithuanian", 204, 236, 140, 113, 172),
+ COLDEF(slovak, "slovak", 205, 237, 141, 114, 173),
+ COLDEF(spanish2, "spanish2", 206, 238, 142, 115, 174),
+ COLDEF(roman, "roman", 207, 239, 143, 116, 175),
+ COLDEF(persian, "persian", 208, 240, 144, 117, 176),
+ COLDEF(esperanto, "esperanto", 209, 241, 145, 118, 177),
+ COLDEF(hungarian, "hungarian", 210, 242, 146, 119, 178),
+ COLDEF(sinhala, "sinhala", 211, 243, 147, 120, 179),
+ COLDEF(german2, "german2", 212, 244, 148, 121, 180),
+ /*
+ Don't add "croatian_mysql561", as its UCA-4.0.0 version was added for
+ compatibility only, to open MySQL tables.
+ The "croatian" version is closer to CLDR. Checked with CLDR-40.
+ */
+ COLDEF(NULL,/*croatian_mysql561*/ NULL, 213, 245, 149, 122, 181),
+ COLDEF(NULL,/*unicode_520_ci*/ NULL, 214, 246, 150, 123, 182),
+ COLDEF(vietnamese, "vietnamese", 215, 247, 151, 124, 183),
+ /* Block N2 */
+ COLDEF(croatian_mariadb, "croatian", 576, 608, 640, 672, 736),
+ /* Don't add myanmar yet. It causes too long weights on the tertiary level.*/
+ COLDEF(NULL,/*myanmar*/ NULL, 577, 609, 641, 673, 737)
+
+#undef COLDEF
+};
+
+
+#define MY_UCA1400_COLLATION_ID_POSSIBLE_MIN 2048
+#define MY_UCA1400_COLLATION_ID_POSSIBLE_MAX 4095
+
+static inline my_bool
+my_collation_id_is_uca1400(uint id)
+{
+ return (my_bool) (id >= MY_UCA1400_COLLATION_ID_POSSIBLE_MIN &&
+ id <= MY_UCA1400_COLLATION_ID_POSSIBLE_MAX);
+}
+
+
+static inline uint
+my_uca1400_collation_id_to_tailoring_id(uint id)
+{
+ return (id >> 3) & 0x1F;
+}
+
+
+static inline my_cs_encoding_t
+my_uca1400_collation_id_to_charset_id(uint id)
+{
+ return (my_cs_encoding_t) ((id >> 8) & 0x07);
+}
+
+
+static uint my_uca1400_collation_id_uca400_compat(uint id)
+{
+ uint tlid= my_uca1400_collation_id_to_tailoring_id(id);
+ my_cs_encoding_t csid= my_uca1400_collation_id_to_charset_id(id);
+ MY_UCA1400_COLLATION_DEFINITION *def;
+ DBUG_ASSERT(my_collation_id_is_uca1400(id));
+ if (!(def= &my_uca1400_collation_definitions[tlid])->name)
+ return id;
+ switch (csid) {
+ case MY_CS_ENCODING_UTF8MB3: return def->id_utf8mb3;
+ case MY_CS_ENCODING_UTF8MB4: return def->id_utf8mb4;
+ case MY_CS_ENCODING_UCS2: return def->id_ucs2;
+ case MY_CS_ENCODING_UTF16: return def->id_utf16;
+ case MY_CS_ENCODING_UTF32: return def->id_utf32;
+ }
+ return id;
+}
+
+
+/* {{{ mysql_find_charset_nr */
+const MARIADB_CHARSET_INFO * mysql_find_charset_nr(unsigned int charsetnr)
+{
+ const MARIADB_CHARSET_INFO * c = mariadb_compiled_charsets;
+
+ if (my_collation_id_is_uca1400(charsetnr))
+ charsetnr= my_uca1400_collation_id_uca400_compat(charsetnr);
+
+ do {
+ if (c->nr == charsetnr) {
+ return(c);
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ return(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysql_find_charset_name */
+MARIADB_CHARSET_INFO * mysql_find_charset_name(const char *name)
+{
+ MARIADB_CHARSET_INFO *c = (MARIADB_CHARSET_INFO *)mariadb_compiled_charsets;
+ const char *csname;
+
+ if (!strcasecmp(name, MADB_AUTODETECT_CHARSET_NAME))
+ csname= madb_get_os_character_set();
+ else
+ csname= (char *)name;
+
+ if (!strcasecmp("utf8",csname))
+ csname= "utf8mb3";
+
+ do {
+ if (!strcasecmp(c->csname, csname)) {
+ return(c);
+ }
+ ++c;
+ } while (c[0].nr != 0);
+ return(NULL);
+}
+/* }}} */
+
+
+/* {{{ mysql_cset_escape_quotes */
+size_t mysql_cset_escape_quotes(const MARIADB_CHARSET_INFO *cset, char *newstr,
+ const char * escapestr, size_t escapestr_len )
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ my_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ unsigned int len = 0;
+ /* check unicode characters */
+
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (*escapestr == '\'') {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = '\'';
+ *newstr++ = '\'';
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ return((size_t)~0);
+ }
+ return((size_t)(newstr - newstr_s));
+}
+/* }}} */
+
+
+/* {{{ mysql_cset_escape_slashes */
+size_t mysql_cset_escape_slashes(const MARIADB_CHARSET_INFO * cset, char *newstr,
+ const char * escapestr, size_t escapestr_len )
+{
+ const char *newstr_s = newstr;
+ const char *newstr_e = newstr + 2 * escapestr_len;
+ const char *end = escapestr + escapestr_len;
+ my_bool escape_overflow = FALSE;
+
+ for (;escapestr < end; escapestr++) {
+ char esc = '\0';
+ unsigned int len = 0;
+
+ /* check unicode characters */
+ if (cset->char_maxlen > 1 && (len = cset->mb_valid(escapestr, end))) {
+ /* check possible overflow */
+ if ((newstr + len) > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy mb char without escaping it */
+ while (len--) {
+ *newstr++ = *escapestr++;
+ }
+ escapestr--;
+ continue;
+ }
+ if (cset->char_maxlen > 1 && cset->mb_charlen(*escapestr) > 1) {
+ esc = *escapestr;
+ } else {
+ switch (*escapestr) {
+ case 0:
+ esc = '0';
+ break;
+ case '\n':
+ esc = 'n';
+ break;
+ case '\r':
+ esc = 'r';
+ break;
+ case '\\':
+ case '\'':
+ case '"':
+ esc = *escapestr;
+ break;
+ case '\032':
+ esc = 'Z';
+ break;
+ }
+ }
+ if (esc) {
+ if (newstr + 2 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy escaped character */
+ *newstr++ = '\\';
+ *newstr++ = esc;
+ } else {
+ if (newstr + 1 > newstr_e) {
+ escape_overflow = TRUE;
+ break;
+ }
+ /* copy non escaped character */
+ *newstr++ = *escapestr;
+ }
+ }
+ *newstr = '\0';
+
+ if (escape_overflow) {
+ return((size_t)~0);
+ }
+ return((size_t)(newstr - newstr_s));
+}
+/* }}} */
+
+/* {{{ MADB_OS_CHARSET */
+struct st_madb_os_charset {
+ const char *identifier;
+ const char *description;
+ const char *charset;
+ const char *iconv_cs;
+ unsigned char supported;
+};
+
+#define MADB_CS_UNSUPPORTED 0
+#define MADB_CS_APPROX 1
+#define MADB_CS_EXACT 2
+
+/* Please add new character sets at the end. */
+struct st_madb_os_charset MADB_OS_CHARSET[]=
+{
+#ifdef _WIN32
+ /* Windows code pages */
+ {"037", "IBM EBCDIC US-Canada", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"437", "OEM United States", "cp850", NULL, MADB_CS_APPROX},
+ {"500", "IBM EBCDIC International", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"708", "Arabic (ASMO 708)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"709", "Arabic (ASMO-449+, BCON V4)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"710", "Transparent Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"720", "Arabic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"737", "Greek (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"775", "Baltic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"850", "Western European (DOS)", "cp850", NULL, MADB_CS_EXACT},
+ {"852", "Central European (DOS)", "cp852", NULL, MADB_CS_EXACT},
+ {"855", "Cyrillic (primarily Russian)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"857", "Turkish (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"858", "OEM Multilingual Latin 1 + Euro symbol", "cp850", NULL, MADB_CS_EXACT},
+ {"860", "Portuguese (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"861", "Icelandic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"862", "Hebrew (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"863", "French Canadian (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"864", "Arabic (864)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"865", "Nordic (DOS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"866", "Cyrillic (DOS)", "cp866", NULL, MADB_CS_EXACT},
+ {"869", "Greek, Modern (DOS)", "greek", NULL, MADB_CS_EXACT},
+ {"870", "IBM EBCDIC Multilingual Latin 2", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"874", "Thai (Windows)", "tis620", NULL, MADB_CS_UNSUPPORTED},
+ {"875", "Greek Modern", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"932", "Japanese (Shift-JIS)", "cp932", NULL, MADB_CS_EXACT},
+ {"936", "Chinese Simplified (GB2312)", "gbk", NULL, MADB_CS_EXACT},
+ {"949", "ANSI/OEM Korean (Unified Hangul Code)", "euckr", NULL, MADB_CS_EXACT},
+ {"950", "Chinese Traditional (Big5)", "big5", NULL, MADB_CS_EXACT},
+ {"1026", "EBCDIC Turkish (Latin 5)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1047", "EBCDIC Latin 1/Open System", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1140", "IBM EBCDIC (US-Canada-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1141", "IBM EBCDIC (Germany-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1142", "IBM EBCDIC (Denmark-Norway-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1143", "IBM EBCDIC (Finland-Sweden-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1144", "IBM EBCDIC (Italy-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1145", "IBM EBCDIC (Spain-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1146", "IBM EBCDIC (UK-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1147", "IBM EBCDIC (France-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1148", "IBM EBCDIC (International-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1149", "IBM EBCDIC (Icelandic-Euro)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1200", "UTF-16, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1201", "UTF-16, big endian byte order", "utf16", NULL, MADB_CS_UNSUPPORTED},
+ {"1250", "Central European (Windows)", "cp1250", NULL, MADB_CS_EXACT},
+ {"1251", "Cyrillic (Windows)", "cp1251", NULL, MADB_CS_EXACT},
+ {"1252", "Western European (Windows)", "latin1", NULL, MADB_CS_EXACT},
+ {"1253", "Greek (Windows)", "greek", NULL, MADB_CS_EXACT},
+ {"1254", "Turkish (Windows)", "latin5", NULL, MADB_CS_EXACT},
+ {"1255", "Hebrew (Windows)", "hebrew", NULL, MADB_CS_EXACT},
+ {"1256", "Arabic (Windows)", "cp1256", NULL, MADB_CS_EXACT},
+ {"1257", "Baltic (Windows)","cp1257", NULL, MADB_CS_EXACT},
+ {"1258", "Vietnamese (Windows)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"1361", "Korean (Johab)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10000", "Western European (Mac)", "macroman", NULL, MADB_CS_EXACT},
+ {"10001", "Japanese (Mac)", "sjis", NULL, MADB_CS_EXACT},
+ {"10002", "Chinese Traditional (Mac)", "big5", NULL, MADB_CS_EXACT},
+ {"10003", "Korean (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10004", "Arabic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10005", "Hebrew (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10006", "Greek (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10007", "Cyrillic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10008", "Chinese Simplified (Mac)", "gb2312", NULL, MADB_CS_EXACT},
+ {"10010", "Romanian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10017", "Ukrainian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10021", "Thai (Mac)", "tis620", NULL, MADB_CS_EXACT},
+ {"10029", "Central European (Mac)", "macce", NULL, MADB_CS_EXACT},
+ {"10079", "Icelandic (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10081", "Turkish (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"10082", "Croatian (Mac)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"12000", "Unicode UTF-32, little endian byte order", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"12001", "Unicode UTF-32, big endian byte order", "utf32", NULL, MADB_CS_UNSUPPORTED},
+ {"20000", "Chinese Traditional (CNS)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20001", "TCA Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20002", "Chinese Traditional (Eten)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20003", "IBM5550 Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20004", "TeleText Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20005", "Wang Taiwan", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20105", "Western European (IA5)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20106", "IA5 German (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20107", "Swedish (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20108", "Norwegian (7-bit)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20127", "US-ASCII (7-bit)", "ascii", NULL, MADB_CS_EXACT},
+ {"20261", "T.61", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20269", "Non-Spacing Accent", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20273", "EBCDIC Germany", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20277", "EBCDIC Denmark-Norway", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20278", "EBCDIC Finland-Sweden", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20280", "EBCDIC Italy", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20284", "EBCDIC Latin America-Spain", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20285", "EBCDIC United Kingdom", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20290", "EBCDIC Japanese Katakana Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20297", "EBCDIC France", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20420", "EBCDIC Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20423", "EBCDIC Greek", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20424", "EBCDIC Hebrew", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20833", "EBCDIC Korean Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20838", "EBCDIC Thai", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20866", "Cyrillic (KOI8-R)", "koi8r", NULL, MADB_CS_EXACT},
+ {"20871", "EBCDIC Icelandic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20880", "EBCDIC Cyrillic Russian", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20905", "EBCDIC Turkish", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20924", "EBCDIC Latin 1/Open System (1047 + Euro symbol)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"20932", "Japanese (JIS 0208-1990 and 0121-1990)", "ujis", NULL, MADB_CS_EXACT},
+ {"20936", "Chinese Simplified (GB2312-80)", "gb2312", NULL, MADB_CS_APPROX},
+ {"20949", "Korean Wansung", "euckr", NULL, MADB_CS_APPROX},
+ {"21025", "EBCDIC Cyrillic Serbian-Bulgarian", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"21866", "Cyrillic (KOI8-U)", "koi8u", NULL, MADB_CS_EXACT},
+ {"28591", "Western European (ISO)", "latin1", NULL, MADB_CS_APPROX},
+ {"28592", "Central European (ISO)", "latin2", NULL, MADB_CS_EXACT},
+ {"28593", "Latin 3", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28594", "Baltic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28595", "ISO 8859-5 Cyrillic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28596", "ISO 8859-6 Arabic", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"28597", "ISO 8859-7 Greek", "greek", NULL, MADB_CS_EXACT},
+ {"28598", "Hebrew (ISO-Visual)", "hebrew", NULL, MADB_CS_EXACT},
+ {"28599", "ISO 8859-9 Turkish", "latin5", NULL, MADB_CS_EXACT},
+ {"28603", "ISO 8859-13 Estonian", "latin7", NULL, MADB_CS_EXACT},
+ {"28605", "8859-15 Latin 9", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"29001", "Europa 3", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"38598", "ISO 8859-8 Hebrew; Hebrew (ISO-Logical)", "hebrew", NULL, MADB_CS_EXACT},
+ {"50220", "ISO 2022 Japanese with no halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50221", "ISO 2022 Japanese with halfwidth Katakana", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50222", "ISO 2022 Japanese JIS X 0201-1989", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50225", "ISO 2022 Korean", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50227", "ISO 2022 Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50229", "ISO 2022 Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50930", "EBCDIC Japanese (Katakana) Extended", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50931", "EBCDIC US-Canada and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50933", "EBCDIC Korean Extended and Korean", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50935", "EBCDIC Simplified Chinese Extended and Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50936", "EBCDIC Simplified Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50937", "EBCDIC US-Canada and Traditional Chinese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"50939", "EBCDIC Japanese (Latin) Extended and Japanese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"51932", "EUC Japanese", "ujis", NULL, MADB_CS_EXACT},
+ {"51936", "EUC Simplified Chinese; Chinese Simplified (EUC)", "gb2312", NULL, MADB_CS_EXACT},
+ {"51949", "EUC Korean", "euckr", NULL, MADB_CS_EXACT},
+ {"51950", "EUC Traditional Chinese", "big5", NULL, MADB_CS_EXACT},
+ {"52936", "Chinese Simplified (HZ)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"54936", "Chinese Simplified (GB18030)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57002", "ISCII Devanagari", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57003", "ISCII Bengali", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57004", "ISCII Tamil", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57005", "ISCII Telugu", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57006", "ISCII Assamese", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57007", "ISCII Oriya", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57008", "ISCII Kannada", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57009", "ISCII Malayalam", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57010", "ISCII Gujarati", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"57011", "ISCII Punjabi", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"65000", "utf-7 Unicode (UTF-7)", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"65001", "utf-8 Unicode (UTF-8)", "utf8", NULL, MADB_CS_EXACT},
+ /* non Windows */
+#else
+ /* iconv encodings */
+ {"ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX},
+ {"US-ASCII", "US-ASCII", "ascii", "ASCII", MADB_CS_APPROX},
+ {"Big5", "Chinese for Taiwan Multi-byte set", "big5", "BIG5", MADB_CS_EXACT},
+ {"CP866", "IBM 866", "cp866", "CP866", MADB_CS_EXACT},
+ {"IBM-1252", "Catalan Spain", "cp1252", "CP1252", MADB_CS_EXACT},
+ {"ISCII-DEV", "Hindi", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"ISO-8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO_8859-1", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO88591", "ISO-8859-1", "latin1", "ISO_8859-1", MADB_CS_APPROX},
+ {"ISO-8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO_8859-13", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO885913", "ISO-8859-13", "latin7", "ISO_8859-13", MADB_CS_EXACT},
+ {"ISO-8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-15", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO885915", "ISO-8859-15", "latin9", "ISO_8859-15", MADB_CS_UNSUPPORTED},
+ {"ISO-8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO_8859-2", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO88592", "ISO-8859-2", "latin2", "ISO_8859-2", MADB_CS_EXACT},
+ {"ISO-8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO_8859-7", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO88597", "ISO-8859-7", "greek", "ISO_8859-7", MADB_CS_EXACT},
+ {"ISO-8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO_8859-8", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO88598", "ISO-8859-8", "hebrew", "ISO_8859-8", MADB_CS_EXACT},
+ {"ISO-8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO_8859-9", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO88599", "ISO-8859-9", "latin5", "ISO_8859-9", MADB_CS_EXACT},
+ {"ISO-8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-4", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO88594", "ISO-8859-4", NULL, "ISO_8859-4", MADB_CS_UNSUPPORTED},
+ {"ISO-8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO_8859-5", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"ISO88595", "ISO-8859-5", NULL, "ISO_8859-5", MADB_CS_UNSUPPORTED},
+ {"KOI8-R", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT},
+ {"koi8r", "KOI8-R", "koi8r", "KOI8R", MADB_CS_EXACT},
+ {"KOI8-U", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT},
+ {"koi8u", "KOI8-U", "koi8u", "KOI8U", MADB_CS_EXACT},
+ {"koi8t", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED},
+ {"KOI8-T", "KOI8-T", NULL, "KOI8-T", MADB_CS_UNSUPPORTED},
+ {"SJIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT},
+ {"Shift-JIS", "SHIFT_JIS", "sjis", "SJIS", MADB_CS_EXACT},
+ {"ansi1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT},
+ {"cp1251", "Cyrillic", "cp1251", "CP1251", MADB_CS_EXACT},
+ {"armscii8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT},
+ {"armscii-8", "Armenian", "armscii8", "ASMSCII-8", MADB_CS_EXACT},
+ {"big5hkscs", "Big5-HKSCS", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"cp1255", "Hebrew", "cp1255", "CP1255", MADB_CS_EXACT},
+ {"eucCN", "GB-2312", "gb2312", "GB2312", MADB_CS_EXACT},
+ {"eucJP", "UJIS", "ujis", "UJIS", MADB_CS_EXACT},
+ {"eucKR", "EUC-KR", "euckr", "EUCKR", MADB_CS_EXACT},
+ {"euctw", "EUC-TW", NULL, NULL, MADB_CS_UNSUPPORTED},
+ {"gb18030", "GB 18030-2000", "gb18030", "GB18030", MADB_CS_UNSUPPORTED},
+ {"gb2312", "GB2312", "gb2312", "GB2312", MADB_CS_EXACT},
+ {"gbk", "GBK", "gbk", "GBK", MADB_CS_EXACT},
+ {"georgianps", "Georgian", "geostd8", "GEORGIAN-PS", MADB_CS_EXACT},
+ {"utf8", "UTF8", "utf8", "UTF-8", MADB_CS_EXACT},
+ {"utf-8", "UTF8", "utf8", "UTF-8", MADB_CS_EXACT},
+#endif
+ {NULL, NULL, NULL, NULL, 0}
+};
+/* }}} */
+
+/* {{{ madb_get_os_character_set */
+const char *madb_get_os_character_set()
+{
+ unsigned int i= 0;
+ char *p= NULL;
+#ifdef _WIN32
+ char codepage[FN_REFLEN];
+ snprintf(codepage, FN_REFLEN, "%u", GetConsoleCP() ? GetConsoleCP() : GetACP());
+ p= codepage;
+#elif defined(HAVE_NL_LANGINFO) && defined(HAVE_SETLOCALE)
+ if (setlocale(LC_CTYPE, ""))
+ p= nl_langinfo(CODESET);
+#endif
+ if (!p)
+ return MADB_DEFAULT_CHARSET_NAME;
+ while (MADB_OS_CHARSET[i].identifier)
+ {
+ if (MADB_OS_CHARSET[i].supported > MADB_CS_UNSUPPORTED &&
+ strcasecmp(MADB_OS_CHARSET[i].identifier, p) == 0)
+ return MADB_OS_CHARSET[i].charset;
+ i++;
+ }
+ return MADB_DEFAULT_CHARSET_NAME;
+}
+/* }}} */
+
+/* {{{ madb_get_code_page */
+#ifdef _WIN32
+int madb_get_windows_cp(const char *charset)
+{
+ unsigned int i= 0;
+ while (MADB_OS_CHARSET[i].identifier)
+ {
+ if (MADB_OS_CHARSET[i].supported > MADB_CS_UNSUPPORTED &&
+ strcmp(MADB_OS_CHARSET[i].charset, charset) == 0)
+ return atoi(MADB_OS_CHARSET[i].identifier);
+ i++;
+ }
+ return -1;
+}
+#endif
+/* }}} */
+
+#ifdef HAVE_ICONV
+/* {{{ map_charset_name
+ 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 map_charset_name(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
+
+/* {{{ mariadb_convert_string
+ 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 mariadb_convert_string(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;
+ }
+
+ map_charset_name(to_cs->encoding, 1, to_encoding, sizeof(to_encoding));
+ map_charset_name(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_WIN(,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
+}
+/* }}} */
+
diff --git a/libmariadb/libmariadb/ma_client_plugin.c.in b/libmariadb/libmariadb/ma_client_plugin.c.in
new file mode 100644
index 00000000..6e0433b3
--- /dev/null
+++ b/libmariadb/libmariadb/ma_client_plugin.c.in
@@ -0,0 +1,503 @@
+/* Copyright (C) 2010 - 2012 Sergei Golubchik and Monty Program Ab
+ 2015-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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA */
+
+/**
+ @file
+
+ Support code for the client side (libmariadb) plugins
+
+ Client plugins are somewhat different from server plugins, they are simpler.
+
+ They do not need to be installed or in any way explicitly loaded on the
+ client, they are loaded automatically on demand.
+ One client plugin per shared object, soname *must* match the plugin name.
+
+ There is no reference counting and no unloading either.
+*/
+
+/* Silence warnings about variable 'unused' being used. */
+#define FORCE_INIT_OF_VARS 1
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_string.h>
+#include <ma_pthread.h>
+
+#include "errmsg.h"
+#include <mysql/client_plugin.h>
+
+#ifndef WIN32
+#include <dlfcn.h>
+#endif
+
+struct st_client_plugin_int {
+ struct st_client_plugin_int *next;
+ void *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+};
+
+static my_bool initialized= 0;
+static MA_MEM_ROOT mem_root;
+
+static uint valid_plugins[][2]= {
+ {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_PVIO_PLUGIN, MARIADB_CLIENT_PVIO_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_TRACE_PLUGIN, MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_REMOTEIO_PLUGIN, MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_CONNECTION_PLUGIN, MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION},
+ {MARIADB_CLIENT_COMPRESSION_PLUGIN, MARIADB_CLIENT_COMPRESSION_PLUGIN_INTERFACE_VERSION},
+ {0, 0}
+};
+
+/*
+ Loaded plugins are stored in a linked list.
+ The list is append-only, the elements are added to the head (like in a stack).
+ The elements are added under a mutex, but the list can be read and traversed
+ without any mutex because once an element is added to the list, it stays
+ there. The main purpose of a mutex is to prevent two threads from
+ loading the same plugin twice in parallel.
+*/
+
+
+struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS + MARIADB_CLIENT_MAX_PLUGINS];
+#ifdef THREAD
+static pthread_mutex_t LOCK_load_client_plugin;
+#endif
+
+@EXTERNAL_PLUGINS@
+
+struct st_mysql_client_plugin *mysql_client_builtins[]=
+{
+ @BUILTIN_PLUGINS@
+ 0
+};
+
+
+static int is_not_initialized(MYSQL *mysql, const char *name)
+{
+ if (initialized)
+ return 0;
+
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ name, "not initialized");
+ return 1;
+}
+
+static int get_plugin_nr(uint type)
+{
+ uint i= 0;
+ for(; valid_plugins[i][1]; i++)
+ if (valid_plugins[i][0] == type)
+ return i;
+ return -1;
+}
+
+static const char *check_plugin_version(struct st_mysql_client_plugin *plugin, unsigned int version)
+{
+ if (plugin->interface_version < version ||
+ (plugin->interface_version >> 8) > (version >> 8))
+ return "Incompatible client plugin interface";
+ return 0;
+}
+
+/**
+ finds a plugin in the list
+
+ @param name plugin name to search for
+ @param type plugin type
+
+ @note this does NOT necessarily need a mutex, take care!
+
+ @retval a pointer to a found plugin or 0
+*/
+static struct st_mysql_client_plugin *find_plugin(const char *name, int type)
+{
+ struct st_client_plugin_int *p;
+ int plugin_nr= get_plugin_nr(type);
+
+ DBUG_ASSERT(initialized);
+ if (plugin_nr == -1)
+ return 0;
+
+ if (!name)
+ return plugin_list[plugin_nr]->plugin;
+
+ for (p= plugin_list[plugin_nr]; p; p= p->next)
+ {
+ if (strcmp(p->plugin->name, name) == 0)
+ return p->plugin;
+ }
+ return NULL;
+}
+
+
+/**
+ verifies the plugin and adds it to the list
+
+ @param mysql MYSQL structure (for error reporting)
+ @param plugin plugin to install
+ @param dlhandle a handle to the shared object (returned by dlopen)
+ or 0 if the plugin was not dynamically loaded
+ @param argc number of arguments in the 'va_list args'
+ @param args arguments passed to the plugin initialization function
+
+ @retval a pointer to an installed plugin or 0
+*/
+
+static struct st_mysql_client_plugin *
+add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ struct st_client_plugin_int plugin_int, *p;
+ char errbuf[1024];
+ int plugin_nr;
+
+ DBUG_ASSERT(initialized);
+
+ plugin_int.plugin= plugin;
+ plugin_int.dlhandle= dlhandle;
+
+ if ((plugin_nr= get_plugin_nr(plugin->type)) == -1)
+ {
+ errmsg= "Unknown client plugin type";
+ goto err1;
+ }
+ if ((errmsg= check_plugin_version(plugin, valid_plugins[plugin_nr][1])))
+ goto err1;
+
+ /* Call the plugin initialization function, if any */
+ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
+ {
+ errmsg= errbuf;
+ goto err1;
+ }
+
+ p= (struct st_client_plugin_int *)
+ ma_memdup_root(&mem_root, (char *)&plugin_int, sizeof(plugin_int));
+
+ if (!p)
+ {
+ errmsg= "Out of memory";
+ goto err2;
+ }
+
+
+ p->next= plugin_list[plugin_nr];
+ plugin_list[plugin_nr]= p;
+
+ return plugin;
+
+err2:
+ if (plugin->deinit)
+ plugin->deinit();
+err1:
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg);
+ if (dlhandle)
+ (void)dlclose(dlhandle);
+ return NULL;
+}
+
+
+/**
+ Loads plugins which are specified in the environment variable
+ LIBMYSQL_PLUGINS.
+
+ Multiple plugins must be separated by semicolon. This function doesn't
+ return or log an error.
+
+ The function is be called by mysql_client_plugin_init
+
+ @todo
+ Support extended syntax, passing parameters to plugins, for example
+ LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
+ or
+ LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
+*/
+
+static void load_env_plugins(MYSQL *mysql)
+{
+ char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
+
+ if (ma_check_env_str(s))
+ return;
+
+ free_env= strdup(s);
+ plugs= s= free_env;
+
+ do {
+ if ((s= strchr(plugs, ';')))
+ *s= '\0';
+ mysql_load_plugin(mysql, plugs, -1, 0);
+ plugs= s + 1;
+ } while (s);
+
+ free(free_env);
+}
+
+/********** extern functions to be used by libmariadb *********************/
+
+/**
+ Initializes the client plugin layer.
+
+ This function must be called before any other client plugin function.
+
+ @retval 0 successful
+ @retval != 0 error occurred
+*/
+
+int mysql_client_plugin_init()
+{
+ MYSQL mysql;
+ struct st_mysql_client_plugin **builtin;
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (initialized)
+ return 0;
+
+ memset(&mysql, 0, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
+
+ pthread_mutex_init(&LOCK_load_client_plugin, NULL);
+ ma_init_alloc_root(&mem_root, 128, 128);
+
+ memset(&plugin_list, 0, sizeof(plugin_list));
+
+ initialized= 1;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+ for (builtin= mysql_client_builtins; *builtin; builtin++)
+ add_plugin(&mysql, *builtin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ load_env_plugins(&mysql);
+
+ return 0;
+}
+
+
+/**
+ Deinitializes the client plugin layer.
+
+ Unloades all client plugins and frees any associated resources.
+*/
+
+void mysql_client_plugin_deinit()
+{
+ int i;
+ struct st_client_plugin_int *p;
+
+ if (!initialized)
+ return;
+
+ for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
+ for (p= plugin_list[i]; p; p= p->next)
+ {
+ if (p->plugin->deinit)
+ p->plugin->deinit();
+ if (p->dlhandle)
+ (void)dlclose(p->dlhandle);
+ }
+
+ memset(&plugin_list, 0, sizeof(plugin_list));
+ initialized= 0;
+ ma_free_root(&mem_root, MYF(0));
+ pthread_mutex_destroy(&LOCK_load_client_plugin);
+}
+
+/************* public facing functions, for client consumption *********/
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_client_register_plugin(MYSQL *mysql,
+ struct st_mysql_client_plugin *plugin)
+{
+ struct st_mysql_client_plugin *found_plugin= NULL;
+ va_list unused;
+ LINT_INIT_STRUCT(unused);
+
+ if (is_not_initialized(mysql, plugin->name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (!(found_plugin= find_plugin(plugin->name, plugin->type)))
+ found_plugin= add_plugin(mysql, plugin, 0, 0, unused);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ return found_plugin;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
+ int argc, va_list args)
+{
+ const char *errmsg;
+#ifdef _WIN32
+ char errbuf[1024];
+#endif
+ char dlpath[FN_REFLEN+1];
+ void *sym, *dlhandle = NULL;
+ struct st_mysql_client_plugin *plugin;
+ char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR");
+
+ CLEAR_CLIENT_ERROR(mysql);
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (type >= 0 && find_plugin(name, type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ /* Compile dll path */
+#ifndef WIN32
+ snprintf(dlpath, sizeof(dlpath) - 1, "%s/%s%s",
+ mysql->options.extension && mysql->options.extension->plugin_dir ?
+ mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir :
+ MARIADB_PLUGINDIR, name, SO_EXT);
+#else
+ {
+ char *p= (mysql->options.extension && mysql->options.extension->plugin_dir) ?
+ mysql->options.extension->plugin_dir : env_plugin_dir;
+ snprintf(dlpath, sizeof(dlpath), "%s%s%s%s", p ? p : "", p ? "\\" : "", name, SO_EXT);
+ }
+#endif
+
+ if (strpbrk(name, "()[]!@#$%^&/*;.,'?\\"))
+ {
+ errmsg= "invalid plugin name";
+ goto err;
+ }
+
+
+ /* Open new dll handle */
+ if (!(dlhandle= dlopen((const char *)dlpath, RTLD_NOW)))
+ {
+#ifdef _WIN32
+ char winmsg[255];
+ size_t len;
+ winmsg[0] = 0;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ winmsg, 255, NULL);
+ len= strlen(winmsg);
+ while (len > 0 && (winmsg[len - 1] == '\n' || winmsg[len - 1] == '\r'))
+ len--;
+ if (len)
+ winmsg[len] = 0;
+ snprintf(errbuf, sizeof(errbuf), "%s Library path is '%s'", winmsg, dlpath);
+ errmsg= errbuf;
+#else
+ errmsg= dlerror();
+#endif
+ goto err;
+ }
+
+
+ if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
+ {
+ errmsg= "not a plugin";
+ (void)dlclose(dlhandle);
+ goto err;
+ }
+
+ plugin= (struct st_mysql_client_plugin*)sym;
+
+ if (type >=0 && type != plugin->type)
+ {
+ errmsg= "type mismatch";
+ goto err;
+ }
+
+ if (strcmp(name, plugin->name))
+ {
+ errmsg= "name mismatch";
+ goto err;
+ }
+
+ if (type < 0 && find_plugin(name, plugin->type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ return plugin;
+
+err:
+ if (dlhandle)
+ dlclose(dlhandle);
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
+ return NULL;
+}
+
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
+{
+ struct st_mysql_client_plugin *p;
+ va_list args;
+ va_start(args, argc);
+ p= mysql_load_plugin_v(mysql, name, type, argc, args);
+ va_end(args);
+ return p;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin * STDCALL
+mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
+{
+ struct st_mysql_client_plugin *p;
+ int plugin_nr= get_plugin_nr(type);
+
+ if (is_not_initialized(mysql, name))
+ return NULL;
+
+ if (plugin_nr == -1)
+ {
+ my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type");
+ }
+
+ if ((p= find_plugin(name, type)))
+ return p;
+
+ /* not found, load it */
+ return mysql_load_plugin(mysql, name, type, 0);
+}
+
diff --git a/libmariadb/libmariadb/ma_compress.c b/libmariadb/libmariadb/ma_compress.c
new file mode 100644
index 00000000..002246e9
--- /dev/null
+++ b/libmariadb/libmariadb/ma_compress.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016, 2022 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Initially Written by Sinisa Milivojevic <sinisa@coresinc.com> */
+
+#include <ma_global.h>
+#ifdef HAVE_COMPRESS
+#include <mysql.h>
+#include <ma_common.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+
+const char *compression_algorithms[] =
+{
+ "none",
+ "zlib",
+ "zstd",
+ "unknown"
+};
+
+const char *_mariadb_compression_algorithm_str(enum enum_ma_compression_algorithm algorithm)
+{
+ switch(algorithm) {
+ case COMPRESSION_NONE:
+ case COMPRESSION_ZLIB:
+ case COMPRESSION_ZSTD:
+ return compression_algorithms[algorithm] ;
+ default:
+ return compression_algorithms[COMPRESSION_UNKNOWN];
+ }
+}
+/*
+** This replaces the packet with a compressed packet
+** Returns 1 on error
+** *complen is 0 if the packet wasn't compressed
+*/
+
+my_bool _mariadb_compress(NET *net, unsigned char *packet, size_t *len, size_t *complen)
+{
+ if (*len < MIN_COMPRESS_LENGTH ||
+ !compression_plugin(net))
+ *complen=0;
+ else
+ {
+ unsigned char *compbuf=_mariadb_compress_alloc(net,packet,len,complen);
+ if (!compbuf)
+ return *complen ? 0 : 1;
+ memcpy(packet,compbuf,*len);
+ free(compbuf);
+ }
+ return 0;
+}
+
+unsigned char *_mariadb_compress_alloc(NET *net, const unsigned char *packet, size_t *len, size_t *complen)
+{
+ unsigned char *compbuf;
+ *complen = *len * 120 / 100 + 12;
+
+ if (!(compbuf = (unsigned char *) malloc(*complen)))
+ return 0; /* Not enough memory */
+
+ if (compression_plugin(net)->compress(compression_ctx(net), (void *)compbuf, complen, (void *)packet, *len))
+ {
+ free(compbuf);
+ return 0;
+ }
+
+ if (*complen >= *len)
+ {
+ *complen=0;
+ free(compbuf);
+ return 0;
+ }
+
+ swap(size_t,*len,*complen); /* *len is now packet length */
+ return compbuf;
+}
+
+my_bool _mariadb_uncompress (NET *net, unsigned char *packet, size_t *len, size_t *complen)
+{
+ if (*complen) /* If compressed */
+ {
+ unsigned char *compbuf = (unsigned char *) malloc (*complen);
+ if (!compbuf)
+ return 1; /* Not enough memory */
+ if (compression_plugin(net)->decompress(compression_ctx(net), compbuf, complen, packet, len))
+ { /* Probably wrong packet */
+ free(compbuf);
+ return 1;
+ }
+ *len = *complen;
+ memcpy(packet,compbuf,*len);
+ free(compbuf);
+ }
+ else *complen= *len;
+ return 0;
+}
+#endif /* HAVE_COMPRESS */
diff --git a/libmariadb/libmariadb/ma_context.c b/libmariadb/libmariadb/ma_context.c
new file mode 100644
index 00000000..222dbe28
--- /dev/null
+++ b/libmariadb/libmariadb/ma_context.c
@@ -0,0 +1,728 @@
+/*
+ Copyright 2011, 2012 Kristian Nielsen and Monty Program Ab
+ 2016 MariaDB Corporation AB
+
+ This file 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 General Public License
+ along with this. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Implementation of async context spawning using Posix ucontext and
+ swapcontext().
+*/
+
+#include "ma_global.h"
+#include "ma_string.h"
+#include "ma_context.h"
+
+#ifdef HAVE_VALGRIND
+#include <valgrind/valgrind.h>
+#endif
+
+#ifdef MY_CONTEXT_USE_UCONTEXT
+
+typedef void (*uc_func_t)(void);
+
+/*
+ The makecontext() only allows to pass integers into the created context :-(
+ We want to pass pointers, so we do it this kinda hackish way.
+ Anyway, it should work everywhere, and at least it does not break strict
+ aliasing.
+*/
+union pass_void_ptr_as_2_int {
+ int a[2];
+ void *p;
+};
+
+/*
+ We use old-style function definition here, as this is passed to
+ makecontext(). And the type of the makecontext() argument does not match
+ the actual type (as the actual type can differ from call to call).
+*/
+static void
+my_context_spawn_internal(int i0, int i1)
+{
+ int err;
+ struct my_context *c;
+ union pass_void_ptr_as_2_int u;
+
+ u.a[0]= i0;
+ u.a[1]= i1;
+ c= (struct my_context *)u.p;
+
+ (*c->user_func)(c->user_data);
+ c->active= 0;
+ err= setcontext(&c->base_context);
+ fprintf(stderr, "Aieie, setcontext() failed: %d (errno=%d)\n", err, errno);
+}
+
+
+int
+my_context_continue(struct my_context *c)
+{
+ int err;
+
+ if (!c->active)
+ return 0;
+
+ err= swapcontext(&c->base_context, &c->spawned_context);
+ if (err)
+ {
+ fprintf(stderr, "Aieie, swapcontext() failed: %d (errno=%d)\n",
+ err, errno);
+ return -1;
+ }
+
+ return c->active;
+}
+
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int err;
+ union pass_void_ptr_as_2_int u;
+
+ err= getcontext(&c->spawned_context);
+ if (err)
+ return -1;
+ c->spawned_context.uc_stack.ss_sp= c->stack;
+ c->spawned_context.uc_stack.ss_size= c->stack_size;
+ c->spawned_context.uc_link= NULL;
+ c->user_func= f;
+ c->user_data= d;
+ c->active= 1;
+ u.p= c;
+ makecontext(&c->spawned_context, (uc_func_t)my_context_spawn_internal, 2,
+ u.a[0], u.a[1]);
+
+ return my_context_continue(c);
+}
+
+
+int
+my_context_yield(struct my_context *c)
+{
+ int err;
+
+ if (!c->active)
+ return -1;
+
+ err= swapcontext(&c->spawned_context, &c->base_context);
+ if (err)
+ return -1;
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+#if SIZEOF_CHARP > SIZEOF_INT*2
+#error Error: Unable to store pointer in 2 ints on this architecture
+#endif
+
+ memset(c, 0, sizeof(*c));
+ if (!(c->stack= malloc(stack_size)))
+ return -1; /* Out of memory */
+ c->stack_size= stack_size;
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack)
+ {
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ free(c->stack);
+ }
+}
+
+#endif /* MY_CONTEXT_USE_UCONTEXT */
+
+
+#ifdef MY_CONTEXT_USE_X86_64_GCC_ASM
+/*
+ GCC-amd64 implementation of my_context.
+
+ This is slightly optimized in the common case where we never yield
+ (eg. fetch next row and it is already fully received in buffer). In this
+ case we do not need to restore registers at return (though we still need to
+ save them as we cannot know if we will yield or not in advance).
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ Layout of saved registers etc.
+ Since this is accessed through gcc inline assembler, it is simpler to just
+ use numbers than to try to define nice constants or structs.
+
+ 0 0 %rsp
+ 1 8 %rbp
+ 2 16 %rbx
+ 3 24 %r12
+ 4 32 %r13
+ 5 40 %r14
+ 6 48 %r15
+ 7 56 %rip for done
+ 8 64 %rip for yield/continue
+*/
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int ret;
+
+ /*
+ There are 6 callee-save registers we need to save and restore when
+ suspending and continuing, plus stack pointer %rsp and instruction pointer
+ %rip.
+
+ However, if we never suspend, the user-supplied function will in any case
+ restore the 6 callee-save registers, so we can avoid restoring them in
+ this case.
+ */
+ __asm__ __volatile__
+ (
+ "movq %%rsp, (%[save])\n\t"
+ "movq %[stack], %%rsp\n\t"
+#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
+ /*
+ This emits a DWARF DW_CFA_undefined directive to make the return address
+ undefined. This indicates that this is the top of the stack frame, and
+ helps tools that use DWARF stack unwinding to obtain stack traces.
+ (I use numeric constant to avoid a dependency on libdwarf includes).
+ */
+ ".cfi_escape 0x07, 16\n\t"
+#endif
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "leaq 1f(%%rip), %%rax\n\t"
+ "leaq 2f(%%rip), %%rcx\n\t"
+ "movq %%rax, 56(%[save])\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+ /*
+ Constraint below puts the argument to the user function into %rdi, as
+ needed for the calling convention.
+ */
+ "callq *%[f]\n\t"
+ "jmpq *56(%[save])\n"
+ /*
+ Come here when operation is done.
+ We do not need to restore callee-save registers, as the called function
+ will do this for us if needed.
+ */
+ "1:\n\t"
+ "movq (%[save]), %%rsp\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 3f\n"
+ /* Come here when operation was suspended. */
+ "2:\n\t"
+ "movl $1, %[ret]\n"
+ "3:\n"
+ : [ret] "=a" (ret),
+ [f] "+S" (f),
+ /* Need this in %rdi to follow calling convention. */
+ [d] "+D" (d)
+ : [stack] "a" (c->stack_top),
+ /* Need this in callee-save register to preserve in function call. */
+ [save] "b" (&c->save[0])
+ : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ int ret;
+
+ __asm__ __volatile__
+ (
+ "movq (%[save]), %%rax\n\t"
+ "movq %%rsp, (%[save])\n\t"
+ "movq %%rax, %%rsp\n\t"
+ "movq 8(%[save]), %%rax\n\t"
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rax, %%rbp\n\t"
+ "movq 24(%[save]), %%rax\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%rax, %%r12\n\t"
+ "movq 32(%[save]), %%rax\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%rax, %%r13\n\t"
+ "movq 40(%[save]), %%rax\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%rax, %%r14\n\t"
+ "movq 48(%[save]), %%rax\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "movq %%rax, %%r15\n\t"
+
+ "leaq 1f(%%rip), %%rax\n\t"
+ "leaq 2f(%%rip), %%rcx\n\t"
+ "movq %%rax, 56(%[save])\n\t"
+ "movq 64(%[save]), %%rax\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+
+ "movq 16(%[save]), %%rcx\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%rcx, %%rbx\n\t"
+
+ "jmpq *%%rax\n"
+ /*
+ Come here when operation is done.
+ Be sure to use the same callee-save register for %[save] here and in
+ my_context_spawn(), so we preserve the value correctly at this point.
+ */
+ "1:\n\t"
+ "movq (%[save]), %%rsp\n\t"
+ "movq 8(%[save]), %%rbp\n\t"
+ /* %rbx is preserved from my_context_spawn() in this case. */
+ "movq 24(%[save]), %%r12\n\t"
+ "movq 32(%[save]), %%r13\n\t"
+ "movq 40(%[save]), %%r14\n\t"
+ "movq 48(%[save]), %%r15\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 3f\n"
+ /* Come here when operation is suspended. */
+ "2:\n\t"
+ "movl $1, %[ret]\n"
+ "3:\n"
+ : [ret] "=a" (ret)
+ : /* Need this in callee-save register to preserve in function call. */
+ [save] "b" (&c->save[0])
+ : "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_yield(struct my_context *c)
+{
+ uint64_t *save= &c->save[0];
+ __asm__ __volatile__
+ (
+ "movq (%[save]), %%rax\n\t"
+ "movq %%rsp, (%[save])\n\t"
+ "movq %%rax, %%rsp\n\t"
+ "movq 8(%[save]), %%rax\n\t"
+ "movq %%rbp, 8(%[save])\n\t"
+ "movq %%rax, %%rbp\n\t"
+ "movq 16(%[save]), %%rax\n\t"
+ "movq %%rbx, 16(%[save])\n\t"
+ "movq %%rax, %%rbx\n\t"
+ "movq 24(%[save]), %%rax\n\t"
+ "movq %%r12, 24(%[save])\n\t"
+ "movq %%rax, %%r12\n\t"
+ "movq 32(%[save]), %%rax\n\t"
+ "movq %%r13, 32(%[save])\n\t"
+ "movq %%rax, %%r13\n\t"
+ "movq 40(%[save]), %%rax\n\t"
+ "movq %%r14, 40(%[save])\n\t"
+ "movq %%rax, %%r14\n\t"
+ "movq 48(%[save]), %%rax\n\t"
+ "movq %%r15, 48(%[save])\n\t"
+ "movq %%rax, %%r15\n\t"
+ "movq 64(%[save]), %%rax\n\t"
+ "leaq 1f(%%rip), %%rcx\n\t"
+ "movq %%rcx, 64(%[save])\n\t"
+
+ "jmpq *%%rax\n"
+
+ "1:\n"
+ : [save] "+D" (save)
+ :
+ : "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"
+ );
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+
+ if (!(c->stack_bot= malloc(stack_size)))
+ return -1; /* Out of memory */
+ /*
+ The x86_64 ABI specifies 16-byte stack alignment.
+ Also put two zero words at the top of the stack.
+ */
+ c->stack_top= (void *)
+ (( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
+ memset(c->stack_top, 0, 16);
+
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack_bot)
+ {
+ free(c->stack_bot);
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ }
+}
+
+#endif /* MY_CONTEXT_USE_X86_64_GCC_ASM */
+
+
+#ifdef MY_CONTEXT_USE_I386_GCC_ASM
+/*
+ GCC-i386 implementation of my_context.
+
+ This is slightly optimized in the common case where we never yield
+ (eg. fetch next row and it is already fully received in buffer). In this
+ case we do not need to restore registers at return (though we still need to
+ save them as we cannot know if we will yield or not in advance).
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ Layout of saved registers etc.
+ Since this is accessed through gcc inline assembler, it is simpler to just
+ use numbers than to try to define nice constants or structs.
+
+ 0 0 %esp
+ 1 4 %ebp
+ 2 8 %ebx
+ 3 12 %esi
+ 4 16 %edi
+ 5 20 %eip for done
+ 6 24 %eip for yield/continue
+*/
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ int ret;
+
+ /*
+ There are 4 callee-save registers we need to save and restore when
+ suspending and continuing, plus stack pointer %esp and instruction pointer
+ %eip.
+
+ However, if we never suspend, the user-supplied function will in any case
+ restore the 4 callee-save registers, so we can avoid restoring them in
+ this case.
+ */
+ __asm__ __volatile__
+ (
+ "movl %%esp, (%[save])\n\t"
+ "movl %[stack], %%esp\n\t"
+#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 && !defined(__INTEL_COMPILER)
+ /*
+ This emits a DWARF DW_CFA_undefined directive to make the return address
+ undefined. This indicates that this is the top of the stack frame, and
+ helps tools that use DWARF stack unwinding to obtain stack traces.
+ (I use numeric constant to avoid a dependency on libdwarf includes).
+ */
+ ".cfi_escape 0x07, 8\n\t"
+#endif
+ /* Push the parameter on the stack. */
+ "pushl %[d]\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ /* Get label addresses in -fPIC-compatible way (no pc-relative on 32bit) */
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%eax\n\t"
+ "addl $(2f-1b), %%eax\n\t"
+ "movl %%eax, 20(%[save])\n\t"
+ "addl $(3f-2f), %%eax\n\t"
+ "movl %%eax, 24(%[save])\n\t"
+ "call *%[f]\n\t"
+ "jmp *20(%[save])\n"
+ /*
+ Come here when operation is done.
+ We do not need to restore callee-save registers, as the called function
+ will do this for us if needed.
+ */
+ "2:\n\t"
+ "movl (%[save]), %%esp\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 4f\n"
+ /* Come here when operation was suspended. */
+ "3:\n\t"
+ "movl $1, %[ret]\n"
+ "4:\n"
+ : [ret] "=a" (ret),
+ [f] "+c" (f),
+ [d] "+d" (d)
+ : [stack] "a" (c->stack_top),
+ /* Need this in callee-save register to preserve across function call. */
+ [save] "D" (&c->save[0])
+ : "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ int ret;
+
+ __asm__ __volatile__
+ (
+ "movl (%[save]), %%eax\n\t"
+ "movl %%esp, (%[save])\n\t"
+ "movl %%eax, %%esp\n\t"
+ "movl 4(%[save]), %%eax\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%eax, %%ebp\n\t"
+ "movl 8(%[save]), %%eax\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%eax, %%ebx\n\t"
+ "movl 12(%[save]), %%eax\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%eax, %%esi\n\t"
+
+ "movl 24(%[save]), %%eax\n\t"
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%ecx\n\t"
+ "addl $(2f-1b), %%ecx\n\t"
+ "movl %%ecx, 20(%[save])\n\t"
+ "addl $(3f-2f), %%ecx\n\t"
+ "movl %%ecx, 24(%[save])\n\t"
+
+ /* Must restore %edi last as it is also our %[save] register. */
+ "movl 16(%[save]), %%ecx\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ "movl %%ecx, %%edi\n\t"
+
+ "jmp *%%eax\n"
+ /*
+ Come here when operation is done.
+ Be sure to use the same callee-save register for %[save] here and in
+ my_context_spawn(), so we preserve the value correctly at this point.
+ */
+ "2:\n\t"
+ "movl (%[save]), %%esp\n\t"
+ "movl 4(%[save]), %%ebp\n\t"
+ "movl 8(%[save]), %%ebx\n\t"
+ "movl 12(%[save]), %%esi\n\t"
+ "movl 16(%[save]), %%edi\n\t"
+ "xorl %[ret], %[ret]\n\t"
+ "jmp 4f\n"
+ /* Come here when operation is suspended. */
+ "3:\n\t"
+ "movl $1, %[ret]\n"
+ "4:\n"
+ : [ret] "=a" (ret)
+ : /* Need this in callee-save register to preserve in function call. */
+ [save] "D" (&c->save[0])
+ : "ecx", "edx", "memory", "cc"
+ );
+
+ return ret;
+}
+
+int
+my_context_yield(struct my_context *c)
+{
+ uint64_t *save= &c->save[0];
+ __asm__ __volatile__
+ (
+ "movl (%[save]), %%eax\n\t"
+ "movl %%esp, (%[save])\n\t"
+ "movl %%eax, %%esp\n\t"
+ "movl 4(%[save]), %%eax\n\t"
+ "movl %%ebp, 4(%[save])\n\t"
+ "movl %%eax, %%ebp\n\t"
+ "movl 8(%[save]), %%eax\n\t"
+ "movl %%ebx, 8(%[save])\n\t"
+ "movl %%eax, %%ebx\n\t"
+ "movl 12(%[save]), %%eax\n\t"
+ "movl %%esi, 12(%[save])\n\t"
+ "movl %%eax, %%esi\n\t"
+ "movl 16(%[save]), %%eax\n\t"
+ "movl %%edi, 16(%[save])\n\t"
+ "movl %%eax, %%edi\n\t"
+
+ "movl 24(%[save]), %%eax\n\t"
+ "call 1f\n"
+ "1:\n\t"
+ "popl %%ecx\n\t"
+ "addl $(2f-1b), %%ecx\n\t"
+ "movl %%ecx, 24(%[save])\n\t"
+
+ "jmp *%%eax\n"
+
+ "2:\n"
+ : [save] "+d" (save)
+ :
+ : "eax", "ecx", "memory", "cc"
+ );
+ return 0;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+ if (!(c->stack_bot= malloc(stack_size)))
+ return -1; /* Out of memory */
+ c->stack_top= (void *)
+ (( ((intptr)c->stack_bot + stack_size) & ~(intptr)0xf) - 16);
+ memset(c->stack_top, 0, 16);
+
+#ifdef HAVE_VALGRIND
+ c->valgrind_stack_id=
+ VALGRIND_STACK_REGISTER(c->stack_bot, c->stack_top);
+#endif
+ return 0;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->stack_bot)
+ {
+ free(c->stack_bot);
+#ifdef HAVE_VALGRIND
+ VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+ }
+}
+
+#endif /* MY_CONTEXT_USE_I386_GCC_ASM */
+
+
+#ifdef MY_CONTEXT_USE_WIN32_FIBERS
+int
+my_context_yield(struct my_context *c)
+{
+ c->return_value= 1;
+ SwitchToFiber(c->app_fiber);
+ return 0;
+}
+
+
+static void WINAPI
+my_context_trampoline(void *p)
+{
+ struct my_context *c= (struct my_context *)p;
+ /*
+ Reuse the Fiber by looping infinitely, each time we are scheduled we
+ spawn the appropriate function and switch back when it is done.
+
+ This way we avoid the overhead of CreateFiber() for every asynchronous
+ operation.
+ */
+ for(;;)
+ {
+ (*(c->user_func))(c->user_arg);
+ c->return_value= 0;
+ SwitchToFiber(c->app_fiber);
+ }
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ memset(c, 0, sizeof(*c));
+ c->lib_fiber= CreateFiber(stack_size, my_context_trampoline, c);
+ if (c->lib_fiber)
+ return 0;
+ return -1;
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+ if (c->lib_fiber)
+ {
+ DeleteFiber(c->lib_fiber);
+ c->lib_fiber= NULL;
+ }
+}
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ c->user_func= f;
+ c->user_arg= d;
+ return my_context_continue(c);
+}
+
+int
+my_context_continue(struct my_context *c)
+{
+ void *current_fiber= IsThreadAFiber() ? GetCurrentFiber() : ConvertThreadToFiber(c);
+ c->app_fiber= current_fiber;
+ SwitchToFiber(c->lib_fiber);
+ return c->return_value;
+}
+
+#endif /* MY_CONTEXT_USE_WIN32_FIBERS */
+
+#ifdef MY_CONTEXT_DISABLE
+int
+my_context_continue(struct my_context *c)
+{
+ return -1;
+}
+
+
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+ return -1;
+}
+
+
+int
+my_context_yield(struct my_context *c)
+{
+ return -1;
+}
+
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+ return -1; /* Out of memory */
+}
+
+void
+my_context_destroy(struct my_context *c)
+{
+}
+
+#endif
diff --git a/libmariadb/libmariadb/ma_decimal.c b/libmariadb/libmariadb/ma_decimal.c
new file mode 100644
index 00000000..de6062df
--- /dev/null
+++ b/libmariadb/libmariadb/ma_decimal.c
@@ -0,0 +1,479 @@
+/* Copyright (C) 2004 Sergei Golubchik
+
+ 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*/
+
+/*
+=======================================================================
+ NOTE: this library implements SQL standard "exact numeric" type
+ and is not at all generic, but rather intentinally crippled to
+ follow the standard :)
+=======================================================================
+ Quoting the standard
+ (SQL:2003, Part 2 Foundations, aka ISO/IEC 9075-2:2003)
+
+4.4.2 Characteristics of numbers, page 27:
+
+ An exact numeric type has a precision P and a scale S. P is a positive
+ integer that determines the number of significant digits in a
+ particular radix R, where R is either 2 or 10. S is a non-negative
+ integer. Every value of an exact numeric type of scale S is of the
+ form n*10^{-S}, where n is an integer such that ?-R^P <= n <= R^P.
+
+ [...]
+
+ If an assignment of some number would result in a loss of its most
+ significant digit, an exception condition is raised. If least
+ significant digits are lost, implementation-defined rounding or
+ truncating occurs, with no exception condition being raised.
+
+ [...]
+
+ Whenever an exact or approximate numeric value is assigned to an exact
+ numeric value site, an approximation of its value that preserves
+ leading significant digits after rounding or truncating is represented
+ in the declared type of the target. The value is converted to have the
+ precision and scale of the target. The choice of whether to truncate
+ or round is implementation-defined.
+
+ [...]
+
+ All numeric values between the smallest and the largest value,
+ inclusive, in a given exact numeric type have an approximation
+ obtained by rounding or truncation for that type; it is
+ implementation-defined which other numeric values have such
+ approximations.
+
+5.3 <literal>, page 143
+
+ <exact numeric literal> ::=
+ <unsigned integer> [ <period> [ <unsigned integer> ] ]
+ | <period> <unsigned integer>
+
+6.1 <data type>, page 165:
+
+ 19) The <scale> of an <exact numeric type> shall not be greater than
+ the <precision> of the <exact numeric type>.
+
+ 20) For the <exact numeric type>s DECIMAL and NUMERIC:
+
+ a) The maximum value of <precision> is implementation-defined.
+ <precision> shall not be greater than this value.
+ b) The maximum value of <scale> is implementation-defined. <scale>
+ shall not be greater than this maximum value.
+
+ 21) NUMERIC specifies the data type exact numeric, with the decimal
+ precision and scale specified by the <precision> and <scale>.
+
+ 22) DECIMAL specifies the data type exact numeric, with the decimal
+ scale specified by the <scale> and the implementation-defined
+ decimal precision equal to or greater than the value of the
+ specified <precision>.
+
+6.26 <numeric value expression>, page 241:
+
+ 1) If the declared type of both operands of a dyadic arithmetic
+ operator is exact numeric, then the declared type of the result is
+ an implementation-defined exact numeric type, with precision and
+ scale determined as follows:
+
+ a) Let S1 and S2 be the scale of the first and second operands
+ respectively.
+ b) The precision of the result of addition and subtraction is
+ implementation-defined, and the scale is the maximum of S1 and S2.
+ c) The precision of the result of multiplication is
+ implementation-defined, and the scale is S1 + S2.
+ d) The precision and scale of the result of division are
+ implementation-defined.
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h> /* for my_alloca */
+#include <ma_decimal.h>
+#include <mysql.h>
+#include <mariadb_rpl.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <malloc.h>
+#endif
+
+typedef decimal_digit dec1;
+typedef longlong dec2;
+
+#define unlikely(A) (A)
+#define DIG_PER_DEC1 9
+#define DIG_MASK 100000000
+#define DIG_BASE 1000000000
+#define DIG_BASE2 LL(1000000000000000000)
+#define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
+static const dec1 powers10[DIG_PER_DEC1+1]={
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
+
+#define sanity(d) DBUG_ASSERT((d)->len >0 && ((d)->buf[0] | \
+ (d)->buf[(d)->len-1] | 1))
+
+#define FIX_INTG_FRAC_ERROR(len, intg1, frac1, error) \
+ do \
+ { \
+ if (unlikely(intg1+frac1 > (len))) \
+ { \
+ if (unlikely(intg1 > (len))) \
+ { \
+ intg1=(len); \
+ frac1=0; \
+ error=E_DEC_OVERFLOW; \
+ } \
+ else \
+ { \
+ frac1=(len)-intg1; \
+ error=E_DEC_TRUNCATED; \
+ } \
+ } \
+ else \
+ error=E_DEC_OK; \
+ } while(0)
+
+#define ADD(to, from1, from2, carry) /* assume carry <= 1 */ \
+ do \
+ { \
+ dec1 a=(from1)+(from2)+(carry); \
+ if (((carry)= a >= DIG_BASE)) /* no division here! */ \
+ a-=DIG_BASE; \
+ (to)=a; \
+ } while(0)
+
+#define ADD2(to, from1, from2, carry) \
+ do \
+ { \
+ dec1 a=(from1)+(from2)+(carry); \
+ if (((carry)= a >= DIG_BASE)) \
+ a-=DIG_BASE; \
+ if (unlikely(a >= DIG_BASE)) \
+ { \
+ a-=DIG_BASE; \
+ carry++; \
+ } \
+ (to)=a; \
+ } while(0)
+
+#define SUB(to, from1, from2, carry) /* to=from1-from2 */ \
+ do \
+ { \
+ dec1 a=(from1)-(from2)-(carry); \
+ if (((carry)= a < 0)) \
+ a+=DIG_BASE; \
+ (to)=a; \
+ } while(0)
+
+#define SUB2(to, from1, from2, carry) /* to=from1-from2 */ \
+ do \
+ { \
+ dec1 a=(from1)-(from2)-(carry); \
+ if (((carry)= a < 0)) \
+ a+=DIG_BASE; \
+ if (unlikely(a < 0)) \
+ { \
+ a+=DIG_BASE; \
+ carry++; \
+ } \
+ (to)=a; \
+ } while(0)
+
+/*
+ Convert decimal to its printable string representation
+
+ SYNOPSIS
+ decimal2string()
+ from - value to convert
+ to - points to buffer where string representation should be stored
+ *to_len - in: size of to buffer
+ out: length of the actually written string
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+
+int decimal2string(decimal *from, char *to, int *to_len)
+{
+ int len, intg=from->intg, frac=from->frac, i;
+ int error=E_DEC_OK;
+ char *s=to;
+ dec1 *buf, *buf0=from->buf, tmp;
+
+ DBUG_ASSERT(*to_len >= 2+from->sign);
+
+ /* removing leading zeroes */
+ i=((intg-1) % DIG_PER_DEC1)+1;
+ while (intg > 0 && *buf0 == 0)
+ {
+ intg-=i;
+ i=DIG_PER_DEC1;
+ buf0++;
+ }
+ if (intg > 0)
+ {
+ for (i=(intg-1) % DIG_PER_DEC1; *buf0 < powers10[i--]; intg--) {}
+ DBUG_ASSERT(intg > 0);
+ }
+ else
+ intg=0;
+ if (unlikely(intg+frac==0))
+ {
+ intg=1;
+ tmp=0;
+ buf0=&tmp;
+ }
+
+ len= from->sign + intg + test(frac) + frac;
+ if (unlikely(len > --*to_len)) /* reserve one byte for \0 */
+ {
+ int i=len-*to_len;
+ error= (frac && i <= frac + 1) ? E_DEC_TRUNCATED : E_DEC_OVERFLOW;
+ if (frac && i >= frac + 1) i--;
+ if (i > frac)
+ {
+ intg-= i-frac;
+ frac= 0;
+ }
+ else
+ frac-=i;
+ len= from->sign + intg + test(frac) + frac;
+ }
+ *to_len=len;
+ s[len]=0;
+
+ if (from->sign)
+ *s++='-';
+
+ if (frac)
+ {
+ char *s1=s+intg;
+ buf=buf0+ROUND_UP(intg);
+ *s1++='.';
+ for (; frac>0; frac-=DIG_PER_DEC1)
+ {
+ dec1 x=*buf++;
+ for (i=min(frac, DIG_PER_DEC1); i; i--)
+ {
+ dec1 y=x/DIG_MASK;
+ *s1++='0'+(uchar)y;
+ x-=y*DIG_MASK;
+ x*=10;
+ }
+ }
+ }
+
+ s+=intg;
+ for (buf=buf0+ROUND_UP(intg); intg>0; intg-=DIG_PER_DEC1)
+ {
+ dec1 x=*--buf;
+ for (i=min(intg, DIG_PER_DEC1); i; i--)
+ {
+ dec1 y=x/10;
+ *--s='0'+(uchar)(x-y*10);
+ x=y;
+ }
+ }
+ return error;
+}
+
+/*
+ Convert string to decimal
+
+ SYNOPSIS
+ str2decl()
+ from - value to convert
+ to - decimal where where the result will be stored
+ to->buf and to->len must be set.
+ end - if not NULL, *end will be set to the char where
+ conversion ended
+ fixed - use to->intg, to->frac as limits for input number
+
+ NOTE
+ to->intg and to->frac can be modified even when fixed=1
+ (but only decreased, in this case)
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW/E_DEC_BAD_NUM/E_DEC_OOM
+*/
+
+/*
+ Convert decimal to its binary fixed-length representation
+ two representations of the same length can be compared with memcmp
+ with the correct -1/0/+1 result
+
+ SYNOPSIS
+ decimal2bin()
+ from - value to convert
+ to - points to buffer where string representation should be stored
+ precision/scale - see decimal_bin_size() below
+
+ NOTE
+ the buffer is assumed to be of the size decimal_bin_size(precision, scale)
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+
+/*
+ Restores decimal from its binary fixed-length representation
+
+ SYNOPSIS
+ bin2decimal()
+ from - value to convert
+ to - result
+ precision/scale - see decimal_bin_size() below
+
+ NOTE
+ see decimal2bin()
+ the buffer is assumed to be of the size decimal_bin_size(precision, scale)
+
+ RETURN VALUE
+ E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW
+*/
+
+int bin2decimal(const char *from, decimal *to, int precision, int scale)
+{
+ int error=E_DEC_OK,
+ intg= precision - scale,
+ intg0= intg / DIG_PER_DEC1,
+ frac0= scale / DIG_PER_DEC1,
+ intg0x= intg - intg0 * DIG_PER_DEC1,
+ frac0x= scale - frac0*DIG_PER_DEC1,
+ intg1= intg0 + (intg0x > 0),
+ frac1= frac0 + (frac0x > 0),
+ tmp_size= decimal_bin_size(precision, scale);
+ char *tmp;
+ dec1 *buf= to->buf,
+ mask=(*from & 0x80) ? 0 : -1;
+ char *stop;
+
+ /* Initial implementation from Sergei modified "from" buffer, (which errored
+ in binlog api when verifying checksum), so we declare from as read only and use
+ a stack buffer instead */
+ tmp= (char *)alloca(tmp_size);
+ memcpy(tmp, from, tmp_size);
+ *tmp^= 0x80; /* remove sign bit */
+ from= tmp;
+
+ sanity(to);
+
+ FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error);
+ if (unlikely(error))
+ {
+ if (intg1 < intg0+(intg0x>0))
+ {
+ from+= dig2bytes[intg0x] + sizeof(dec1)*(intg0 - intg1);
+ frac0= frac0x= intg0x= 0;
+ intg0= intg1;
+ }
+ else
+ {
+ frac0x= 0;
+ frac0= frac1;
+ }
+ }
+
+ to->sign= (mask != 0);
+ to->intg= intg0 * DIG_PER_DEC1 + intg0x;
+ to->frac= frac0 * DIG_PER_DEC1 + frac0x;
+
+ if (intg0x)
+ {
+ int i= dig2bytes[intg0x];
+ dec1 x= 0;
+ switch (i)
+ {
+ case 1: x=myisam_sint1korr(from); break;
+ case 2: x=myisam_sint2korr(from); break;
+ case 3: x=myisam_sint3korr(from); break;
+ case 4: x=myisam_sint4korr(from); break;
+ default: DBUG_ASSERT(0); x= 0;
+ }
+ from+=i;
+ *buf=x ^ mask;
+ if (buf > to->buf || *buf != 0)
+ buf++;
+ else
+ to->intg-=intg0x;
+ }
+ for (stop=(char *)from+intg0*sizeof(dec1); from < stop; from+=sizeof(dec1))
+ {
+ DBUG_ASSERT(sizeof(dec1) == 4);
+ *buf=myisam_sint4korr(from) ^ mask;
+ if (buf > to->buf || *buf != 0)
+ buf++;
+ else
+ to->intg-=DIG_PER_DEC1;
+ }
+ DBUG_ASSERT(to->intg >=0);
+ for (stop=(char *)from+frac0*sizeof(dec1); from < stop; from+=sizeof(dec1))
+ {
+ DBUG_ASSERT(sizeof(dec1) == 4);
+ *buf=myisam_sint4korr(from) ^ mask;
+ buf++;
+ }
+ if (frac0x)
+ {
+ int i=dig2bytes[frac0x];
+ dec1 x= 0;
+ switch (i)
+ {
+ case 1: x=myisam_sint1korr(from); break;
+ case 2: x=myisam_sint2korr(from); break;
+ case 3: x=myisam_sint3korr(from); break;
+ case 4: x=myisam_sint4korr(from); break;
+ default: DBUG_ASSERT(0); x= 0;
+ }
+ *buf= (x ^ mask) * powers10[DIG_PER_DEC1 - frac0x];
+ buf++;
+ }
+ return error;
+}
+
+/*
+ Returns the size of array to hold a decimal with given precision and scale
+
+ RETURN VALUE
+ size in dec1
+ (multiply by sizeof(dec1) to get the size if bytes)
+*/
+
+int decimal_size(int precision, int scale)
+{
+ DBUG_ASSERT(scale >= 0 && precision > 0 && scale <= precision);
+ return ROUND_UP(precision-scale)+ROUND_UP(scale);
+}
+
+/*
+ Returns the size of array to hold a binary representation of a decimal
+
+ RETURN VALUE
+ size in bytes
+*/
+
+int decimal_bin_size(int precision, int scale)
+{
+ int intg=precision-scale,
+ intg0=intg/DIG_PER_DEC1, frac0=scale/DIG_PER_DEC1,
+ intg0x=intg-intg0*DIG_PER_DEC1, frac0x=scale-frac0*DIG_PER_DEC1;
+
+ DBUG_ASSERT(scale >= 0 && precision > 0 && scale <= precision);
+ return intg0*sizeof(dec1)+dig2bytes[intg0x]+
+ frac0*sizeof(dec1)+dig2bytes[frac0x];
+}
diff --git a/libmariadb/libmariadb/ma_default.c b/libmariadb/libmariadb/ma_default.c
new file mode 100644
index 00000000..0f6921c1
--- /dev/null
+++ b/libmariadb/libmariadb/ma_default.c
@@ -0,0 +1,370 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "ma_string.h"
+#include <ctype.h>
+#include "mariadb_ctype.h"
+#include <mysql.h>
+#include <ma_common.h>
+#include <mariadb/ma_io.h>
+
+#ifdef _WIN32
+#include <io.h>
+#include "shlwapi.h"
+
+#define access _access
+
+static const char *ini_exts[]= {"ini", "cnf", 0};
+#define R_OK 4
+#else
+#include <unistd.h>
+static const char *ini_exts[]= {"cnf", 0};
+#endif
+
+char **configuration_dirs= NULL;
+#define MAX_CONFIG_DIRS 6
+
+my_bool _mariadb_read_options(MYSQL *mysql,
+ const char *config_dir,
+ const char *config_file,
+ const char *group,
+ unsigned int recursion);
+
+static int add_cfg_dir(char **cfg_dirs, const char *directory)
+{
+ int i;
+
+ for (i = 0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++)
+ if (!strcmp(cfg_dirs[i], directory)) /* already present */
+ return 0;
+
+ if (i < MAX_CONFIG_DIRS) {
+ cfg_dirs[i]= strdup(directory);
+ return 0;
+ }
+ return 1;
+}
+
+void release_configuration_dirs()
+{
+ if (configuration_dirs)
+ {
+ int i= 0;
+ while (configuration_dirs[i])
+ free(configuration_dirs[i++]);
+ free(configuration_dirs);
+ }
+}
+
+char **get_default_configuration_dirs()
+{
+#ifdef _WIN32
+ char dirname[FN_REFLEN];
+#endif
+ char *env;
+
+ configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *));
+ if (!configuration_dirs)
+ goto end;
+
+#ifdef _WIN32
+ /* On Windows operating systems configuration files are stored in
+ 1. System Windows directory
+ 2. System directory
+ 3. Windows directory
+ 4. C:\
+ */
+
+ if (!GetSystemWindowsDirectory(dirname, FN_REFLEN) ||
+ add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+
+ if (!GetWindowsDirectory(dirname, FN_REFLEN) ||
+ add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+
+ if (add_cfg_dir(configuration_dirs, "C:"))
+ goto error;
+
+ if (GetModuleFileName(NULL, dirname, FN_REFLEN))
+ {
+ PathRemoveFileSpec(dirname);
+ if (add_cfg_dir(configuration_dirs, dirname))
+ goto error;
+ }
+#else
+ /* on *nix platforms configuration files are stored in
+ 1. SYSCONFDIR (if build happens inside server package, or
+ -DDEFAULT_SYSCONFDIR was specified
+ 2. /etc
+ 3. /etc/mysql
+ */
+#ifdef DEFAULT_SYSCONFDIR
+ if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR))
+ goto error;
+#else
+ if (add_cfg_dir(configuration_dirs, "/etc"))
+ goto error;
+ if (add_cfg_dir(configuration_dirs, "/etc/mysql"))
+ goto error;
+#endif
+#endif
+ /* CONC-537: Read configuration files from MYSQL_HOME directory only if
+ MARIADB_HOME was not set */
+ if (!(env= getenv("MARIADB_HOME")))
+ env= getenv("MYSQL_HOME");
+ if (env && add_cfg_dir(configuration_dirs, env))
+ goto error;
+end:
+ return configuration_dirs;
+error:
+ return NULL;
+}
+
+extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value);
+
+static my_bool is_group(char *ptr, const char **groups)
+{
+ while (*groups)
+ {
+ if (!strcmp(ptr, *groups))
+ return 1;
+ groups++;
+ }
+ return 0;
+}
+
+static my_bool _mariadb_read_options_from_file(MYSQL *mysql,
+ const char *config_file,
+ const char *group,
+ unsigned int recursion)
+{
+ my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0;
+ char buff[4096],*ptr,*end,*value, *key= 0, *optval;
+ MA_FILE *file= NULL;
+ my_bool rc= 1;
+ const char *groups[5]= {"client",
+ "client-server",
+ "client-mariadb",
+ group,
+ NULL};
+ my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value);
+
+
+ /* if a plugin registered a hook we will call this hook, otherwise
+ * default (_mariadb_set_conf_option) will be called */
+ if (mysql->options.extension && mysql->options.extension->set_option)
+ set_option= mysql->options.extension->set_option;
+ else
+ set_option= _mariadb_set_conf_option;
+
+ if (!(file = ma_open(config_file, "r", NULL)))
+ goto err;
+
+ while (ma_gets(buff,sizeof(buff)-1,file))
+ {
+ key= 0;
+ /* Ignore comment and empty lines */
+ for (ptr=buff ; isspace(*ptr) ; ptr++ );
+ if (!is_escaped && (*ptr == '\"' || *ptr== '\''))
+ {
+ is_quoted= !is_quoted;
+ continue;
+ }
+ /* CONC- 327: !includedir and !include */
+ if (*ptr == '!')
+ {
+ char *val;
+ ptr++;
+ if (!(val= strchr(ptr, ' ')))
+ continue;
+ *val++= 0;
+ end= strchr(val, 0);
+ for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
+ *end= 0;
+ if (!strcmp(ptr, "includedir"))
+ _mariadb_read_options(mysql, (const char *)val, NULL, group, recursion + 1);
+ else if (!strcmp(ptr, "include"))
+ _mariadb_read_options(mysql, NULL, (const char *)val, group, recursion + 1);
+ continue;
+ }
+ if (*ptr == '#' || *ptr == ';' || !*ptr)
+ continue;
+ is_escaped= (*ptr == '\\');
+ if (*ptr == '[') /* Group name */
+ {
+ found_group=1;
+ if (!(end=(char *) strchr(++ptr,']')))
+ {
+ /* todo: set error */
+ goto err;
+ }
+ for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */
+ end[0]=0;
+ read_values= is_group(ptr, groups);
+ continue;
+ }
+ if (!found_group)
+ {
+ /* todo: set error */
+ goto err;
+ }
+ if (!read_values)
+ continue;
+ if (!(end=value=strchr(ptr,'=')))
+ {
+ end=strchr(ptr, '\0'); /* Option without argument */
+ set_option(mysql, ptr, NULL);
+ }
+ if (!key)
+ key= ptr;
+ for ( ; isspace(end[-1]) ; end--) ;
+ *end= 0;
+ if (value)
+ {
+ /* Remove pre- and end space */
+ char *value_end;
+ *value= 0;
+ value++;
+ ptr= value;
+ for ( ; isspace(*value); value++) ;
+ value_end=strchr(value, '\0');
+ *value_end= 0;
+ optval= ptr;
+ for ( ; isspace(value_end[-1]) ; value_end--) ;
+ /* remove possible quotes */
+ if (*value == '\'' || *value == '\"')
+ {
+ value++;
+ if (value_end[-1] == '\'' || value_end[-1] == '\"')
+ value_end--;
+ }
+ if (value_end < value) /* Empty string */
+ value_end=value;
+ for ( ; value != value_end; value++)
+ {
+ if (*value == '\\' && value != value_end-1)
+ {
+ switch(*++value) {
+ case 'n':
+ *ptr++='\n';
+ break;
+ case 't':
+ *ptr++= '\t';
+ break;
+ case 'r':
+ *ptr++ = '\r';
+ break;
+ case 'b':
+ *ptr++ = '\b';
+ break;
+ case 's':
+ *ptr++= ' '; /* space */
+ break;
+ case '\"':
+ *ptr++= '\"';
+ break;
+ case '\'':
+ *ptr++= '\'';
+ break;
+ case '\\':
+ *ptr++= '\\';
+ break;
+ default: /* Unknown; Keep '\' */
+ *ptr++= '\\';
+ *ptr++= *value;
+ break;
+ }
+ }
+ else
+ *ptr++= *value;
+ }
+ *ptr=0;
+ set_option(mysql, key, optval);
+ key= optval= 0;
+ }
+ }
+ rc= 0;
+
+err:
+ if (file)
+ ma_close(file);
+ return rc;
+}
+
+
+my_bool _mariadb_read_options(MYSQL *mysql,
+ const char *config_dir,
+ const char *config_file,
+ const char *group,
+ unsigned int recursion)
+{
+ int i= 0,
+ exts,
+ errors= 0;
+ char filename[FN_REFLEN + 1];
+ unsigned int recursion_stop= 64;
+#ifndef _WIN32
+ char *env;
+#endif
+
+ if (recursion >= recursion_stop)
+ return 1;
+
+ if (config_file && config_file[0])
+ return _mariadb_read_options_from_file(mysql, config_file, group, recursion);
+
+ if (config_dir && config_dir[0])
+ {
+ for (exts= 0; ini_exts[exts]; exts++)
+ {
+ snprintf(filename, FN_REFLEN,
+ "%s%cmy.%s", config_dir, FN_LIBCHAR, ini_exts[exts]);
+ if (!access(filename, R_OK))
+ errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
+ }
+ return errors;
+ }
+
+ for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++)
+ {
+ for (exts= 0; ini_exts[exts]; exts++)
+ {
+ snprintf(filename, FN_REFLEN,
+ "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]);
+ if (!access(filename, R_OK))
+ errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
+ }
+ }
+#ifndef _WIN32
+ /* special case: .my.cnf in Home directory */
+ if ((env= getenv("HOME")))
+ {
+ for (exts= 0; ini_exts[exts]; exts++)
+ {
+ snprintf(filename, FN_REFLEN,
+ "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]);
+ if (!access(filename, R_OK))
+ errors+= _mariadb_read_options_from_file(mysql, filename, group, recursion);
+ }
+ }
+#endif
+ return errors;
+}
diff --git a/libmariadb/libmariadb/ma_dtoa.c b/libmariadb/libmariadb/ma_dtoa.c
new file mode 100644
index 00000000..05ab0f0b
--- /dev/null
+++ b/libmariadb/libmariadb/ma_dtoa.c
@@ -0,0 +1,1924 @@
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
+ 2016,2022 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; version 2
+ of the License.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/****************************************************************
+
+ This file incorporates work covered by the following copyright and
+ permission notice:
+
+ The author of this software is David M. Gay.
+
+ Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+
+ Permission to use, copy, modify, and distribute this software for any
+ purpose without fee is hereby granted, provided that this entire notice
+ is included in all copies of any software which is or includes a copy
+ or modification of this software and in all copies of the supporting
+ documentation for such software.
+
+ THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+
+ ***************************************************************/
+
+//#include "strings_def.h"
+//#include <my_base.h> /* for EOVERFLOW on Windows */
+#include <ma_global.h>
+#include <memory.h>
+#include "ma_string.h"
+
+/**
+ Appears to suffice to not call malloc() in most cases.
+ @todo
+ see if it is possible to get rid of malloc().
+ this constant is sufficient to avoid malloc() on all inputs I have tried.
+*/
+#define DTOA_BUFF_SIZE (460 * sizeof(void *))
+
+/* Magic value returned by dtoa() to indicate overflow */
+#define DTOA_OVERFLOW 9999
+
+static char *dtoa(double, int, int, int *, int *, char **, char *, size_t);
+static void dtoa_free(char *, char *, size_t);
+
+/**
+ @brief
+ Converts a given floating point number to a zero-terminated string
+ representation using the 'f' format.
+
+ @details
+ This function is a wrapper around dtoa() to do the same as
+ sprintf(to, "%-.*f", precision, x), though the conversion is usually more
+ precise. The only difference is in handling [-,+]infinity and nan values,
+ in which case we print '0\0' to the output string and indicate an overflow.
+
+ @param x the input floating point number.
+ @param precision the number of digits after the decimal point.
+ All properties of sprintf() apply:
+ - if the number of significant digits after the decimal
+ point is less than precision, the resulting string is
+ right-padded with zeros
+ - if the precision is 0, no decimal point appears
+ - if a decimal point appears, at least one digit appears
+ before it
+ @param to pointer to the output buffer. The longest string which
+ my_fcvt() can return is FLOATING_POINT_BUFFER bytes
+ (including the terminating '\0').
+ @param error if not NULL, points to a location where the status of
+ conversion is stored upon return.
+ FALSE successful conversion
+ TRUE the input number is [-,+]infinity or nan.
+ The output string in this case is always '0'.
+ @return number of written characters (excluding terminating '\0')
+*/
+
+size_t ma_fcvt(double x, int precision, char *to, my_bool *error)
+{
+ int decpt, sign, len, i;
+ char *res, *src, *end, *dst= to;
+ char buf[DTOA_BUFF_SIZE];
+ DBUG_ASSERT(precision >= 0 && precision < NOT_FIXED_DEC && to != NULL);
+
+ res= dtoa(x, 5, precision, &decpt, &sign, &end, buf, sizeof(buf));
+
+ if (decpt == DTOA_OVERFLOW)
+ {
+ dtoa_free(res, buf, sizeof(buf));
+ *to++= '0';
+ *to= '\0';
+ if (error != NULL)
+ *error= TRUE;
+ return 1;
+ }
+
+ src= res;
+ len= (int)(end - src);
+
+ if (sign)
+ *dst++= '-';
+
+ if (decpt <= 0)
+ {
+ *dst++= '0';
+ *dst++= '.';
+ for (i= decpt; i < 0; i++)
+ *dst++= '0';
+ }
+
+ for (i= 1; i <= len; i++)
+ {
+ *dst++= *src++;
+ if (i == decpt && i < len)
+ *dst++= '.';
+ }
+ while (i++ <= decpt)
+ *dst++= '0';
+
+ if (precision > 0)
+ {
+ if (len <= decpt)
+ *dst++= '.';
+
+ for (i= precision - MAX(0, (len - decpt)); i > 0; i--)
+ *dst++= '0';
+ }
+
+ *dst= '\0';
+ if (error != NULL)
+ *error= FALSE;
+
+ dtoa_free(res, buf, sizeof(buf));
+
+ return dst - to;
+}
+
+/**
+ @brief
+ Converts a given floating point number to a zero-terminated string
+ representation with a given field width using the 'e' format
+ (aka scientific notation) or the 'f' one.
+
+ @details
+ The format is chosen automatically to provide the most number of significant
+ digits (and thus, precision) with a given field width. In many cases, the
+ result is similar to that of sprintf(to, "%g", x) with a few notable
+ differences:
+ - the conversion is usually more precise than C library functions.
+ - there is no 'precision' argument. instead, we specify the number of
+ characters available for conversion (i.e. a field width).
+ - the result never exceeds the specified field width. If the field is too
+ short to contain even a rounded decimal representation, ma_gcvt()
+ indicates overflow and truncates the output string to the specified width.
+ - float-type arguments are handled differently than double ones. For a
+ float input number (i.e. when the 'type' argument is MY_GCVT_ARG_FLOAT)
+ we deliberately limit the precision of conversion by FLT_DIG digits to
+ avoid garbage past the significant digits.
+ - unlike sprintf(), in cases where the 'e' format is preferred, we don't
+ zero-pad the exponent to save space for significant digits. The '+' sign
+ for a positive exponent does not appear for the same reason.
+
+ @param x the input floating point number.
+ @param type is either MY_GCVT_ARG_FLOAT or MY_GCVT_ARG_DOUBLE.
+ Specifies the type of the input number (see notes above).
+ @param width field width in characters. The minimal field width to
+ hold any number representation (albeit rounded) is 7
+ characters ("-Ne-NNN").
+ @param to pointer to the output buffer. The result is always
+ zero-terminated, and the longest returned string is thus
+ 'width + 1' bytes.
+ @param error if not NULL, points to a location where the status of
+ conversion is stored upon return.
+ FALSE successful conversion
+ TRUE the input number is [-,+]infinity or nan.
+ The output string in this case is always '0'.
+ @return number of written characters (excluding terminating '\0')
+
+ @todo
+ Check if it is possible and makes sense to do our own rounding on top of
+ dtoa() instead of calling dtoa() twice in (rare) cases when the resulting
+ string representation does not fit in the specified field width and we want
+ to re-round the input number with fewer significant digits. Examples:
+
+ ma_gcvt(-9e-3, ..., 4, ...);
+ ma_gcvt(-9e-3, ..., 2, ...);
+ ma_gcvt(1.87e-3, ..., 4, ...);
+ ma_gcvt(55, ..., 1, ...);
+
+ We do our best to minimize such cases by:
+
+ - passing to dtoa() the field width as the number of significant digits
+
+ - removing the sign of the number early (and decreasing the width before
+ passing it to dtoa())
+
+ - choosing the proper format to preserve the most number of significant
+ digits.
+*/
+
+size_t ma_gcvt(double x, my_gcvt_arg_type type, int width, char *to,
+ my_bool *error)
+{
+ int decpt, sign, len, exp_len;
+ char *res, *src, *end, *dst= to, *dend= dst + width;
+ char buf[DTOA_BUFF_SIZE];
+ my_bool have_space, force_e_format;
+ DBUG_ASSERT(width > 0 && to != NULL);
+
+ /* We want to remove '-' from equations early */
+ if (x < 0.)
+ width--;
+
+ res= dtoa(x, 4, type == MY_GCVT_ARG_DOUBLE ? width : MIN(width, FLT_DIG),
+ &decpt, &sign, &end, buf, sizeof(buf));
+ if (decpt == DTOA_OVERFLOW)
+ {
+ dtoa_free(res, buf, sizeof(buf));
+ *to++= '0';
+ *to= '\0';
+ if (error != NULL)
+ *error= TRUE;
+ return 1;
+ }
+
+ if (error != NULL)
+ *error= FALSE;
+
+ src= res;
+ len= (int)(end - res);
+
+ /*
+ Number of digits in the exponent from the 'e' conversion.
+ The sign of the exponent is taken into account separately, we don't need
+ to count it here.
+ */
+ exp_len= 1 + (decpt >= 101 || decpt <= -99) + (decpt >= 11 || decpt <= -9);
+
+ /*
+ Do we have enough space for all digits in the 'f' format?
+ Let 'len' be the number of significant digits returned by dtoa,
+ and F be the length of the resulting decimal representation.
+ Consider the following cases:
+ 1. decpt <= 0, i.e. we have "0.NNN" => F = len - decpt + 2
+ 2. 0 < decpt < len, i.e. we have "NNN.NNN" => F = len + 1
+ 3. len <= decpt, i.e. we have "NNN00" => F = decpt
+ */
+ have_space= (decpt <= 0 ? len - decpt + 2 :
+ decpt > 0 && decpt < len ? len + 1 :
+ decpt) <= width;
+ /*
+ The following is true when no significant digits can be placed with the
+ specified field width using the 'f' format, and the 'e' format
+ will not be truncated.
+ */
+ force_e_format= (decpt <= 0 && width <= 2 - decpt && width >= 3 + exp_len);
+ /*
+ Assume that we don't have enough space to place all significant digits in
+ the 'f' format. We have to choose between the 'e' format and the 'f' one
+ to keep as many significant digits as possible.
+ Let E and F be the lengths of decimal representation in the 'e' and 'f'
+ formats, respectively. We want to use the 'f' format if, and only if F <= E.
+ Consider the following cases:
+ 1. decpt <= 0.
+ F = len - decpt + 2 (see above)
+ E = len + (len > 1) + 1 + 1 (decpt <= -99) + (decpt <= -9) + 1
+ ("N.NNe-MMM")
+ (F <= E) <=> (len == 1 && decpt >= -1) || (len > 1 && decpt >= -2)
+ We also need to ensure that if the 'f' format is chosen,
+ the field width allows us to place at least one significant digit
+ (i.e. width > 2 - decpt). If not, we prefer the 'e' format.
+ 2. 0 < decpt < len
+ F = len + 1 (see above)
+ E = len + 1 + 1 + ... ("N.NNeMMM")
+ F is always less than E.
+ 3. len <= decpt <= width
+ In this case we have enough space to represent the number in the 'f'
+ format, so we prefer it with some exceptions.
+ 4. width < decpt
+ The number cannot be represented in the 'f' format at all, always use
+ the 'e' 'one.
+ */
+ if ((have_space ||
+ /*
+ Not enough space, let's see if the 'f' format provides the most number
+ of significant digits.
+ */
+ ((decpt <= width && (decpt >= -1 || (decpt == -2 &&
+ (len > 1 || !force_e_format)))) &&
+ !force_e_format)) &&
+
+ /*
+ Use the 'e' format in some cases even if we have enough space for the
+ 'f' one. See comment for DBL_DIG.
+ */
+ (!have_space || (decpt >= -DBL_DIG + 1 &&
+ (decpt <= DBL_DIG || len > decpt))))
+ {
+ /* 'f' format */
+ int i;
+
+ width-= (decpt < len) + (decpt <= 0 ? 1 - decpt : 0);
+
+ /* Do we have to truncate any digits? */
+ if (width < len)
+ {
+ if (width < decpt)
+ {
+ if (error != NULL)
+ *error= TRUE;
+ width= decpt;
+ }
+
+ /*
+ We want to truncate (len - width) least significant digits after the
+ decimal point. For this we are calling dtoa with mode=5, passing the
+ number of significant digits = (len-decpt) - (len-width) = width-decpt
+ */
+ dtoa_free(res, buf, sizeof(buf));
+ res= dtoa(x, 5, width - decpt, &decpt, &sign, &end, buf, sizeof(buf));
+ src= res;
+ len= (int)(end - res);
+ }
+
+ if (len == 0)
+ {
+ /* Underflow. Just print '0' and exit */
+ *dst++= '0';
+ goto end;
+ }
+
+ /*
+ At this point we are sure we have enough space to put all digits
+ returned by dtoa
+ */
+ if (sign && dst < dend)
+ *dst++= '-';
+ if (decpt <= 0)
+ {
+ if (dst < dend)
+ *dst++= '0';
+ if (len > 0 && dst < dend)
+ *dst++= '.';
+ for (; decpt < 0 && dst < dend; decpt++)
+ *dst++= '0';
+ }
+
+ for (i= 1; i <= len && dst < dend; i++)
+ {
+ *dst++= *src++;
+ if (i == decpt && i < len && dst < dend)
+ *dst++= '.';
+ }
+ while (i++ <= decpt && dst < dend)
+ *dst++= '0';
+ }
+ else
+ {
+ /* 'e' format */
+ int decpt_sign= 0;
+
+ if (--decpt < 0)
+ {
+ decpt= -decpt;
+ width--;
+ decpt_sign= 1;
+ }
+ width-= 1 + exp_len; /* eNNN */
+
+ if (len > 1)
+ width--;
+
+ if (width <= 0)
+ {
+ /* Overflow */
+ if (error != NULL)
+ *error= TRUE;
+ width= 0;
+ }
+
+ /* Do we have to truncate any digits? */
+ if (width < len)
+ {
+ /* Yes, re-convert with a smaller width */
+ dtoa_free(res, buf, sizeof(buf));
+ res= dtoa(x, 4, width, &decpt, &sign, &end, buf, sizeof(buf));
+ src= res;
+ len= (int)(end - res);
+ if (--decpt < 0)
+ decpt= -decpt;
+ }
+ /*
+ At this point we are sure we have enough space to put all digits
+ returned by dtoa
+ */
+ if (sign && dst < dend)
+ *dst++= '-';
+ if (dst < dend)
+ *dst++= *src++;
+ if (len > 1 && dst < dend)
+ {
+ *dst++= '.';
+ while (src < end && dst < dend)
+ *dst++= *src++;
+ }
+ if (dst < dend)
+ *dst++= 'e';
+ if (decpt_sign && dst < dend)
+ *dst++= '-';
+
+ if (decpt >= 100 && dst < dend)
+ {
+ *dst++= decpt / 100 + '0';
+ decpt%= 100;
+ if (dst < dend)
+ *dst++= decpt / 10 + '0';
+ }
+ else if (decpt >= 10 && dst < dend)
+ *dst++= decpt / 10 + '0';
+ if (dst < dend)
+ *dst++= decpt % 10 + '0';
+
+ }
+
+end:
+ dtoa_free(res, buf, sizeof(buf));
+ *dst= '\0';
+
+ return dst - to;
+}
+
+/****************************************************************
+ *
+ * The author of this software is David M. Gay.
+ *
+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose without fee is hereby granted, provided that this entire notice
+ * is included in all copies of any software which is or includes a copy
+ * or modification of this software and in all copies of the supporting
+ * documentation for such software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ ***************************************************************/
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to "."). */
+
+/*
+ Original copy of the software is located at http://www.netlib.org/fp/dtoa.c
+ It was adjusted to serve MySQL server needs:
+ * strtod() was modified to not expect a zero-terminated string.
+ It now honors 'se' (end of string) argument as the input parameter,
+ not just as the output one.
+ * in dtoa(), in case of overflow/underflow/NaN result string now contains "0";
+ decpt is set to DTOA_OVERFLOW to indicate overflow.
+ * support for VAX, IBM mainframe and 16-bit hardware removed
+ * we always assume that 64-bit integer type is available
+ * support for Kernigan-Ritchie style headers (pre-ANSI compilers)
+ removed
+ * all gcc warnings ironed out
+ * we always assume multithreaded environment, so we had to change
+ memory allocation procedures to use stack in most cases;
+ malloc is used as the last resort.
+ * pow5mult rewritten to use pre-calculated pow5 list instead of
+ the one generated on the fly.
+*/
+
+
+/*
+ On a machine with IEEE extended-precision registers, it is
+ necessary to specify double-precision (53-bit) rounding precision
+ before invoking strtod or dtoa. If the machine uses (the equivalent
+ of) Intel 80x87 arithmetic, the call
+ _control87(PC_53, MCW_PC);
+ does this with many compilers. Whether this or another call is
+ appropriate depends on the compiler; for this to work, it may be
+ necessary to #include "float.h" or another system-dependent header
+ file.
+*/
+
+/*
+ #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ and dtoa should round accordingly.
+ #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3
+ and Honor_FLT_ROUNDS is not #defined.
+
+ TODO: check if we can get rid of the above two
+*/
+
+typedef int32 Long;
+typedef uint32 ULong;
+typedef int64 LLong;
+typedef uint64 ULLong;
+
+typedef union { double d; ULong L[2]; } U;
+
+#if defined(HAVE_BIGENDIAN) || \
+ (defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN))
+#define word0(x) ((x)->L[0])
+#define word1(x) ((x)->L[1])
+#else
+#define word0(x) ((x)->L[1])
+#define word1(x) ((x)->L[0])
+#endif
+
+#define dval(x) ((x)->d)
+
+/* #define P DBL_MANT_DIG */
+/* Ten_pmax= floor(P*log(2)/log(5)) */
+/* Bletch= (highest power of 2 < DBL_MAX_10_EXP) / 16 */
+/* Quick_max= floor((P-1)*log(FLT_RADIX)/log(10) - 1) */
+/* Int_max= floor(P*log(FLT_RADIX)/log(10) - 1) */
+
+#define Exp_shift 20
+#define Exp_shift1 20
+#define Exp_msk1 0x100000
+#define Exp_mask 0x7ff00000
+#define P 53
+#define Bias 1023
+#define Emin (-1022)
+#define Exp_1 0x3ff00000
+#define Exp_11 0x3ff00000
+#define Ebits 11
+#define Frac_mask 0xfffff
+#define Frac_mask1 0xfffff
+#define Ten_pmax 22
+#define Bletch 0x10
+#define Bndry_mask 0xfffff
+#define Bndry_mask1 0xfffff
+#define LSB 1
+#define Sign_bit 0x80000000
+#define Log2P 1
+#define Tiny1 1
+#define Quick_max 14
+#define Int_max 14
+
+#ifndef Flt_Rounds
+#ifdef FLT_ROUNDS
+#define Flt_Rounds FLT_ROUNDS
+#else
+#define Flt_Rounds 1
+#endif
+#endif /*Flt_Rounds*/
+
+#ifdef Honor_FLT_ROUNDS
+#define Rounding rounding
+#undef Check_FLT_ROUNDS
+#define Check_FLT_ROUNDS
+#else
+#define Rounding Flt_Rounds
+#endif
+
+#define rounded_product(a,b) ((a)*= (b))
+#define rounded_quotient(a,b) ((a)/= (b))
+
+#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
+#define Big1 0xffffffff
+#define FFFFFFFF 0xffffffffUL
+
+/* This is tested to be enough for dtoa */
+
+#define Kmax 15
+
+#define Bcopy(x,y) memcpy((char *)&(x)->sign, (char *)&(y)->sign, \
+ 2*sizeof(int) + (y)->wds*sizeof(ULong))
+
+/* Arbitrary-length integer */
+
+typedef struct Bigint
+{
+ union {
+ ULong *x; /* points right after this Bigint object */
+ struct Bigint *next; /* to maintain free lists */
+ } p;
+ int k; /* 2^k = maxwds */
+ int maxwds; /* maximum length in 32-bit words */
+ int sign; /* not zero if number is negative */
+ int wds; /* current length in 32-bit words */
+} Bigint;
+
+
+/* A simple stack-memory based allocator for Bigints */
+
+typedef struct Stack_alloc
+{
+ char *begin;
+ char *free;
+ char *end;
+ /*
+ Having list of free blocks lets us reduce maximum required amount
+ of memory from ~4000 bytes to < 1680 (tested on x86).
+ */
+ Bigint *freelist[Kmax+1];
+} Stack_alloc;
+
+
+/*
+ Try to allocate object on stack, and resort to malloc if all
+ stack memory is used. Ensure allocated objects to be aligned by the pointer
+ size in order to not break the alignment rules when storing a pointer to a
+ Bigint.
+*/
+
+static Bigint *Balloc(int k, Stack_alloc *alloc)
+{
+ Bigint *rv;
+ DBUG_ASSERT(k <= Kmax);
+ if (k <= Kmax && alloc->freelist[k])
+ {
+ rv= alloc->freelist[k];
+ alloc->freelist[k]= rv->p.next;
+ }
+ else
+ {
+ int x, len;
+
+ x= 1 << k;
+ len= MY_ALIGN(sizeof(Bigint) + x * sizeof(ULong), SIZEOF_CHARP);
+
+ if (alloc->free + len <= alloc->end)
+ {
+ rv= (Bigint*) alloc->free;
+ alloc->free+= len;
+ }
+ else
+ rv= (Bigint*) malloc(len);
+
+ rv->k= k;
+ rv->maxwds= x;
+ }
+ rv->sign= rv->wds= 0;
+ rv->p.x= (ULong*) (rv + 1);
+ return rv;
+}
+
+
+/*
+ If object was allocated on stack, try putting it to the free
+ list. Otherwise call free().
+*/
+
+static void Bfree(Bigint *v, Stack_alloc *alloc)
+{
+ char *gptr= (char*) v; /* generic pointer */
+ if (gptr < alloc->begin || gptr >= alloc->end)
+ free(gptr);
+ else if (v->k <= Kmax)
+ {
+ /*
+ Maintain free lists only for stack objects: this way we don't
+ have to bother with freeing lists in the end of dtoa;
+ heap should not be used normally anyway.
+ */
+ v->p.next= alloc->freelist[v->k];
+ alloc->freelist[v->k]= v;
+ }
+}
+
+
+/*
+ This is to place return value of dtoa in: tries to use stack
+ as well, but passes by free lists management and just aligns len by
+ the pointer size in order to not break the alignment rules when storing a
+ pointer to a Bigint.
+*/
+
+static char *dtoa_alloc(int i, Stack_alloc *alloc)
+{
+ char *rv;
+ int aligned_size= MY_ALIGN(i, SIZEOF_CHARP);
+ if (alloc->free + aligned_size <= alloc->end)
+ {
+ rv= alloc->free;
+ alloc->free+= aligned_size;
+ }
+ else
+ rv= malloc(i);
+ return rv;
+}
+
+
+/*
+ dtoa_free() must be used to free values s returned by dtoa()
+ This is the counterpart of dtoa_alloc()
+*/
+
+static void dtoa_free(char *gptr, char *buf, size_t buf_size)
+{
+ if (gptr < buf || gptr >= buf + buf_size)
+ free(gptr);
+}
+
+
+/* Bigint arithmetic functions */
+
+/* Multiply by m and add a */
+
+static Bigint *multadd(Bigint *b, int m, int a, Stack_alloc *alloc)
+{
+ int i, wds;
+ ULong *x;
+ ULLong carry, y;
+ Bigint *b1;
+
+ wds= b->wds;
+ x= b->p.x;
+ i= 0;
+ carry= a;
+ do
+ {
+ y= *x * (ULLong)m + carry;
+ carry= y >> 32;
+ *x++= (ULong)(y & FFFFFFFF);
+ }
+ while (++i < wds);
+ if (carry)
+ {
+ if (wds >= b->maxwds)
+ {
+ b1= Balloc(b->k+1, alloc);
+ Bcopy(b1, b);
+ Bfree(b, alloc);
+ b= b1;
+ }
+ b->p.x[wds++]= (ULong) carry;
+ b->wds= wds;
+ }
+ return b;
+}
+
+
+static int hi0bits(register ULong x)
+{
+ register int k= 0;
+
+ if (!(x & 0xffff0000))
+ {
+ k= 16;
+ x<<= 16;
+ }
+ if (!(x & 0xff000000))
+ {
+ k+= 8;
+ x<<= 8;
+ }
+ if (!(x & 0xf0000000))
+ {
+ k+= 4;
+ x<<= 4;
+ }
+ if (!(x & 0xc0000000))
+ {
+ k+= 2;
+ x<<= 2;
+ }
+ if (!(x & 0x80000000))
+ {
+ k++;
+ if (!(x & 0x40000000))
+ return 32;
+ }
+ return k;
+}
+
+
+static int lo0bits(ULong *y)
+{
+ register int k;
+ register ULong x= *y;
+
+ if (x & 7)
+ {
+ if (x & 1)
+ return 0;
+ if (x & 2)
+ {
+ *y= x >> 1;
+ return 1;
+ }
+ *y= x >> 2;
+ return 2;
+ }
+ k= 0;
+ if (!(x & 0xffff))
+ {
+ k= 16;
+ x>>= 16;
+ }
+ if (!(x & 0xff))
+ {
+ k+= 8;
+ x>>= 8;
+ }
+ if (!(x & 0xf))
+ {
+ k+= 4;
+ x>>= 4;
+ }
+ if (!(x & 0x3))
+ {
+ k+= 2;
+ x>>= 2;
+ }
+ if (!(x & 1))
+ {
+ k++;
+ x>>= 1;
+ if (!x)
+ return 32;
+ }
+ *y= x;
+ return k;
+}
+
+
+/* Convert integer to Bigint number */
+
+static Bigint *i2b(int i, Stack_alloc *alloc)
+{
+ Bigint *b;
+
+ b= Balloc(1, alloc);
+ b->p.x[0]= i;
+ b->wds= 1;
+ return b;
+}
+
+
+/* Multiply two Bigint numbers */
+
+static Bigint *mult(Bigint *a, Bigint *b, Stack_alloc *alloc)
+{
+ Bigint *c;
+ int k, wa, wb, wc;
+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;
+ ULong y;
+ ULLong carry, z;
+
+ if (a->wds < b->wds)
+ {
+ c= a;
+ a= b;
+ b= c;
+ }
+ k= a->k;
+ wa= a->wds;
+ wb= b->wds;
+ wc= wa + wb;
+ if (wc > a->maxwds)
+ k++;
+ c= Balloc(k, alloc);
+ for (x= c->p.x, xa= x + wc; x < xa; x++)
+ *x= 0;
+ xa= a->p.x;
+ xae= xa + wa;
+ xb= b->p.x;
+ xbe= xb + wb;
+ xc0= c->p.x;
+ for (; xb < xbe; xc0++)
+ {
+ if ((y= *xb++))
+ {
+ x= xa;
+ xc= xc0;
+ carry= 0;
+ do
+ {
+ z= *x++ * (ULLong)y + *xc + carry;
+ carry= z >> 32;
+ *xc++= (ULong) (z & FFFFFFFF);
+ }
+ while (x < xae);
+ *xc= (ULong) carry;
+ }
+ }
+ for (xc0= c->p.x, xc= xc0 + wc; wc > 0 && !*--xc; --wc) ;
+ c->wds= wc;
+ return c;
+}
+
+
+/*
+ Precalculated array of powers of 5: tested to be enough for
+ vasting majority of dtoa_r cases.
+*/
+
+static ULong powers5[]=
+{
+ 625UL,
+
+ 390625UL,
+
+ 2264035265UL, 35UL,
+
+ 2242703233UL, 762134875UL, 1262UL,
+
+ 3211403009UL, 1849224548UL, 3668416493UL, 3913284084UL, 1593091UL,
+
+ 781532673UL, 64985353UL, 253049085UL, 594863151UL, 3553621484UL,
+ 3288652808UL, 3167596762UL, 2788392729UL, 3911132675UL, 590UL,
+
+ 2553183233UL, 3201533787UL, 3638140786UL, 303378311UL, 1809731782UL,
+ 3477761648UL, 3583367183UL, 649228654UL, 2915460784UL, 487929380UL,
+ 1011012442UL, 1677677582UL, 3428152256UL, 1710878487UL, 1438394610UL,
+ 2161952759UL, 4100910556UL, 1608314830UL, 349175UL
+};
+
+
+static Bigint p5_a[]=
+{
+ /* { x } - k - maxwds - sign - wds */
+ { { powers5 }, 1, 1, 0, 1 },
+ { { powers5 + 1 }, 1, 1, 0, 1 },
+ { { powers5 + 2 }, 1, 2, 0, 2 },
+ { { powers5 + 4 }, 2, 3, 0, 3 },
+ { { powers5 + 7 }, 3, 5, 0, 5 },
+ { { powers5 + 12 }, 4, 10, 0, 10 },
+ { { powers5 + 22 }, 5, 19, 0, 19 }
+};
+
+#define P5A_MAX (sizeof(p5_a)/sizeof(*p5_a) - 1)
+
+static Bigint *pow5mult(Bigint *b, int k, Stack_alloc *alloc)
+{
+ Bigint *b1, *p5, *p51=NULL;
+ int i;
+ static int p05[3]= { 5, 25, 125 };
+ my_bool overflow= FALSE;
+
+ if ((i= k & 3))
+ b= multadd(b, p05[i-1], 0, alloc);
+
+ if (!(k>>= 2))
+ return b;
+ p5= p5_a;
+ for (;;)
+ {
+ if (k & 1)
+ {
+ b1= mult(b, p5, alloc);
+ Bfree(b, alloc);
+ b= b1;
+ }
+ if (!(k>>= 1))
+ break;
+ /* Calculate next power of 5 */
+ if (overflow)
+ {
+ p51= mult(p5, p5, alloc);
+ Bfree(p5, alloc);
+ p5= p51;
+ }
+ else if (p5 < p5_a + P5A_MAX)
+ ++p5;
+ else if (p5 == p5_a + P5A_MAX)
+ {
+ p5= mult(p5, p5, alloc);
+ overflow= TRUE;
+ }
+ }
+ if (p51)
+ Bfree(p51, alloc);
+ return b;
+}
+
+
+static Bigint *lshift(Bigint *b, int k, Stack_alloc *alloc)
+{
+ int i, k1, n, n1;
+ Bigint *b1;
+ ULong *x, *x1, *xe, z;
+
+ n= k >> 5;
+ k1= b->k;
+ n1= n + b->wds + 1;
+ for (i= b->maxwds; n1 > i; i<<= 1)
+ k1++;
+ b1= Balloc(k1, alloc);
+ x1= b1->p.x;
+ for (i= 0; i < n; i++)
+ *x1++= 0;
+ x= b->p.x;
+ xe= x + b->wds;
+ if (k&= 0x1f)
+ {
+ k1= 32 - k;
+ z= 0;
+ do
+ {
+ *x1++= *x << k | z;
+ z= *x++ >> k1;
+ }
+ while (x < xe);
+ if ((*x1= z))
+ ++n1;
+ }
+ else
+ do
+ *x1++= *x++;
+ while (x < xe);
+ b1->wds= n1 - 1;
+ Bfree(b, alloc);
+ return b1;
+}
+
+
+static int cmp(Bigint *a, Bigint *b)
+{
+ ULong *xa, *xa0, *xb, *xb0;
+ int i, j;
+
+ i= a->wds;
+ j= b->wds;
+ if (i-= j)
+ return i;
+ xa0= a->p.x;
+ xa= xa0 + j;
+ xb0= b->p.x;
+ xb= xb0 + j;
+ for (;;)
+ {
+ if (*--xa != *--xb)
+ return *xa < *xb ? -1 : 1;
+ if (xa <= xa0)
+ break;
+ }
+ return 0;
+}
+
+
+static Bigint *diff(Bigint *a, Bigint *b, Stack_alloc *alloc)
+{
+ Bigint *c;
+ int i, wa, wb;
+ ULong *xa, *xae, *xb, *xbe, *xc;
+ ULLong borrow, y;
+
+ i= cmp(a,b);
+ if (!i)
+ {
+ c= Balloc(0, alloc);
+ c->wds= 1;
+ c->p.x[0]= 0;
+ return c;
+ }
+ if (i < 0)
+ {
+ c= a;
+ a= b;
+ b= c;
+ i= 1;
+ }
+ else
+ i= 0;
+ c= Balloc(a->k, alloc);
+ c->sign= i;
+ wa= a->wds;
+ xa= a->p.x;
+ xae= xa + wa;
+ wb= b->wds;
+ xb= b->p.x;
+ xbe= xb + wb;
+ xc= c->p.x;
+ borrow= 0;
+ do
+ {
+ y= (ULLong)*xa++ - *xb++ - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *xc++= (ULong) (y & FFFFFFFF);
+ }
+ while (xb < xbe);
+ while (xa < xae)
+ {
+ y= *xa++ - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *xc++= (ULong) (y & FFFFFFFF);
+ }
+ while (!*--xc)
+ wa--;
+ c->wds= wa;
+ return c;
+}
+
+
+static Bigint *d2b(U *d, int *e, int *bits, Stack_alloc *alloc)
+{
+ Bigint *b;
+ int de, k;
+ ULong *x, y, z;
+ int i;
+#define d0 word0(d)
+#define d1 word1(d)
+
+ b= Balloc(1, alloc);
+ x= b->p.x;
+
+ z= d0 & Frac_mask;
+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */
+ if ((de= (int)(d0 >> Exp_shift)))
+ z|= Exp_msk1;
+ if ((y= d1))
+ {
+ if ((k= lo0bits(&y)))
+ {
+ x[0]= y | z << (32 - k);
+ z>>= (k == 32) ? (--k) : k;
+ }
+ else
+ x[0]= y;
+ i= b->wds= (x[1]= z) ? 2 : 1;
+ }
+ else
+ {
+ k= lo0bits(&z);
+ x[0]= z;
+ i= b->wds= 1;
+ k+= 32;
+ }
+ if (de)
+ {
+ *e= de - Bias - (P-1) + k;
+ *bits= P - k;
+ }
+ else
+ {
+ *e= de - Bias - (P-1) + 1 + k;
+ *bits= 32*i - hi0bits(x[i-1]);
+ }
+ return b;
+#undef d0
+#undef d1
+}
+
+
+static const double tens[] =
+{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22
+};
+
+static const double bigtens[]= { 1e16, 1e32, 1e64, 1e128, 1e256 };
+/*
+ The factor of 2^53 in tinytens[4] helps us avoid setting the underflow
+ flag unnecessarily. It leads to a song and dance at the end of strtod.
+*/
+#define Scale_Bit 0x10
+#define n_bigtens 5
+
+
+static int quorem(Bigint *b, Bigint *S)
+{
+ int n;
+ ULong *bx, *bxe, q, *sx, *sxe;
+ ULLong borrow, carry, y, ys;
+
+ n= S->wds;
+ if (b->wds < n)
+ return 0;
+ sx= S->p.x;
+ sxe= sx + --n;
+ bx= b->p.x;
+ bxe= bx + n;
+ q= *bxe / (*sxe + 1); /* ensure q <= true quotient */
+ if (q)
+ {
+ borrow= 0;
+ carry= 0;
+ do
+ {
+ ys= *sx++ * (ULLong)q + carry;
+ carry= ys >> 32;
+ y= *bx - (ys & FFFFFFFF) - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *bx++= (ULong) (y & FFFFFFFF);
+ }
+ while (sx <= sxe);
+ if (!*bxe)
+ {
+ bx= b->p.x;
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds= n;
+ }
+ }
+ if (cmp(b, S) >= 0)
+ {
+ q++;
+ borrow= 0;
+ carry= 0;
+ bx= b->p.x;
+ sx= S->p.x;
+ do
+ {
+ ys= *sx++ + carry;
+ carry= ys >> 32;
+ y= *bx - (ys & FFFFFFFF) - borrow;
+ borrow= y >> 32 & (ULong)1;
+ *bx++= (ULong) (y & FFFFFFFF);
+ }
+ while (sx <= sxe);
+ bx= b->p.x;
+ bxe= bx + n;
+ if (!*bxe)
+ {
+ while (--bxe > bx && !*bxe)
+ --n;
+ b->wds= n;
+ }
+ }
+ return q;
+}
+
+
+/*
+ dtoa for IEEE arithmetic (dmg): convert double to ASCII string.
+
+ Inspired by "How to Print Floating-Point Numbers Accurately" by
+ Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].
+
+ Modifications:
+ 1. Rather than iterating, we use a simple numeric overestimate
+ to determine k= floor(log10(d)). We scale relevant
+ quantities using O(log2(k)) rather than O(k) multiplications.
+ 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't
+ try to generate digits strictly left to right. Instead, we
+ compute with fewer bits and propagate the carry if necessary
+ when rounding the final digit up. This is often faster.
+ 3. Under the assumption that input will be rounded nearest,
+ mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.
+ That is, we allow equality in stopping tests when the
+ round-nearest rule will give the same floating-point value
+ as would satisfaction of the stopping test with strict
+ inequality.
+ 4. We remove common factors of powers of 2 from relevant
+ quantities.
+ 5. When converting floating-point integers less than 1e16,
+ we use floating-point arithmetic rather than resorting
+ to multiple-precision integers.
+ 6. When asked to produce fewer than 15 digits, we first try
+ to get by with floating-point arithmetic; we resort to
+ multiple-precision integer arithmetic only if we cannot
+ guarantee that the floating-point calculation has given
+ the correctly rounded result. For k requested digits and
+ "uniformly" distributed input, the probability is
+ something like 10^(k-15) that we must resort to the Long
+ calculation.
+ */
+
+static char *dtoa(double dd, int mode, int ndigits, int *decpt, int *sign,
+ char **rve, char *buf, size_t buf_size)
+{
+ /*
+ Arguments ndigits, decpt, sign are similar to those
+ of ecvt and fcvt; trailing zeros are suppressed from
+ the returned string. If not null, *rve is set to point
+ to the end of the return value. If d is +-Infinity or NaN,
+ then *decpt is set to DTOA_OVERFLOW.
+
+ mode:
+ 0 ==> shortest string that yields d when read in
+ and rounded to nearest.
+ 1 ==> like 0, but with Steele & White stopping rule;
+ e.g. with IEEE P754 arithmetic , mode 0 gives
+ 1e23 whereas mode 1 gives 9.999999999999999e22.
+ 2 ==> MAX(1,ndigits) significant digits. This gives a
+ return value similar to that of ecvt, except
+ that trailing zeros are suppressed.
+ 3 ==> through ndigits past the decimal point. This
+ gives a return value similar to that from fcvt,
+ except that trailing zeros are suppressed, and
+ ndigits can be negative.
+ 4,5 ==> similar to 2 and 3, respectively, but (in
+ round-nearest mode) with the tests of mode 0 to
+ possibly return a shorter string that rounds to d.
+ With IEEE arithmetic and compilation with
+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same
+ as modes 2 and 3 when FLT_ROUNDS != 1.
+ 6-9 ==> Debugging modes similar to mode - 4: don't try
+ fast floating-point estimate (if applicable).
+
+ Values of mode other than 0-9 are treated as mode 0.
+
+ Sufficient space is allocated to the return value
+ to hold the suppressed trailing zeros.
+ */
+
+ int bbits, b2, b5, be, dig, i, ieps, UNINIT_VAR(ilim), ilim0,
+ UNINIT_VAR(ilim1), j, j1, k, k0, k_check, leftright, m2, m5, s2, s5,
+ spec_case, try_quick;
+ Long L;
+ int denorm;
+ ULong x;
+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;
+ U d2, eps, u;
+ double ds;
+ char *s, *s0;
+#ifdef Honor_FLT_ROUNDS
+ int rounding;
+#endif
+ Stack_alloc alloc;
+
+ alloc.begin= alloc.free= buf;
+ alloc.end= buf + buf_size;
+ memset(alloc.freelist, 0, sizeof(alloc.freelist));
+
+ u.d= dd;
+ if (word0(&u) & Sign_bit)
+ {
+ /* set sign for everything, including 0's and NaNs */
+ *sign= 1;
+ word0(&u) &= ~Sign_bit; /* clear sign bit */
+ }
+ else
+ *sign= 0;
+
+ /* If infinity, set decpt to DTOA_OVERFLOW, if 0 set it to 1 */
+ /* coverity[assign_where_compare_meant] */
+ if (((word0(&u) & Exp_mask) == Exp_mask && (*decpt= DTOA_OVERFLOW)) ||
+ /* coverity[assign_where_compare_meant] */
+ (!dval(&u) && (*decpt= 1)))
+ {
+ /* Infinity, NaN, 0 */
+ char *res= (char*) dtoa_alloc(2, &alloc);
+ res[0]= '0';
+ res[1]= '\0';
+ if (rve)
+ *rve= res + 1;
+ return res;
+ }
+
+#ifdef Honor_FLT_ROUNDS
+ if ((rounding= Flt_Rounds) >= 2)
+ {
+ if (*sign)
+ rounding= rounding == 2 ? 0 : 2;
+ else
+ if (rounding != 2)
+ rounding= 0;
+ }
+#endif
+
+ b= d2b(&u, &be, &bbits, &alloc);
+ if ((i= (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1))))
+ {
+ dval(&d2)= dval(&u);
+ word0(&d2) &= Frac_mask1;
+ word0(&d2) |= Exp_11;
+
+ /*
+ log(x) ~=~ log(1.5) + (x-1.5)/1.5
+ log10(x) = log(x) / log(10)
+ ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))
+ log10(d)= (i-Bias)*log(2)/log(10) + log10(d2)
+
+ This suggests computing an approximation k to log10(d) by
+
+ k= (i - Bias)*0.301029995663981
+ + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );
+
+ We want k to be too large rather than too small.
+ The error in the first-order Taylor series approximation
+ is in our favor, so we just round up the constant enough
+ to compensate for any error in the multiplication of
+ (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,
+ and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,
+ adding 1e-13 to the constant term more than suffices.
+ Hence we adjust the constant term to 0.1760912590558.
+ (We could get a more accurate k by invoking log10,
+ but this is probably not worthwhile.)
+ */
+
+ i-= Bias;
+ denorm= 0;
+ }
+ else
+ {
+ /* d is denormalized */
+
+ i= bbits + be + (Bias + (P-1) - 1);
+ x= i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)
+ : word1(&u) << (32 - i);
+ dval(&d2)= x;
+ word0(&d2)-= 31*Exp_msk1; /* adjust exponent */
+ i-= (Bias + (P-1) - 1) + 1;
+ denorm= 1;
+ }
+ ds= (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;
+ k= (int)ds;
+ if (ds < 0. && ds != k)
+ k--; /* want k= floor(ds) */
+ k_check= 1;
+ if (k >= 0 && k <= Ten_pmax)
+ {
+ if (dval(&u) < tens[k])
+ k--;
+ k_check= 0;
+ }
+ j= bbits - i - 1;
+ if (j >= 0)
+ {
+ b2= 0;
+ s2= j;
+ }
+ else
+ {
+ b2= -j;
+ s2= 0;
+ }
+ if (k >= 0)
+ {
+ b5= 0;
+ s5= k;
+ s2+= k;
+ }
+ else
+ {
+ b2-= k;
+ b5= -k;
+ s5= 0;
+ }
+ if (mode < 0 || mode > 9)
+ mode= 0;
+
+#ifdef Check_FLT_ROUNDS
+ try_quick= Rounding == 1;
+#else
+ try_quick= 1;
+#endif
+
+ if (mode > 5)
+ {
+ mode-= 4;
+ try_quick= 0;
+ }
+ leftright= 1;
+ switch (mode) {
+ case 0:
+ case 1:
+ ilim= ilim1= -1;
+ i= 18;
+ ndigits= 0;
+ break;
+ case 2:
+ leftright= 0;
+ /* fall through */
+ case 4:
+ if (ndigits <= 0)
+ ndigits= 1;
+ ilim= ilim1= i= ndigits;
+ break;
+ case 3:
+ leftright= 0;
+ /* fall through */
+ case 5:
+ i= ndigits + k + 1;
+ ilim= i;
+ ilim1= i - 1;
+ if (i <= 0)
+ i= 1;
+ }
+ s= s0= dtoa_alloc(i, &alloc);
+
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1 && rounding != 1)
+ leftright= 0;
+#endif
+
+ if (ilim >= 0 && ilim <= Quick_max && try_quick)
+ {
+ /* Try to get by with floating-point arithmetic. */
+ i= 0;
+ dval(&d2)= dval(&u);
+ k0= k;
+ ilim0= ilim;
+ ieps= 2; /* conservative */
+ if (k > 0)
+ {
+ ds= tens[k&0xf];
+ j= k >> 4;
+ if (j & Bletch)
+ {
+ /* prevent overflows */
+ j&= Bletch - 1;
+ dval(&u)/= bigtens[n_bigtens-1];
+ ieps++;
+ }
+ for (; j; j>>= 1, i++)
+ {
+ if (j & 1)
+ {
+ ieps++;
+ ds*= bigtens[i];
+ }
+ }
+ dval(&u)/= ds;
+ }
+ else if ((j1= -k))
+ {
+ dval(&u)*= tens[j1 & 0xf];
+ for (j= j1 >> 4; j; j>>= 1, i++)
+ {
+ if (j & 1)
+ {
+ ieps++;
+ dval(&u)*= bigtens[i];
+ }
+ }
+ }
+ if (k_check && dval(&u) < 1. && ilim > 0)
+ {
+ if (ilim1 <= 0)
+ goto fast_failed;
+ ilim= ilim1;
+ k--;
+ dval(&u)*= 10.;
+ ieps++;
+ }
+ dval(&eps)= ieps*dval(&u) + 7.;
+ word0(&eps)-= (P-1)*Exp_msk1;
+ if (ilim == 0)
+ {
+ S= mhi= 0;
+ dval(&u)-= 5.;
+ if (dval(&u) > dval(&eps))
+ goto one_digit;
+ if (dval(&u) < -dval(&eps))
+ goto no_digits;
+ goto fast_failed;
+ }
+ if (leftright)
+ {
+ /* Use Steele & White method of only generating digits needed. */
+ dval(&eps)= 0.5/tens[ilim-1] - dval(&eps);
+ for (i= 0;;)
+ {
+ L= (Long) dval(&u);
+ dval(&u)-= L;
+ *s++= '0' + (int)L;
+ if (dval(&u) < dval(&eps))
+ goto ret1;
+ if (1. - dval(&u) < dval(&eps))
+ goto bump_up;
+ if (++i >= ilim)
+ break;
+ dval(&eps)*= 10.;
+ dval(&u)*= 10.;
+ }
+ }
+ else
+ {
+ /* Generate ilim digits, then fix them up. */
+ dval(&eps)*= tens[ilim-1];
+ for (i= 1;; i++, dval(&u)*= 10.)
+ {
+ L= (Long)(dval(&u));
+ if (!(dval(&u)-= L))
+ ilim= i;
+ *s++= '0' + (int)L;
+ if (i == ilim)
+ {
+ if (dval(&u) > 0.5 + dval(&eps))
+ goto bump_up;
+ else if (dval(&u) < 0.5 - dval(&eps))
+ {
+ while (*--s == '0');
+ s++;
+ goto ret1;
+ }
+ break;
+ }
+ }
+ }
+ fast_failed:
+ s= s0;
+ dval(&u)= dval(&d2);
+ k= k0;
+ ilim= ilim0;
+ }
+
+ /* Do we have a "small" integer? */
+
+ if (be >= 0 && k <= Int_max)
+ {
+ /* Yes. */
+ ds= tens[k];
+ if (ndigits < 0 && ilim <= 0)
+ {
+ S= mhi= 0;
+ if (ilim < 0 || dval(&u) <= 5*ds)
+ goto no_digits;
+ goto one_digit;
+ }
+ for (i= 1;; i++, dval(&u)*= 10.)
+ {
+ L= (Long)(dval(&u) / ds);
+ dval(&u)-= L*ds;
+#ifdef Check_FLT_ROUNDS
+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */
+ if (dval(&u) < 0)
+ {
+ L--;
+ dval(&u)+= ds;
+ }
+#endif
+ *s++= '0' + (int)L;
+ if (!dval(&u))
+ {
+ break;
+ }
+ if (i == ilim)
+ {
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ {
+ switch (rounding) {
+ case 0: goto ret1;
+ case 2: goto bump_up;
+ }
+ }
+#endif
+ dval(&u)+= dval(&u);
+ if (dval(&u) > ds || (dval(&u) == ds && L & 1))
+ {
+bump_up:
+ while (*--s == '9')
+ if (s == s0)
+ {
+ k++;
+ *s= '0';
+ break;
+ }
+ ++*s++;
+ }
+ break;
+ }
+ }
+ goto ret1;
+ }
+
+ m2= b2;
+ m5= b5;
+ mhi= mlo= 0;
+ if (leftright)
+ {
+ i = denorm ? be + (Bias + (P-1) - 1 + 1) : 1 + P - bbits;
+ b2+= i;
+ s2+= i;
+ mhi= i2b(1, &alloc);
+ }
+ if (m2 > 0 && s2 > 0)
+ {
+ i= m2 < s2 ? m2 : s2;
+ b2-= i;
+ m2-= i;
+ s2-= i;
+ }
+ if (b5 > 0)
+ {
+ if (leftright)
+ {
+ if (m5 > 0)
+ {
+ mhi= pow5mult(mhi, m5, &alloc);
+ b1= mult(mhi, b, &alloc);
+ Bfree(b, &alloc);
+ b= b1;
+ }
+ if ((j= b5 - m5))
+ b= pow5mult(b, j, &alloc);
+ }
+ else
+ b= pow5mult(b, b5, &alloc);
+ }
+ S= i2b(1, &alloc);
+ if (s5 > 0)
+ S= pow5mult(S, s5, &alloc);
+
+ /* Check for special case that d is a normalized power of 2. */
+
+ spec_case= 0;
+ if ((mode < 2 || leftright)
+#ifdef Honor_FLT_ROUNDS
+ && rounding == 1
+#endif
+ )
+ {
+ if (!word1(&u) && !(word0(&u) & Bndry_mask) &&
+ word0(&u) & (Exp_mask & ~Exp_msk1)
+ )
+ {
+ /* The special case */
+ b2+= Log2P;
+ s2+= Log2P;
+ spec_case= 1;
+ }
+ }
+
+ /*
+ Arrange for convenient computation of quotients:
+ shift left if necessary so divisor has 4 leading 0 bits.
+
+ Perhaps we should just compute leading 28 bits of S once
+ a nd for all and pass them and a shift to quorem, so it
+ can do shifts and ors to compute the numerator for q.
+ */
+ if ((i= ((s5 ? 32 - hi0bits(S->p.x[S->wds-1]) : 1) + s2) & 0x1f))
+ i= 32 - i;
+ if (i > 4)
+ {
+ i-= 4;
+ b2+= i;
+ m2+= i;
+ s2+= i;
+ }
+ else if (i < 4)
+ {
+ i+= 28;
+ b2+= i;
+ m2+= i;
+ s2+= i;
+ }
+ if (b2 > 0)
+ b= lshift(b, b2, &alloc);
+ if (s2 > 0)
+ S= lshift(S, s2, &alloc);
+ if (k_check)
+ {
+ if (cmp(b,S) < 0)
+ {
+ k--;
+ /* we botched the k estimate */
+ b= multadd(b, 10, 0, &alloc);
+ if (leftright)
+ mhi= multadd(mhi, 10, 0, &alloc);
+ ilim= ilim1;
+ }
+ }
+ if (ilim <= 0 && (mode == 3 || mode == 5))
+ {
+ if (ilim < 0 || cmp(b,S= multadd(S,5,0, &alloc)) <= 0)
+ {
+ /* no digits, fcvt style */
+no_digits:
+ k= -1 - ndigits;
+ goto ret;
+ }
+one_digit:
+ *s++= '1';
+ k++;
+ goto ret;
+ }
+ if (leftright)
+ {
+ if (m2 > 0)
+ mhi= lshift(mhi, m2, &alloc);
+
+ /*
+ Compute mlo -- check for special case that d is a normalized power of 2.
+ */
+
+ mlo= mhi;
+ if (spec_case)
+ {
+ mhi= Balloc(mhi->k, &alloc);
+ Bcopy(mhi, mlo);
+ mhi= lshift(mhi, Log2P, &alloc);
+ }
+
+ for (i= 1;;i++)
+ {
+ dig= quorem(b,S) + '0';
+ /* Do we yet have the shortest decimal string that will round to d? */
+ j= cmp(b, mlo);
+ delta= diff(S, mhi, &alloc);
+ j1= delta->sign ? 1 : cmp(b, delta);
+ Bfree(delta, &alloc);
+ if (j1 == 0 && mode != 1 && !(word1(&u) & 1)
+#ifdef Honor_FLT_ROUNDS
+ && rounding >= 1
+#endif
+ )
+ {
+ if (dig == '9')
+ goto round_9_up;
+ if (j > 0)
+ dig++;
+ *s++= dig;
+ goto ret;
+ }
+ if (j < 0 || (j == 0 && mode != 1 && !(word1(&u) & 1)))
+ {
+ if (!b->p.x[0] && b->wds <= 1)
+ {
+ goto accept_dig;
+ }
+#ifdef Honor_FLT_ROUNDS
+ if (mode > 1)
+ switch (rounding) {
+ case 0: goto accept_dig;
+ case 2: goto keep_dig;
+ }
+#endif /*Honor_FLT_ROUNDS*/
+ if (j1 > 0)
+ {
+ b= lshift(b, 1, &alloc);
+ j1= cmp(b, S);
+ if ((j1 > 0 || (j1 == 0 && dig & 1))
+ && dig++ == '9')
+ goto round_9_up;
+ }
+accept_dig:
+ *s++= dig;
+ goto ret;
+ }
+ if (j1 > 0)
+ {
+#ifdef Honor_FLT_ROUNDS
+ if (!rounding)
+ goto accept_dig;
+#endif
+ if (dig == '9')
+ { /* possible if i == 1 */
+round_9_up:
+ *s++= '9';
+ goto roundoff;
+ }
+ *s++= dig + 1;
+ goto ret;
+ }
+#ifdef Honor_FLT_ROUNDS
+keep_dig:
+#endif
+ *s++= dig;
+ if (i == ilim)
+ break;
+ b= multadd(b, 10, 0, &alloc);
+ if (mlo == mhi)
+ mlo= mhi= multadd(mhi, 10, 0, &alloc);
+ else
+ {
+ mlo= multadd(mlo, 10, 0, &alloc);
+ mhi= multadd(mhi, 10, 0, &alloc);
+ }
+ }
+ }
+ else
+ for (i= 1;; i++)
+ {
+ *s++= dig= quorem(b,S) + '0';
+ if (!b->p.x[0] && b->wds <= 1)
+ {
+ goto ret;
+ }
+ if (i >= ilim)
+ break;
+ b= multadd(b, 10, 0, &alloc);
+ }
+
+ /* Round off last digit */
+
+#ifdef Honor_FLT_ROUNDS
+ switch (rounding) {
+ case 0: goto trimzeros;
+ case 2: goto roundoff;
+ }
+#endif
+ b= lshift(b, 1, &alloc);
+ j= cmp(b, S);
+ if (j > 0 || (j == 0 && dig & 1))
+ {
+roundoff:
+ while (*--s == '9')
+ if (s == s0)
+ {
+ k++;
+ *s++= '1';
+ goto ret;
+ }
+ ++*s++;
+ }
+ else
+ {
+#ifdef Honor_FLT_ROUNDS
+trimzeros:
+#endif
+ while (*--s == '0');
+ s++;
+ }
+ret:
+ if (S != NULL)
+ Bfree(S, &alloc);
+ if (mhi)
+ {
+ if (mlo && mlo != mhi)
+ Bfree(mlo, &alloc);
+ Bfree(mhi, &alloc);
+ }
+ret1:
+ Bfree(b, &alloc);
+ *s= 0;
+ *decpt= k + 1;
+ if (rve)
+ *rve= s;
+ return s0;
+}
diff --git a/libmariadb/libmariadb/ma_errmsg.c b/libmariadb/libmariadb/ma_errmsg.c
new file mode 100644
index 00000000..888a41e3
--- /dev/null
+++ b/libmariadb/libmariadb/ma_errmsg.c
@@ -0,0 +1,131 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Error messages for clients */
+/* error messages for the demon is in share/language/errmsg.sys */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "errmsg.h"
+#include <stdarg.h>
+
+const char *SQLSTATE_UNKNOWN= "HY000";
+
+const char *client_errors[]=
+{
+/* 2000 */ "Unknown error",
+/* 2001 */ "Can't create UNIX socket (%d)",
+/* 2002 */ "Can't connect to local server through socket '%-.64s' (%d)",
+/* 2003 */ "Can't connect to server on '%-.64s' (%d)",
+/* 2004 */ "Can't create TCP/IP socket (%d)",
+/* 2005 */ "Unknown server host '%-.100s' (%d)",
+/* 2006 */ "Server has gone away",
+/* 2007 */ "Protocol mismatch. Server Version = %d Client Version = %d",
+/* 2008 */ "Client run out of memory",
+/* 2009 */ "Wrong host info",
+/* 2010 */ "Localhost via UNIX socket",
+/* 2011 */ "%-.64s via TCP/IP",
+/* 2012 */ "Error in server handshake",
+/* 2013 */ "Lost connection to server during query",
+/* 2014 */ "Commands out of sync; you can't run this command now",
+/* 2015 */ "%-.64s via named pipe",
+/* 2016 */ "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2017 */ "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2018 */ "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
+/* 2019 */ "Can't initialize character set %-.64s (path: %-.64s)",
+/* 2020 */ "Got packet bigger than 'max_allowed_packet'",
+/* 2021 */ "",
+/* 2022 */ "",
+/* 2023 */ "",
+/* 2024 */ "",
+/* 2025 */ "",
+/* 2026 */ "TLS/SSL error: %-.100s",
+/* 2027 */ "Received malformed packet",
+/* 2028 */ "",
+/* 2029 */ "",
+/* 2030 */ "Statement is not prepared",
+/* 2031 */ "No data supplied for parameters in prepared statement",
+/* 2032 */ "Data truncated",
+/* 2033 */ "",
+/* 2034 */ "Invalid parameter number",
+/* 2035 */ "Invalid buffer type: %d (parameter: %d)",
+/* 2036 */ "Buffer type is not supported",
+/* 2037 */ "Shared memory: %-.64s",
+/* 2038 */ "Shared memory connection failed during %s. (%lu)",
+/* 2039 */ "",
+/* 2040 */ "",
+/* 2041 */ "",
+/* 2042 */ "",
+/* 2043 */ "",
+/* 2044 */ "",
+/* 2045 */ "",
+/* 2046 */ "",
+/* 2047 */ "Wrong or unknown protocol",
+/* 2048 */ "",
+/* 2049 */ "Connection with old authentication protocol refused.",
+/* 2050 */ "",
+/* 2051 */ "",
+/* 2052 */ "Prepared statement contains no metadata",
+/* 2053 */ "",
+/* 2054 */ "This feature is not implemented or disabled",
+/* 2055 */ "Lost connection to server at '%s', system error: %d",
+/* 2056 */ "Server closed statement due to a prior %s function call",
+/* 2057 */ "The number of parameters in bound buffers differs from number of columns in resultset",
+/* 2058 */ "Can't connect twice. Already connected",
+/* 2059 */ "Plugin %s could not be loaded: %s",
+/* 2060 */ "An attribute with same name already exists",
+/* 2061 */ "Plugin doesn't support this function",
+ ""
+};
+
+const char *mariadb_client_errors[] =
+{
+ /* 5000 */ "Creating an event failed (Errorcode: %d)",
+ /* 5001 */ "Bind to local interface '-.%64s' failed (Errorcode: %d)",
+ /* 5002 */ "Connection type doesn't support asynchronous IO operations",
+ /* 5003 */ "Server doesn't support function '%s'",
+ /* 5004 */ "File '%s' not found (Errcode: %d)",
+ /* 5005 */ "Error reading file '%s' (Errcode: %d)",
+ /* 5006 */ "Bulk operation without parameters is not supported",
+ /* 5007 */ "Invalid statement handle",
+ /* 5008 */ "Unsupported version %d. Supported versions are in the range %d - %d",
+ /* 5009 */ "Invalid or missing parameter '%s'.",
+ /* 5010 */ "Authentication plugin '%s' couldn't be found in restricted_auth plugin list.",
+ /* 5011 */ "Parse error in connection string (offset %d)",
+ /* 5012 */ "Error while loading plugin '%s'",
+ /* 5013 */ "Read error: %s (%d)",
+ /* 5014 */ "Write error: %s (%d)",
+ /* 5015 */ "Error while uncompressing packet",
+ /* 5016 */ "Error while retrieving parameter from callback function",
+ /* 5017 */ "Error while uncompressing packet (File: %.*s start_pos=%ld).",
+ /* 5018 */ "Checksum verification failed (File: %.*s start_pos=%ld). Reported checksum is %x, while calculated checksum is %x.",
+ /* 5019 */ "Binary log protocol error (File: %.*s start_pos=%ld): Log format %d not supported.",
+ /* 5020 */ "Binary log error (File: %.*s start_pos=%ld): Unknown event type (%d) with flag 'not_ignorable'.",
+ /* 5021 */ "Binary log error (File: %.*s start_pos=%ld): %s.",
+ /* 5022 */ "File '%s' is not a binary log file",
+ /* 5023 */ "Semi sync request error: %s",
+ ""
+};
+
+const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0};
+char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE];
+
+void init_client_errs(void)
+{
+ my_errmsg[CLIENT_ERRMAP] = &client_errors[0];
+}
+
diff --git a/libmariadb/libmariadb/ma_hashtbl.c b/libmariadb/libmariadb/ma_hashtbl.c
new file mode 100644
index 00000000..4805dee0
--- /dev/null
+++ b/libmariadb/libmariadb/ma_hashtbl.c
@@ -0,0 +1,580 @@
+/************************************************************************************
+ Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
+ Monty Program 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 <http://www.gnu.org/licenses>
+ 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
+*************************************************************************************/
+
+/* The hash functions used for saving keys */
+/* One of key_length or key_length_offset must be given */
+/* Key length of 0 isn't allowed */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "ma_hashtbl.h"
+
+#define NO_RECORD ((uint) -1)
+#define LOWFIND 1
+#define LOWUSED 2
+#define HIGHFIND 4
+#define HIGHUSED 8
+
+static uint hash_mask(uint hashnr,uint buffmax,uint maxlength);
+static void movelink(MA_HASHTBL_LINK *array,uint pos,uint next_link,uint newlink);
+static uint calc_hashnr(const uchar *key,uint length);
+static uint calc_hashnr_caseup(const uchar *key,uint length);
+static int hashcmp(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,const uchar *key,uint length);
+
+
+my_bool _ma_hashtbl_init(MA_HASHTBL *hash,uint size,uint key_offset,uint key_length,
+ hash_get_key get_key,
+ void (*free_element)(void*),uint flags CALLER_INFO_PROTO)
+{
+ hash->records=0;
+ if (ma_init_dynamic_array_ci(&hash->array,sizeof(MA_HASHTBL_LINK),size,0))
+ {
+ hash->free=0; /* Allow call to hash_free */
+ return(TRUE);
+ }
+ hash->key_offset=key_offset;
+ hash->key_length=key_length;
+ hash->blength=1;
+ hash->current_record= NO_RECORD; /* For the future */
+ hash->get_key=get_key;
+ hash->free=free_element;
+ hash->flags=flags;
+ if (flags & MA_HASHTBL_CASE_INSENSITIVE)
+ hash->calc_hashnr=calc_hashnr_caseup;
+ else
+ hash->calc_hashnr=calc_hashnr;
+ return(0);
+}
+
+
+void ma_hashtbl_free(MA_HASHTBL *hash)
+{
+ if (hash->free)
+ {
+ uint i,records;
+ MA_HASHTBL_LINK *data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
+ for (i=0,records=hash->records ; i < records ; i++)
+ (*hash->free)(data[i].data);
+ hash->free=0;
+ }
+ ma_delete_dynamic(&hash->array);
+ hash->records=0;
+ return;
+}
+
+ /* some helper functions */
+
+/*
+ This function is char* instead of uchar* as HPUX11 compiler can't
+ handle inline functions that are not defined as native types
+*/
+
+static inline char*
+hash_key(MA_HASHTBL *hash,const uchar *record,uint *length,my_bool first)
+{
+ if (hash->get_key)
+ return (char *)(*hash->get_key)(record,(uint *)length,first);
+ *length=hash->key_length;
+ return (char*) record+hash->key_offset;
+}
+
+ /* Calculate pos according to keys */
+
+static uint hash_mask(uint hashnr,uint buffmax,uint maxlength)
+{
+ if ((hashnr & (buffmax-1)) < maxlength) return (hashnr & (buffmax-1));
+ return (hashnr & ((buffmax >> 1) -1));
+}
+
+static uint hash_rec_mask(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,uint buffmax,
+ uint maxlength)
+{
+ uint length;
+ uchar *key= (uchar*) hash_key(hash,pos->data,&length,0);
+ return hash_mask((*hash->calc_hashnr)(key,length),buffmax,maxlength);
+}
+
+#ifndef NEW_MA_HASHTBL_FUNCTION
+
+ /* Calc hashvalue for a key */
+
+static uint calc_hashnr(const uchar *key,uint length)
+{
+ register uint nr=1, nr2=4;
+ while (length--)
+ {
+ nr^= (((nr & 63)+nr2)*((uint) (uchar) *key++))+ (nr << 8);
+ nr2+=3;
+ }
+ return((uint) nr);
+}
+
+ /* Calc hashvalue for a key, case independently */
+
+static uint calc_hashnr_caseup(const uchar *key,uint length)
+{
+ register uint nr=1, nr2=4;
+ while (length--)
+ {
+ nr^= (((nr & 63)+nr2)*((uint) (uchar) toupper(*key++)))+ (nr << 8);
+ nr2+=3;
+ }
+ return((uint) nr);
+}
+
+#else
+
+/*
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of the hash algorithm was taken from an idea sent by email to the
+ * IEEE Posix P1003.2 mailing list from Phong Vo (kpv@research.att.com) and
+ * Glenn Fowler (gsf@research.att.com). Landon Curt Noll (chongo@toad.com)
+ * later improved on their algorithm.
+ *
+ * The magic is in the interesting relationship between the special prime
+ * 16777619 (2^24 + 403) and 2^32 and 2^8.
+ *
+ * This hash produces the fewest collisions of any function that we've seen so
+ * far, and works well on both numbers and strings.
+ */
+
+uint calc_hashnr(const uchar *key, uint len)
+{
+ const uchar *end=key+len;
+ uint hash;
+ for (hash = 0; key < end; key++)
+ {
+ hash *= 16777619;
+ hash ^= (uint) *(uchar*) key;
+ }
+ return (hash);
+}
+
+uint calc_hashnr_caseup(const uchar *key, uint len)
+{
+ const uchar *end=key+len;
+ uint hash;
+ for (hash = 0; key < end; key++)
+ {
+ hash *= 16777619;
+ hash ^= (uint) (uchar) toupper(*key);
+ }
+ return (hash);
+}
+
+#endif
+
+
+#ifndef __SUNPRO_C /* SUNPRO can't handle this */
+static inline
+#endif
+unsigned int rec_hashnr(MA_HASHTBL *hash,const uchar *record)
+{
+ uint length;
+ uchar *key= (uchar*) hash_key(hash,record,&length,0);
+ return (*hash->calc_hashnr)(key,length);
+}
+
+
+ /* Search after a record based on a key */
+ /* Sets info->current_ptr to found record */
+
+void* ma_hashtbl_search(MA_HASHTBL *hash,const uchar *key,uint length)
+{
+ MA_HASHTBL_LINK *pos;
+ uint flag,idx;
+
+ flag=1;
+ if (hash->records)
+ {
+ idx=hash_mask((*hash->calc_hashnr)(key,length ? length :
+ hash->key_length),
+ hash->blength,hash->records);
+ do
+ {
+ pos= dynamic_element(&hash->array,idx,MA_HASHTBL_LINK*);
+ if (!hashcmp(hash,pos,key,length))
+ {
+ hash->current_record= idx;
+ return (pos->data);
+ }
+ if (flag)
+ {
+ flag=0; /* Reset flag */
+ if (hash_rec_mask(hash,pos,hash->blength,hash->records) != idx)
+ break; /* Wrong link */
+ }
+ }
+ while ((idx=pos->next) != NO_RECORD);
+ }
+ hash->current_record= NO_RECORD;
+ return(0);
+}
+
+ /* Get next record with identical key */
+ /* Can only be called if previous calls was hash_search */
+
+void *ma_hashtbl_next(MA_HASHTBL *hash,const uchar *key,uint length)
+{
+ MA_HASHTBL_LINK *pos;
+ uint idx;
+
+ if (hash->current_record != NO_RECORD)
+ {
+ MA_HASHTBL_LINK *data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
+ for (idx=data[hash->current_record].next; idx != NO_RECORD ; idx=pos->next)
+ {
+ pos=data+idx;
+ if (!hashcmp(hash,pos,key,length))
+ {
+ hash->current_record= idx;
+ return pos->data;
+ }
+ }
+ hash->current_record=NO_RECORD;
+ }
+ return 0;
+}
+
+
+ /* Change link from pos to new_link */
+
+static void movelink(MA_HASHTBL_LINK *array,uint find,uint next_link,uint newlink)
+{
+ MA_HASHTBL_LINK *old_link;
+ do
+ {
+ old_link=array+next_link;
+ }
+ while ((next_link=old_link->next) != find);
+ old_link->next= newlink;
+ return;
+}
+
+ /* Compare a key in a record to a whole key. Return 0 if identical */
+
+static int hashcmp(MA_HASHTBL *hash,MA_HASHTBL_LINK *pos,const uchar *key,uint length)
+{
+ uint rec_keylength;
+ uchar *rec_key= (uchar*) hash_key(hash,pos->data,&rec_keylength,1);
+ return (length && length != rec_keylength) ||
+ memcmp(rec_key,key,rec_keylength);
+}
+
+
+ /* Write a hash-key to the hash-index */
+
+my_bool ma_hashtbl_insert(MA_HASHTBL *info,const uchar *record)
+{
+ int flag;
+ uint halfbuff,hash_nr,first_index,idx;
+ uchar *ptr_to_rec= NULL,*ptr_to_rec2= NULL;
+ MA_HASHTBL_LINK *data,*empty,*gpos= NULL,*gpos2 = NULL,*pos;
+
+ LINT_INIT(gpos); LINT_INIT(gpos2);
+ LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2);
+
+ flag=0;
+ if (!(empty=(MA_HASHTBL_LINK*) ma_alloc_dynamic(&info->array)))
+ return(TRUE); /* No more memory */
+
+ info->current_record= NO_RECORD;
+ data=dynamic_element(&info->array,0,MA_HASHTBL_LINK*);
+ halfbuff= info->blength >> 1;
+
+ idx=first_index=info->records-halfbuff;
+ if (idx != info->records) /* If some records */
+ {
+ do
+ {
+ pos=data+idx;
+ hash_nr=rec_hashnr(info,pos->data);
+ if (flag == 0) /* First loop; Check if ok */
+ if (hash_mask(hash_nr,info->blength,info->records) != first_index)
+ break;
+ if (!(hash_nr & halfbuff))
+ { /* Key will not move */
+ if (!(flag & LOWFIND))
+ {
+ if (flag & HIGHFIND)
+ {
+ flag=LOWFIND | HIGHFIND;
+ /* key shall be moved to the current empty position */
+ gpos=empty;
+ ptr_to_rec=pos->data;
+ empty=pos; /* This place is now free */
+ }
+ else
+ {
+ flag=LOWFIND | LOWUSED; /* key isn't changed */
+ gpos=pos;
+ ptr_to_rec=pos->data;
+ }
+ }
+ else
+ {
+ if (!(flag & LOWUSED))
+ {
+ /* Change link of previous LOW-key */
+ gpos->data=ptr_to_rec;
+ gpos->next=(uint) (pos-data);
+ flag= (flag & HIGHFIND) | (LOWFIND | LOWUSED);
+ }
+ gpos=pos;
+ ptr_to_rec=pos->data;
+ }
+ }
+ else
+ { /* key will be moved */
+ if (!(flag & HIGHFIND))
+ {
+ flag= (flag & LOWFIND) | HIGHFIND;
+ /* key shall be moved to the last (empty) position */
+ gpos2 = empty; empty=pos;
+ ptr_to_rec2=pos->data;
+ }
+ else
+ {
+ if (!(flag & HIGHUSED))
+ {
+ /* Change link of previous hash-key and save */
+ gpos2->data=ptr_to_rec2;
+ gpos2->next=(uint) (pos-data);
+ flag= (flag & LOWFIND) | (HIGHFIND | HIGHUSED);
+ }
+ gpos2=pos;
+ ptr_to_rec2=pos->data;
+ }
+ }
+ }
+ while ((idx=pos->next) != NO_RECORD);
+
+ if ((flag & (LOWFIND | LOWUSED)) == LOWFIND)
+ {
+ gpos->data=ptr_to_rec;
+ gpos->next=NO_RECORD;
+ }
+ if ((flag & (HIGHFIND | HIGHUSED)) == HIGHFIND)
+ {
+ gpos2->data=ptr_to_rec2;
+ gpos2->next=NO_RECORD;
+ }
+ }
+ /* Check if we are at the empty position */
+
+ idx=hash_mask(rec_hashnr(info,record),info->blength,info->records+1);
+ pos=data+idx;
+ if (pos == empty)
+ {
+ pos->data=(uchar*) record;
+ pos->next=NO_RECORD;
+ }
+ else
+ {
+ /* Check if more records in same hash-nr family */
+ empty[0]=pos[0];
+ gpos=data+hash_rec_mask(info,pos,info->blength,info->records+1);
+ if (pos == gpos)
+ {
+ pos->data=(uchar*) record;
+ pos->next=(uint) (empty - data);
+ }
+ else
+ {
+ pos->data=(uchar*) record;
+ pos->next=NO_RECORD;
+ movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));
+ }
+ }
+ if (++info->records == info->blength)
+ info->blength+= info->blength;
+ return(0);
+}
+
+
+/******************************************************************************
+** Remove one record from hash-table. The record with the same record
+** ptr is removed.
+** if there is a free-function it's called for record if found
+******************************************************************************/
+
+my_bool ma_hashtbl_delete(MA_HASHTBL *hash,uchar *record)
+{
+ uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index;
+ MA_HASHTBL_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
+ if (!hash->records)
+ return(1);
+
+ blength=hash->blength;
+ data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
+ /* Search after record with key */
+ pos=data+ hash_mask(rec_hashnr(hash,record),blength,hash->records);
+ gpos = 0;
+
+ while (pos->data != record)
+ {
+ gpos=pos;
+ if (pos->next == NO_RECORD)
+ return(1); /* Key not found */
+ pos=data+pos->next;
+ }
+
+ if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
+ hash->current_record= NO_RECORD;
+ lastpos=data+hash->records;
+
+ /* Remove link to record */
+ empty=pos; empty_index=(uint) (empty-data);
+ if (gpos)
+ gpos->next=pos->next; /* unlink current ptr */
+ else if (pos->next != NO_RECORD)
+ {
+ empty=data+(empty_index=pos->next);
+ pos->data=empty->data;
+ pos->next=empty->next;
+ }
+
+ if (empty == lastpos) /* last key at wrong pos or no next link */
+ goto exit;
+
+ /* Move the last key (lastpos) */
+ lastpos_hashnr=rec_hashnr(hash,lastpos->data);
+ /* pos is where lastpos should be */
+ pos=data+hash_mask(lastpos_hashnr,hash->blength,hash->records);
+ if (pos == empty) /* Move to empty position. */
+ {
+ empty[0]=lastpos[0];
+ goto exit;
+ }
+ pos_hashnr=rec_hashnr(hash,pos->data);
+ /* pos3 is where the pos should be */
+ pos3= data+hash_mask(pos_hashnr,hash->blength,hash->records);
+ if (pos != pos3)
+ { /* pos is on wrong posit */
+ empty[0]=pos[0]; /* Save it here */
+ pos[0]=lastpos[0]; /* This should be here */
+ movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
+ goto exit;
+ }
+ pos2= hash_mask(lastpos_hashnr,blength,hash->records+1);
+ if (pos2 == hash_mask(pos_hashnr,blength,hash->records+1))
+ { /* Identical key-positions */
+ if (pos2 != hash->records)
+ {
+ empty[0]=lastpos[0];
+ movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
+ goto exit;
+ }
+ idx= (uint) (pos-data); /* Link pos->next after lastpos */
+ }
+ else idx= NO_RECORD; /* Different positions merge */
+
+ empty[0]=lastpos[0];
+ movelink(data,idx,empty_index,pos->next);
+ pos->next=empty_index;
+
+exit:
+ ma_pop_dynamic(&hash->array);
+ if (hash->free)
+ (*hash->free)((uchar*) record);
+ return(0);
+}
+
+ /*
+ Update keys when record has changed.
+ This is much more efficient than using a delete & insert.
+ */
+
+my_bool ma_hashtbl_update(MA_HASHTBL *hash,uchar *record,uchar *old_key,uint old_key_length)
+{
+ uint idx,new_index,new_pos_index,blength,records,empty;
+ MA_HASHTBL_LINK org_link,*data,*previous,*pos;
+
+ data=dynamic_element(&hash->array,0,MA_HASHTBL_LINK*);
+ blength=hash->blength; records=hash->records;
+
+ /* Search after record with key */
+
+ idx=hash_mask((*hash->calc_hashnr)(old_key,(old_key_length ?
+ old_key_length :
+ hash->key_length)),
+ blength,records);
+ new_index=hash_mask(rec_hashnr(hash,record),blength,records);
+ if (idx == new_index)
+ return(0); /* Nothing to do (No record check) */
+ previous=0;
+ for (;;)
+ {
+
+ if ((pos= data+idx)->data == record)
+ break;
+ previous=pos;
+ if ((idx=pos->next) == NO_RECORD)
+ return(1); /* Not found in links */
+ }
+ hash->current_record= NO_RECORD;
+ org_link= *pos;
+ empty=idx;
+
+ /* Relink record from current chain */
+
+ if (!previous)
+ {
+ if (pos->next != NO_RECORD)
+ {
+ empty=pos->next;
+ *pos= data[pos->next];
+ }
+ }
+ else
+ previous->next=pos->next; /* unlink pos */
+
+ /* Move data to correct position */
+ pos=data+new_index;
+ new_pos_index=hash_rec_mask(hash,pos,blength,records);
+ if (new_index != new_pos_index)
+ { /* Other record in wrong position */
+ data[empty] = *pos;
+ movelink(data,new_index,new_pos_index,empty);
+ org_link.next=NO_RECORD;
+ data[new_index]= org_link;
+ }
+ else
+ { /* Link in chain at right position */
+ org_link.next=data[new_index].next;
+ data[empty]=org_link;
+ data[new_index].next=empty;
+ }
+ return(0);
+}
+
+
+uchar *ma_hashtbl_element(MA_HASHTBL *hash,uint idx)
+{
+ if (idx < hash->records)
+ return dynamic_element(&hash->array,idx,MA_HASHTBL_LINK*)->data;
+ return 0;
+}
diff --git a/libmariadb/libmariadb/ma_init.c b/libmariadb/libmariadb/ma_init.c
new file mode 100644
index 00000000..008aec54
--- /dev/null
+++ b/libmariadb/libmariadb/ma_init.c
@@ -0,0 +1,87 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include "mariadb_ctype.h"
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include <signal.h>
+#ifdef _WIN32
+#ifdef _MSC_VER
+#include <locale.h>
+#include <crtdbg.h>
+#endif
+static my_bool my_win_init(void);
+#else
+#define my_win_init()
+#endif
+
+my_bool ma_init_done=0;
+
+
+
+/* Init ma_sys functions and ma_sys variabels */
+
+void ma_init(void)
+{
+ if (ma_init_done)
+ return;
+ ma_init_done=1;
+ {
+#ifdef _WIN32
+ my_win_init();
+#endif
+ return;
+ }
+} /* ma_init */
+
+
+
+void ma_end(int infoflag __attribute__((unused)))
+{
+#ifdef _WIN32
+ WSACleanup( );
+#endif /* _WIN32 */
+ ma_init_done=0;
+} /* ma_end */
+
+#ifdef _WIN32
+static my_bool my_win_init()
+{
+ WORD VersionRequested;
+ int err;
+ WSADATA WsaData;
+ const unsigned int MajorVersion=2,
+ MinorVersion=2;
+ VersionRequested= MAKEWORD(MajorVersion, MinorVersion);
+ /* Load WinSock library */
+ if ((err= WSAStartup(VersionRequested, &WsaData)))
+ {
+ return 0;
+ }
+ /* make sure 2.2 or higher is supported */
+ if ((LOBYTE(WsaData.wVersion) * 10 + HIBYTE(WsaData.wVersion)) < 22)
+ {
+ WSACleanup();
+ return 1;
+ }
+ return 0;
+}
+#endif
+
diff --git a/libmariadb/libmariadb/ma_io.c b/libmariadb/libmariadb/ma_io.c
new file mode 100644
index 00000000..178ffe9f
--- /dev/null
+++ b/libmariadb/libmariadb/ma_io.c
@@ -0,0 +1,224 @@
+/*
+ Copyright (C) 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 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <errmsg.h>
+#include <mysql.h>
+#include <mysql/client_plugin.h>
+#include <mariadb/ma_io.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_REMOTEIO
+struct st_mysql_client_plugin_REMOTEIO *rio_plugin= NULL;
+#endif
+
+/* {{{ ma_open */
+MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql)
+{
+ int CodePage= -1;
+ FILE *fp= NULL;
+ MA_FILE *ma_file= NULL;
+
+ if (!location || !location[0])
+ return NULL;
+#ifdef HAVE_REMOTEIO
+ if (strstr(location, "://"))
+ goto remote;
+#endif
+
+#ifdef _WIN32
+ if (mysql && mysql->charset)
+ CodePage= madb_get_windows_cp(mysql->charset->csname);
+#endif
+ if (CodePage == -1)
+ {
+ if (!(fp= fopen(location, mode)))
+ {
+ return NULL;
+ }
+ }
+#ifdef _WIN32
+ /* See CONC-44: we need to support non ascii filenames too, so we convert
+ current character set to wchar_t and try to open the file via _wsopen */
+ else
+ {
+ wchar_t *w_filename= NULL;
+ wchar_t *w_mode= NULL;
+ int len;
+ DWORD Length;
+
+ len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), NULL, 0);
+ if (!len)
+ return NULL;
+ if (!(w_filename= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
+ {
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+ Length= len;
+ len= MultiByteToWideChar(CodePage, 0, location, (int)strlen(location), w_filename, (int)Length);
+ if (!len)
+ {
+ /* todo: error handling */
+ free(w_filename);
+ return NULL;
+ }
+ len= (int)strlen(mode);
+ if (!(w_mode= (wchar_t *)calloc(1, (len + 1) * sizeof(wchar_t))))
+ {
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ free(w_filename);
+ return NULL;
+ }
+ Length= len;
+ len= MultiByteToWideChar(CodePage, 0, mode, (int)strlen(mode), w_mode, (int)Length);
+ if (!len)
+ {
+ /* todo: error handling */
+ free(w_filename);
+ free(w_mode);
+ return NULL;
+ }
+ fp= _wfopen(w_filename, w_mode);
+ free(w_filename);
+ free(w_mode);
+ }
+
+#endif
+ if (fp)
+ {
+ ma_file= (MA_FILE *)malloc(sizeof(MA_FILE));
+ if (!ma_file)
+ {
+ fclose(fp);
+ my_set_error(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+ ma_file->type= MA_FILE_LOCAL;
+ ma_file->ptr= (void *)fp;
+ }
+ return ma_file;
+#ifdef HAVE_REMOTEIO
+remote:
+ /* check if plugin for remote io is available and try
+ * to open location */
+ {
+ MYSQL mysql;
+ if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *)
+ mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN)))
+ return rio_plugin->methods->mopen(location, mode);
+ return NULL;
+ }
+#endif
+}
+/* }}} */
+
+/* {{{ ma_close */
+int ma_close(MA_FILE *file)
+{
+ int rc;
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ rc= fclose((FILE *)file->ptr);
+ free(file);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ rc= rio_plugin->methods->mclose(file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+ return rc;
+}
+/* }}} */
+
+
+/* {{{ ma_feof */
+int ma_feof(MA_FILE *file)
+{
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ return feof((FILE *)file->ptr);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mfeof(file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ ma_read */
+size_t ma_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file)
+{
+ size_t s= 0;
+ if (!file)
+ return -1;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ s= fread(ptr, size, nmemb, (FILE *)file->ptr);
+ return s;
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mread(ptr, size, nmemb, file);
+ break;
+#endif
+ default:
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ ma_gets */
+char *ma_gets(char *ptr, size_t size, MA_FILE *file)
+{
+ if (!file)
+ return NULL;
+
+ switch (file->type) {
+ case MA_FILE_LOCAL:
+ return fgets(ptr, (int)size, (FILE *)file->ptr);
+ break;
+#ifdef HAVE_REMOTEIO
+ case MA_FILE_REMOTE:
+ return rio_plugin->methods->mgets(ptr, size, file);
+ break;
+#endif
+ default:
+ return NULL;
+ }
+}
+/* }}} */
+
+
diff --git a/libmariadb/libmariadb/ma_list.c b/libmariadb/libmariadb/ma_list.c
new file mode 100644
index 00000000..63c526f1
--- /dev/null
+++ b/libmariadb/libmariadb/ma_list.c
@@ -0,0 +1,114 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/*
+ Code for handling dubble-linked lists in C
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_list.h>
+
+ /* Add a element to start of list */
+
+LIST *list_add(LIST *root, 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 */
+}
+
+
+LIST *list_delete(LIST *root, 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 list_free(LIST *root, unsigned int free_data)
+{
+ LIST *next;
+ while (root)
+ {
+ next=root->next;
+ if (free_data)
+ free(root->data);
+ free(root);
+ root=next;
+ }
+}
+
+
+LIST *list_cons(void *data, LIST *list)
+{
+ LIST *new_charset=(LIST*) malloc(sizeof(LIST));
+ if (!new_charset)
+ return 0;
+ new_charset->data=data;
+ return list_add(list,new_charset);
+}
+
+
+LIST *list_reverse(LIST *root)
+{
+ LIST *last;
+
+ last=root;
+ while (root)
+ {
+ last=root;
+ root=root->next;
+ last->next=last->prev;
+ last->prev=root;
+ }
+ return last;
+}
+
+uint list_length(LIST *list)
+{
+ uint count;
+ for (count=0 ; list ; list=list->next, count++) ;
+ return count;
+}
+
+
+int list_walk(LIST *list, list_walk_action action, gptr argument)
+{
+ int error=0;
+ while (list)
+ {
+ if ((error = (*action)(list->data,argument)))
+ return error;
+ list= list_rest(list);
+ }
+ return 0;
+}
diff --git a/libmariadb/libmariadb/ma_ll2str.c b/libmariadb/libmariadb/ma_ll2str.c
new file mode 100644
index 00000000..6938d37f
--- /dev/null
+++ b/libmariadb/libmariadb/ma_ll2str.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016,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 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include "ma_string.h"
+#include <ctype.h>
+
+static char NEAR _dig_vec[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+char *ma_ll2str(long long val,char *dst,int radix)
+{
+ char buffer[65];
+ register char *p;
+ long long_val;
+
+ if (radix < 0)
+ {
+ if (radix < -36 || radix > -2) return (char*) 0;
+ if (val < 0) {
+ *dst++ = '-';
+ val = 0ULL - val;
+ }
+ radix = -radix;
+ }
+ else
+ {
+ if (radix > 36 || radix < 2) return (char*) 0;
+ }
+ if (val == 0)
+ {
+ *dst++='0';
+ *dst='\0';
+ return dst;
+ }
+ p = &buffer[sizeof(buffer)-1];
+ *p = '\0';
+
+ while ((ulonglong) val > (ulonglong) LONG_MAX)
+ {
+ ulonglong quo=(ulonglong) val/(uint) radix;
+ uint rem= (uint) (val- quo* (uint) radix);
+ *--p = _dig_vec[rem];
+ val= quo;
+ }
+ long_val= (long) val;
+ while (long_val != 0)
+ {
+ long quo= long_val/radix;
+ *--p = _dig_vec[(uchar) (long_val - quo*radix)];
+ long_val= quo;
+ }
+ while ((*dst++ = *p++) != 0) ;
+ return dst-1;
+}
diff --git a/libmariadb/libmariadb/ma_loaddata.c b/libmariadb/libmariadb/ma_loaddata.c
new file mode 100644
index 00000000..c59285ec
--- /dev/null
+++ b/libmariadb/libmariadb/ma_loaddata.c
@@ -0,0 +1,265 @@
+/************************************************************************************
+ Copyright (C) 2000, 2011 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
+ Monty Program 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 <http://www.gnu.org/licenses>
+ 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
+*************************************************************************************/
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include "errmsg.h"
+#include "mysql.h"
+#include <mariadb/ma_io.h>
+#include <string.h>
+#ifdef _WIN32
+#include <share.h>
+#endif
+#include <ma_common.h>
+
+typedef struct st_mysql_infile_info
+{
+ MA_FILE *fp;
+ int error_no;
+ char error_msg[MYSQL_ERRMSG_SIZE + 1];
+ const char *filename;
+} MYSQL_INFILE_INFO;
+
+/* {{{ mysql_local_infile_init */
+static
+int mysql_local_infile_init(void **ptr, const char *filename, void *userdata)
+{
+ MYSQL_INFILE_INFO *info;
+ MYSQL *mysql= (MYSQL *)userdata;
+
+ info = (MYSQL_INFILE_INFO *)malloc(sizeof(MYSQL_INFILE_INFO));
+ if (!info) {
+ return(1);
+ }
+ memset(info, 0, sizeof(MYSQL_INFILE_INFO));
+ *ptr = info;
+
+ info->filename = filename;
+
+ info->fp= ma_open(filename, "rb", mysql);
+
+ if (!info->fp)
+ {
+ /* error handling is done via mysql_local_infile_error function, so we
+ need to copy error to info */
+ if (mysql_errno(mysql) && !info->error_no)
+ {
+ info->error_no= mysql_errno(mysql);
+ ma_strmake(info->error_msg, mysql_error(mysql), MYSQL_ERRMSG_SIZE);
+ }
+ else
+ {
+ info->error_no = errno;
+ snprintf((char *)info->error_msg, sizeof(info->error_msg),
+ CER(CR_FILE_NOT_FOUND), filename, info->error_no);
+ }
+ return(1);
+ }
+
+ return(0);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_read */
+static
+int mysql_local_infile_read(void *ptr, char * buf, unsigned int buf_len)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+ size_t count;
+
+ count= ma_read((void *)buf, 1, (size_t)buf_len, info->fp);
+
+ if (count == (size_t)-1)
+ {
+ info->error_no = errno;
+ snprintf((char *)info->error_msg, sizeof(info->error_msg),
+ CER(CR_FILE_READ), info->filename, info->error_no);
+ }
+ return((int)count);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_error */
+static
+int mysql_local_infile_error(void *ptr, char *error_buf, unsigned int error_buf_len)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+
+ if (info) {
+ ma_strmake(error_buf, info->error_msg, error_buf_len);
+ return(info->error_no);
+ }
+
+ ma_strmake(error_buf, "Unknown error", error_buf_len);
+ return(CR_UNKNOWN_ERROR);
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_end */
+static
+void mysql_local_infile_end(void *ptr)
+{
+ MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr;
+
+ if (info)
+ {
+ if (info->fp)
+ ma_close(info->fp);
+ free(ptr);
+ }
+ return;
+}
+/* }}} */
+
+
+/* {{{ mysql_local_infile_default */
+void mysql_set_local_infile_default(MYSQL *conn)
+{
+ conn->options.local_infile_init = mysql_local_infile_init;
+ conn->options.local_infile_read = mysql_local_infile_read;
+ conn->options.local_infile_error = mysql_local_infile_error;
+ conn->options.local_infile_end = mysql_local_infile_end;
+ return;
+}
+/* }}} */
+
+/* {{{ mysql_set_local_infile_handler */
+void STDCALL mysql_set_local_infile_handler(MYSQL *conn,
+ int (*local_infile_init)(void **, const char *, void *),
+ int (*local_infile_read)(void *, char *, uint),
+ void (*local_infile_end)(void *),
+ int (*local_infile_error)(void *, char *, uint),
+ void *userdata)
+{
+ conn->options.local_infile_init= local_infile_init;
+ conn->options.local_infile_read= local_infile_read;
+ conn->options.local_infile_end= local_infile_end;
+ conn->options.local_infile_error= local_infile_error;
+ conn->options.local_infile_userdata = userdata;
+ return;
+}
+/* }}} */
+
+/* {{{ mysql_handle_local_infile */
+my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename, my_bool can_local_infile)
+{
+ unsigned int buflen= 4096;
+ int bufread;
+ unsigned char *buf= NULL;
+ void *info= NULL;
+ my_bool result= 1;
+
+ /* check if all callback functions exist */
+ if (!conn->options.local_infile_init || !conn->options.local_infile_end ||
+ !conn->options.local_infile_read || !conn->options.local_infile_error)
+ {
+ conn->options.local_infile_userdata= conn;
+ mysql_set_local_infile_default(conn);
+ }
+
+ if (!(conn->options.client_flag & CLIENT_LOCAL_FILES) ||
+ !can_local_infile)
+ {
+ my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden");
+ /* write empty packet to server */
+ ma_net_write(&conn->net, (unsigned char *)"", 0);
+ ma_net_flush(&conn->net);
+ goto infile_error;
+ }
+
+ /* allocate buffer for reading data */
+ buf = (uchar *)malloc(buflen);
+
+ /* init handler: allocate read buffer and open file */
+ if (conn->options.local_infile_init(&info, filename,
+ conn->options.local_infile_userdata))
+ {
+ char tmp_buf[MYSQL_ERRMSG_SIZE];
+ int tmp_errno;
+
+ tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
+ my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
+ ma_net_write(&conn->net, (unsigned char *)"", 0);
+ ma_net_flush(&conn->net);
+ goto infile_error;
+ }
+
+ /* read data */
+ while ((bufread= conn->options.local_infile_read(info, (char *)buf, buflen)) > 0)
+ {
+ if (ma_net_write(&conn->net, (unsigned char *)buf, bufread))
+ {
+ my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
+ goto infile_error;
+ }
+ }
+
+ /* send empty packet for eof */
+ if (ma_net_write(&conn->net, (unsigned char *)"", 0) ||
+ ma_net_flush(&conn->net))
+ {
+ my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL);
+ goto infile_error;
+ }
+
+ /* error during read occurred */
+ if (bufread < 0)
+ {
+ char tmp_buf[MYSQL_ERRMSG_SIZE];
+ int tmp_errno= conn->options.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
+ my_set_error(conn, tmp_errno, SQLSTATE_UNKNOWN, tmp_buf);
+ goto infile_error;
+ }
+
+ result = 0;
+
+infile_error:
+ conn->options.local_infile_end(info);
+ free(buf);
+ return(result);
+}
+/* }}} */
+
diff --git a/libmariadb/libmariadb/ma_net.c b/libmariadb/libmariadb/ma_net.c
new file mode 100644
index 00000000..2e932b17
--- /dev/null
+++ b/libmariadb/libmariadb/ma_net.c
@@ -0,0 +1,595 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2012-2016 SkySQL AB, 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* Write and read of logical packets to/from socket
+ ** Writes are cached into net_buffer_length big packets.
+ ** Read packets are reallocated dynamically when reading big packets.
+ ** Each logical packet has the following pre-info:
+ ** 3 byte length & 1 byte package-number.
+ */
+
+
+#include <ma_global.h>
+#include <mysql.h>
+#include <ma_pvio.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <ma_pvio.h>
+#include <ma_common.h>
+#ifndef _WIN32
+#include <poll.h>
+#endif
+
+#define MAX_PACKET_LENGTH (256L*256L*256L-1)
+
+/* net_buffer_length and max_allowed_packet are defined in mysql.h
+ See bug conc-57
+ */
+#undef net_buffer_length
+
+#undef max_allowed_packet
+ulong max_allowed_packet=1024L * 1024L * 1024L;
+ulong net_read_timeout= NET_READ_TIMEOUT;
+ulong net_write_timeout= NET_WRITE_TIMEOUT;
+ulong net_buffer_length= 8192; /* Default length. Enlarged if necessary */
+
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#else
+#undef MYSQL_SERVER /* Win32 can't handle interrupts */
+#endif
+#if !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__)
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#if !defined(alpha_linux_port)
+#include <netinet/tcp.h>
+#endif
+#endif
+
+
+/*
+ ** Give error if a too big packet is found
+ ** The server can change this with the -O switch, but because the client
+ ** can't normally do this the client should have a bigger max-buffer.
+ */
+
+static int ma_net_write_buff(NET *net,const char *packet, size_t len);
+
+
+/* Init with packet info */
+
+int ma_net_init(NET *net, MARIADB_PVIO* pvio)
+{
+ if (!(net->buff=(uchar*) malloc(net_buffer_length)))
+ return 1;
+ if (!net->extension)
+ return 1;
+
+ memset(net->buff, 0, net_buffer_length);
+
+ net->max_packet_size= MAX(net_buffer_length, max_allowed_packet);
+ net->buff_end=net->buff+(net->max_packet=net_buffer_length);
+ net->pvio = pvio;
+ net->error=0; net->return_status=0;
+ net->read_timeout=(uint) net_read_timeout; /* Timeout for read */
+ net->compress_pkt_nr= net->pkt_nr= 0;
+ net->write_pos=net->read_pos = net->buff;
+ net->last_error[0]= net->sqlstate[0] =0;
+
+ net->compress=0; net->reading_or_writing=0;
+ net->where_b = net->remain_in_buf=0;
+ net->last_errno=0;
+
+ if (pvio != 0) /* If real connection */
+ {
+ ma_pvio_get_handle(pvio, &net->fd);
+ ma_pvio_blocking(pvio, 1, 0);
+ ma_pvio_fast_send(pvio);
+ }
+ return 0;
+}
+
+void ma_net_end(NET *net)
+{
+ free(net->buff);
+ net->buff=0;
+}
+
+/* Realloc the packet buffer */
+
+static my_bool net_realloc(NET *net, size_t length)
+{
+ uchar *buff;
+ size_t pkt_length;
+
+ if (length >= net->max_packet_size)
+ {
+ net->error=1;
+ net->pvio->set_error(net->pvio->mysql, CR_NET_PACKET_TOO_LARGE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
+ /* reallocate buffer:
+ size= pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE */
+ if (!(buff=(uchar*) realloc(net->buff,
+ pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE)))
+ {
+ net->error=1;
+ return(1);
+ }
+ net->buff=net->write_pos=buff;
+ net->buff_end=buff+(net->max_packet=(unsigned long)pkt_length);
+ return(0);
+}
+
+/* Remove unwanted characters from connection */
+void ma_net_clear(NET *net)
+{
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ return;
+ net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */
+ net->write_pos=net->buff;
+ return;
+}
+
+/* Flush write_buffer if not empty. */
+int ma_net_flush(NET *net)
+{
+ int error=0;
+
+ /* don't flush if pipelined query is in progress */
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ return 0;
+
+ if (net->buff != net->write_pos)
+ {
+ error=ma_net_real_write(net,(char*) net->buff,
+ (size_t) (net->write_pos - net->buff));
+ net->write_pos=net->buff;
+ }
+ if (net->compress)
+ net->pkt_nr= net->compress_pkt_nr;
+ return(error);
+}
+
+/*****************************************************************************
+ ** Write something to server/client buffer
+ *****************************************************************************/
+
+/*
+ ** Write a logical packet with packet header
+ ** Format: Packet length (3 bytes), packet number(1 byte)
+ ** When compression is used a 3 byte compression length is added
+ ** NOTE: If compression is used the original package is destroyed!
+ */
+
+int ma_net_write(NET *net, const uchar *packet, size_t len)
+{
+ uchar buff[NET_HEADER_SIZE];
+ while (len >= MAX_PACKET_LENGTH)
+ {
+ const ulong max_len= MAX_PACKET_LENGTH;
+ int3store(buff,max_len);
+ buff[3]= (uchar)net->pkt_nr++;
+ if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
+ ma_net_write_buff(net, (char *)packet, max_len))
+ return 1;
+ packet+= max_len;
+ len-= max_len;
+ }
+ /* write last remaining packet, size can be zero */
+ int3store(buff, len);
+ buff[3]= (uchar)net->pkt_nr++;
+ if (ma_net_write_buff(net,(char*) buff,NET_HEADER_SIZE) ||
+ ma_net_write_buff(net, (char *)packet, len))
+ return 1;
+ return 0;
+}
+
+int ma_net_write_command(NET *net, uchar command,
+ const char *packet, size_t len,
+ my_bool disable_flush)
+{
+ uchar buff[NET_HEADER_SIZE+1];
+ size_t buff_size= NET_HEADER_SIZE + 1;
+ size_t length= 1 + len; /* 1 extra byte for command */
+ int rc;
+
+ buff[NET_HEADER_SIZE]= 0;
+ buff[4]=command;
+
+ if (length >= MAX_PACKET_LENGTH)
+ {
+ len= MAX_PACKET_LENGTH - 1;
+ do
+ {
+ int3store(buff, MAX_PACKET_LENGTH);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+
+ if (ma_net_write_buff(net, (char *)buff, buff_size) ||
+ ma_net_write_buff(net, packet, len))
+ return(1);
+ packet+= len;
+ length-= MAX_PACKET_LENGTH;
+ len= MAX_PACKET_LENGTH;
+ buff_size= NET_HEADER_SIZE; /* don't send command for further packets */
+ } while (length >= MAX_PACKET_LENGTH);
+ len= length;
+ }
+ int3store(buff,length);
+ buff[3]= (net->compress) ? 0 :(uchar) (net->pkt_nr++);
+ rc= test (ma_net_write_buff(net,(char *)buff, buff_size) ||
+ ma_net_write_buff(net,packet,len));
+ if (!rc && !disable_flush)
+ return test(ma_net_flush(net));
+ return rc;
+}
+
+
+static int ma_net_write_buff(NET *net,const char *packet, size_t len)
+{
+ size_t left_length;
+
+ if (!len)
+ return 0;
+
+ if (net->max_packet > MAX_PACKET_LENGTH &&
+ net->compress)
+ left_length= (size_t)(MAX_PACKET_LENGTH - (net->write_pos - net->buff));
+ else
+ left_length=(size_t) (net->buff_end - net->write_pos);
+
+ if (len > left_length)
+ {
+ if (net->write_pos != net->buff)
+ {
+ memcpy((char*) net->write_pos,packet,left_length);
+ if (ma_net_real_write(net,(char*) net->buff,
+ (size_t)(net->write_pos - net->buff) + left_length))
+ return 1;
+ packet+=left_length;
+ len-=left_length;
+ net->write_pos= net->buff;
+ }
+ if (net->compress)
+ {
+ /* uncompressed length is stored in 3 bytes,so
+ packet can't be > 0xFFFFFF */
+ left_length= MAX_PACKET_LENGTH;
+ while (len > left_length)
+ {
+ if (ma_net_real_write(net, packet, left_length))
+ return 1;
+ packet+= left_length;
+ len-= left_length;
+ }
+ }
+ if (len > net->max_packet)
+ return(test(ma_net_real_write(net, packet, len)));
+ }
+ memcpy((char*) net->write_pos,packet,len);
+ net->write_pos+=len;
+ return 0;
+}
+
+unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong length);
+
+/* Read and write using timeouts */
+
+int ma_net_real_write(NET *net, const char *packet, size_t len)
+{
+ ssize_t length;
+ char *pos,*end;
+
+ if (net->error == 2)
+ return(-1); /* socket can't be used */
+
+ net->reading_or_writing=2;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ size_t complen;
+ uchar *b;
+ uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
+ if (!(b=(uchar*) malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1)))
+ {
+ net->pvio->set_error(net->pvio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ net->error=2;
+ net->reading_or_writing=0;
+ return(1);
+ }
+ memcpy(b+header_length,packet,len);
+
+ if (_mariadb_compress(net, (unsigned char*) b+header_length,&len,&complen))
+ {
+ complen=0;
+ }
+ int3store(&b[NET_HEADER_SIZE],complen);
+ int3store(b,len);
+ b[3]=(uchar) (net->compress_pkt_nr++);
+ len+= header_length;
+ packet= (char*) b;
+ }
+#endif /* HAVE_COMPRESS */
+
+ pos=(char*) packet; end=pos+len;
+ while (pos != end)
+ {
+ if ((length=ma_pvio_write(net->pvio,(uchar *)pos,(size_t) (end-pos))) <= 0)
+ {
+ int save_errno= errno;
+ char errmsg[100];
+
+ net->error=2; /* Close socket */
+ strerror_r(save_errno, errmsg, 100);
+ net->pvio->set_error(net->pvio->mysql, CR_ERR_NET_WRITE, SQLSTATE_UNKNOWN, 0,
+ errmsg, save_errno);
+ net->reading_or_writing=0;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ free((char*) packet);
+#endif
+ return(1);
+ }
+ pos+=length;
+ }
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ free((char*) packet);
+#endif
+ net->reading_or_writing=0;
+ return(((int) (pos != end)));
+}
+
+/*****************************************************************************
+ ** Read something from server/clinet
+ *****************************************************************************/
+static ulong ma_real_read(NET *net, size_t *complen)
+{
+ uchar *pos;
+ ssize_t length;
+ uint i;
+ ulong len=packet_error;
+ size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
+ NET_HEADER_SIZE);
+ *complen = 0;
+
+ net->reading_or_writing=1;
+
+ pos = net->buff + net->where_b; /* net->packet -4 */
+ for (i=0 ; i < 2 ; i++)
+ {
+ while (remain > 0)
+ {
+ /* First read is done with non blocking mode */
+ if ((length=ma_pvio_cache_read(net->pvio, pos,remain)) <= 0L)
+ {
+ len= packet_error;
+ net->error=2; /* Close socket */
+ goto end;
+ }
+ remain -= (ulong) length;
+ pos+= (ulong) length;
+ }
+
+ if (i == 0)
+ { /* First parts is packet length */
+ ulong helping;
+ net->pkt_nr= net->buff[net->where_b + 3];
+ net->compress_pkt_nr= ++net->pkt_nr;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ /* complen is > 0 if package is really compressed */
+ *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
+ }
+#endif
+
+ len=uint3korr(net->buff+net->where_b);
+ if (!len)
+ goto end;
+ helping = max(len,(ulong)*complen) + net->where_b;
+ /* The necessary size of net->buff */
+ if (helping >= net->max_packet)
+ {
+ if (net_realloc(net, helping))
+ {
+ len= packet_error; /* Return error */
+ goto end;
+ }
+ }
+ pos=net->buff + net->where_b;
+ remain = len;
+ }
+ }
+
+end:
+ net->reading_or_writing=0;
+ return(len);
+}
+
+ulong ma_net_read(NET *net)
+{
+ size_t len,complen;
+
+#ifdef HAVE_COMPRESS
+ if (!net->compress)
+ {
+#endif
+ len = ma_real_read (net,(size_t *)&complen);
+ if (len == MAX_PACKET_LENGTH)
+ {
+ /* multi packet read */
+ size_t length= 0;
+ ulong last_pos= net->where_b;
+
+ do
+ {
+ length+= len;
+ net->where_b+= (unsigned long)len;
+ len= ma_real_read(net, &complen);
+ } while (len == MAX_PACKET_LENGTH);
+ net->where_b= last_pos;
+ if (len != packet_error)
+ len+= length;
+ }
+ net->read_pos = net->buff + net->where_b;
+ if (len != packet_error)
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ return (ulong)len;
+#ifdef HAVE_COMPRESS
+ }
+ else
+ {
+ /*
+ compressed protocol:
+
+ --------------------------------------
+ packet_length 3
+ sequence_id 1
+ uncompressed_length 3
+ --------------------------------------
+ compressed data packet_length - 7
+ --------------------------------------
+
+ Another packet will follow if:
+ packet_length == MAX_PACKET_LENGTH
+
+ Last package will be identified by
+ - packet_length is zero (special case)
+ - packet_length < MAX_PACKET_LENGTH
+ */
+
+ size_t packet_length,
+ buffer_length;
+ size_t current= 0, start= 0;
+ my_bool is_multi_packet= 0;
+
+ /* check if buffer is empty */
+ if (!net->remain_in_buf)
+ {
+ buffer_length= 0;
+ }
+ else
+ {
+ /* save position and restore \0 character */
+ buffer_length= net->buf_length;
+ current= net->buf_length - net->remain_in_buf;
+ start= current;
+ net->buff[net->buf_length - net->remain_in_buf]=net->save_char;
+ }
+ for (;;)
+ {
+ if (buffer_length - current >= 4)
+ {
+ uchar *pos= net->buff + current;
+ packet_length= uint3korr(pos);
+
+ /* check if we have last package (special case: zero length) */
+ if (!packet_length)
+ {
+ current+= 4; /* length + sequence_id,
+ no more data will follow */
+ break;
+ }
+ if (packet_length + 4 <= buffer_length - current)
+ {
+ if (!is_multi_packet)
+ {
+ current= current + packet_length + 4;
+ }
+ else
+ {
+ /* remove packet_header */
+ memmove(net->buff + current,
+ net->buff + current + 4,
+ buffer_length - current);
+ buffer_length-= 4;
+ current+= packet_length;
+ }
+ /* do we have last packet ? */
+ if (packet_length != MAX_PACKET_LENGTH)
+ {
+ is_multi_packet= 0;
+ break;
+ }
+ else
+ is_multi_packet= 1;
+ if (start)
+ {
+ memmove(net->buff, net->buff + start,
+ buffer_length - start);
+ /* decrease buflen*/
+ buffer_length-= start;
+ start= 0;
+ }
+ continue;
+ }
+ }
+ if (start)
+ {
+ memmove(net->buff, net->buff + start, buffer_length - start);
+ /* decrease buflen and current */
+ current -= start;
+ buffer_length-= start;
+ start= 0;
+ }
+
+ net->where_b=(unsigned long)buffer_length;
+
+ if ((packet_length = ma_real_read(net,(size_t *)&complen)) == packet_error)
+ return packet_error;
+ if (_mariadb_uncompress(net, (unsigned char*) net->buff + net->where_b, &packet_length, &complen))
+ {
+ net->error=2; /* caller will close socket */
+ net->pvio->set_error(net->pvio->mysql, CR_ERR_NET_UNCOMPRESS, SQLSTATE_UNKNOWN, 0);
+ return packet_error;
+ }
+ buffer_length+= complen;
+ }
+ /* set values */
+ net->buf_length= (unsigned long)buffer_length;
+ net->remain_in_buf= (unsigned long)(buffer_length - current);
+ net->read_pos= net->buff + start + 4;
+ len= current - start - 4;
+ if (is_multi_packet)
+ len-= 4;
+ net->save_char= net->read_pos[len]; /* Must be saved */
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ }
+#endif
+ return (ulong)len;
+}
+
+int net_add_multi_command(NET *net, uchar command, const uchar *packet,
+ size_t length)
+{
+ if (net->extension->multi_status == COM_MULTI_OFF)
+ {
+ return(1);
+ }
+ /* don't increase packet number */
+ net->compress_pkt_nr= net->pkt_nr= 0;
+ return ma_net_write_command(net, command, (const char *)packet, length, 1);
+}
+
diff --git a/libmariadb/libmariadb/ma_password.c b/libmariadb/libmariadb/ma_password.c
new file mode 100644
index 00000000..eb6fe6a8
--- /dev/null
+++ b/libmariadb/libmariadb/ma_password.c
@@ -0,0 +1,162 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+ 2016,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 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/* password checking routines */
+/*****************************************************************************
+ The main idea is that no password are sent between client & server on
+ connection and that no password are saved in mysql in a decodable form.
+
+ On connection a random string is generated and sent to the client.
+ The client generates a new string with a random generator inited with
+ the hash values from the password and the sent string.
+ This 'check' string is sent to the server where it is compared with
+ a string generated from the stored hash_value of the password and the
+ random string.
+
+ The password is saved (in user.password) by using the PASSWORD() function in
+ mysql.
+
+ Example:
+ update user set password=PASSWORD("hello") where user="test"
+ This saves a hashed number as a string in the password field.
+*****************************************************************************/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <ma_sha1.h>
+#include "mysql.h"
+
+
+void ma_randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
+{ /* For mysql 3.21.# */
+#ifdef HAVE_purify
+ memset((char*) rand_st, 0m sizeof(*rand_st)); /* Avoid UMC warnings */
+#endif
+ rand_st->max_value= 0x3FFFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ rand_st->seed1=seed1%rand_st->max_value ;
+ rand_st->seed2=seed2%rand_st->max_value;
+}
+
+double rnd(struct rand_struct *rand_st)
+{
+ rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
+ rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
+ return (((double) rand_st->seed1)/rand_st->max_value_dbl);
+}
+
+void ma_hash_password(ulong *result, const char *password, size_t len)
+{
+ register ulong nr=1345345333L, add=7, nr2=0x12345671L;
+ ulong tmp;
+ const char *password_end= password + len;
+ for (; password < password_end; password++)
+ {
+ if (*password == ' ' || *password == '\t')
+ continue; /* skip space in password */
+ tmp= (ulong) (uchar) *password;
+ nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
+ nr2+=(nr2 << 8) ^ nr;
+ add+=tmp;
+ }
+ result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
+ result[1]=nr2 & (((ulong) 1L << 31) -1L);
+ return;
+}
+
+/*
+ * Generate a new message based on message and password
+ * The same thing is done in client and server and the results are checked.
+ */
+
+/* scramble for 4.1 servers
+ * Code based on php_nysqlnd_scramble function from PHP's mysqlnd extension,
+ * written by Andrey Hristov (andrey@php.net)
+ * License: PHP License 3.0
+ */
+void my_crypt(unsigned char *buffer, const unsigned char *s1, const unsigned char *s2, size_t len)
+{
+ const unsigned char *s1_end= s1 + len;
+ while (s1 < s1_end) {
+ *buffer++= *s1++ ^ *s2++;
+ }
+}
+
+void ma_scramble_41(const unsigned char *buffer, const char *scramble, const char *password)
+{
+ _MA_SHA1_CTX context;
+ unsigned char sha1[SHA1_MAX_LENGTH];
+ unsigned char sha2[SHA1_MAX_LENGTH];
+
+
+ /* Phase 1: hash password */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char *)password, strlen((char *)password));
+ ma_SHA1Final(sha1, &context);
+
+ /* Phase 2: hash sha1 */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH);
+ ma_SHA1Final(sha2, &context);
+
+ /* Phase 3: hash scramble + sha2 */
+ ma_SHA1Init(&context);
+ ma_SHA1Update(&context, (unsigned char *)scramble, SCRAMBLE_LENGTH);
+ ma_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH);
+ ma_SHA1Final((unsigned char *)buffer, &context);
+
+ /* let's crypt buffer now */
+ my_crypt((uchar *)buffer, (const unsigned char *)buffer, (const unsigned char *)sha1, SHA1_MAX_LENGTH);
+}
+/* }}} */
+
+void ma_make_scrambled_password(char *to,const char *password)
+{
+ ulong hash_res[2];
+ ma_hash_password(hash_res,password, strlen(password));
+ sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
+}
+
+/*
+ * Generate a new message based on message and password
+ * The same thing is done in client and server and the results are checked.
+ */
+char *ma_scramble_323(char *to, const char *message, const char *password)
+{
+ struct rand_struct rand_st;
+ ulong hash_pass[2], hash_message[2];
+
+ if (password && password[0])
+ {
+ char extra, *to_start=to;
+ const char *end_scramble323= message + SCRAMBLE_LENGTH_323;
+ ma_hash_password(hash_pass,password, (uint) strlen(password));
+ /* Don't use strlen, could be > SCRAMBLE_LENGTH_323 ! */
+ ma_hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
+ ma_randominit(&rand_st, hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ for (; message < end_scramble323; message++)
+ *to++= (char) (floor(rnd(&rand_st) * 31) + 64);
+ extra=(char) (floor(rnd(&rand_st) * 31));
+ while (to_start != to)
+ *(to_start++)^= extra;
+ }
+ *to= 0;
+ return to;
+}
diff --git a/libmariadb/libmariadb/ma_pvio.c b/libmariadb/libmariadb/ma_pvio.c
new file mode 100644
index 00000000..a8a653b6
--- /dev/null
+++ b/libmariadb/libmariadb/ma_pvio.c
@@ -0,0 +1,598 @@
+/************************************************************************************
+ Copyright (C) 2015, 2022 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*************************************************************************************/
+
+/* MariaDB Communication IO (PVIO) interface
+
+ PVIO is the interface for client server communication and replaces former vio
+ component of the client library.
+
+ PVIO support various protocols like sockets, pipes and shared memory, which are
+ implemented as plugins and can be extended therefore easily.
+
+ Interface function description:
+
+ ma_pvio_init allocates a new PVIO object which will be used
+ for the current connection
+
+ ma_pvio_close frees all resources of previously allocated PVIO object
+ and closes open connections
+
+ ma_pvio_read reads data from server
+
+ ma_pvio_write sends data to server
+
+ ma_pvio_set_timeout sets timeout for connection, read and write
+
+ ma_pvio_register_callback
+ register callback functions for read and write
+ */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <mysql.h>
+#include <errmsg.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <mariadb_async.h>
+#include <ma_context.h>
+
+/* callback functions for read/write */
+LIST *pvio_callback= NULL;
+
+#define IS_BLOCKING_ERROR() \
+ IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
+ (errno != EAGAIN && errno != EINTR))
+
+/* {{{ MARIADB_PVIO *ma_pvio_init */
+MARIADB_PVIO *ma_pvio_init(MA_PVIO_CINFO *cinfo)
+{
+ /* check connection type and load the required plugin.
+ * Currently we support the following pvio types:
+ * pvio_socket
+ * pvio_namedpipe
+ * pvio_sharedmed
+ */
+ const char *pvio_plugins[] = {"pvio_socket", "pvio_npipe", "pvio_shmem"};
+ int type;
+ MARIADB_PVIO_PLUGIN *pvio_plugin;
+ MARIADB_PVIO *pvio= NULL;
+
+ switch (cinfo->type)
+ {
+ case PVIO_TYPE_UNIXSOCKET:
+ case PVIO_TYPE_SOCKET:
+ type= 0;
+ break;
+#ifdef _WIN32
+ case PVIO_TYPE_NAMEDPIPE:
+ type= 1;
+ break;
+ case PVIO_TYPE_SHAREDMEM:
+ type= 2;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ if (!(pvio_plugin= (MARIADB_PVIO_PLUGIN *)
+ mysql_client_find_plugin(cinfo->mysql,
+ pvio_plugins[type],
+ MARIADB_CLIENT_PVIO_PLUGIN)))
+ {
+ /* error already set in mysql_client_find_plugin */
+ return NULL;
+ }
+
+/* coverity[var_deref_op] */
+ if (!(pvio= (MARIADB_PVIO *)calloc(1, sizeof(MARIADB_PVIO))))
+ {
+ my_set_error(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
+ return NULL;
+ }
+
+ /* register error routine and methods */
+ pvio->methods= pvio_plugin->methods;
+ pvio->set_error= my_set_error;
+ pvio->type= cinfo->type;
+
+ /* set timeout to connect timeout - after successful connect we will set
+ * correct values for read and write */
+ if (pvio->methods->set_timeout)
+ {
+ pvio->methods->set_timeout(pvio, PVIO_CONNECT_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ pvio->methods->set_timeout(pvio, PVIO_READ_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ pvio->methods->set_timeout(pvio, PVIO_WRITE_TIMEOUT, cinfo->mysql->options.connect_timeout);
+ }
+
+ if (!(pvio->cache= calloc(1, PVIO_READ_AHEAD_CACHE_SIZE)))
+ {
+ PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0);
+ free(pvio);
+ return NULL;
+ }
+ pvio->cache_size= 0;
+ pvio->cache_pos= pvio->cache;
+
+ return pvio;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_is_alive */
+my_bool ma_pvio_is_alive(MARIADB_PVIO *pvio)
+{
+ if (!pvio)
+ return FALSE;
+ if (pvio->methods->is_alive)
+ return pvio->methods->is_alive(pvio);
+ return TRUE;
+}
+/* }}} */
+
+/* {{{ int ma_pvio_fast_send */
+int ma_pvio_fast_send(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->methods->fast_send)
+ return 1;
+ return pvio->methods->fast_send(pvio);
+}
+/* }}} */
+
+/* {{{ int ma_pvio_keepalive */
+int ma_pvio_keepalive(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->methods->keepalive)
+ return 1;
+ return pvio->methods->keepalive(pvio);
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_set_timeout */
+my_bool ma_pvio_set_timeout(MARIADB_PVIO *pvio,
+ enum enum_pvio_timeout type,
+ int timeout)
+{
+ if (!pvio)
+ return 1;
+
+ if (pvio->methods->set_timeout)
+ return pvio->methods->set_timeout(pvio, type, timeout);
+ return 1;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_read_async */
+static size_t ma_pvio_read_async(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t res= 0;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ int timeout= pvio->timeout[PVIO_READ_TIMEOUT];
+
+ if (!pvio->methods->async_read)
+ {
+ PVIO_SET_ERROR(pvio->mysql, CR_ASYNC_NOT_SUPPORTED, unknown_sqlstate, 0);
+ return -1;
+ }
+
+ for (;;)
+ {
+ if (pvio->methods->async_read)
+ res= pvio->methods->async_read(pvio, buffer, length);
+ if (res >= 0 || IS_BLOCKING_ERROR())
+ return res;
+ b->events_to_wait_for= MYSQL_WAIT_READ;
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_read */
+ssize_t ma_pvio_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t r= -1;
+ if (!pvio)
+ return -1;
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ {
+ r=
+#if defined(HAVE_TLS) && !defined(HAVE_SCHANNEL)
+ (pvio->ctls) ? ma_tls_read_async(pvio, buffer, length) :
+#endif
+ (ssize_t)ma_pvio_read_async(pvio, buffer, length);
+ goto end;
+ }
+ else
+ {
+ if (IS_PVIO_ASYNC(pvio))
+ {
+ /*
+ If switching from non-blocking to blocking API usage, set the socket
+ back to blocking mode.
+ */
+ my_bool old_mode;
+ ma_pvio_blocking(pvio, TRUE, &old_mode);
+ }
+ }
+
+ /* secure connection */
+#ifdef HAVE_TLS
+ if (pvio->ctls)
+ {
+ r= ma_pvio_tls_read(pvio->ctls, buffer, length);
+ goto end;
+ }
+#endif
+ if (pvio->methods->read)
+ r= pvio->methods->read(pvio, buffer, length);
+end:
+ if (pvio_callback)
+ {
+ void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ callback= p->data;
+ callback(0, pvio->mysql, buffer, r);
+ p= p->next;
+ }
+ }
+ if (r > 0)
+ pvio->bytes_read+= r;
+ return r;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_cache_read */
+ssize_t ma_pvio_cache_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
+{
+ ssize_t r;
+
+ if (!pvio)
+ return -1;
+
+ if (!pvio->cache)
+ return ma_pvio_read(pvio, buffer, length);
+
+ if (pvio->cache + pvio->cache_size > pvio->cache_pos)
+ {
+ ssize_t remaining = pvio->cache + pvio->cache_size - pvio->cache_pos;
+ assert(remaining > 0);
+ r= MIN((ssize_t)length, remaining);
+ memcpy(buffer, pvio->cache_pos, r);
+ pvio->cache_pos+= r;
+ }
+ else if (length >= PVIO_READ_AHEAD_CACHE_MIN_SIZE)
+ {
+ r= ma_pvio_read(pvio, buffer, length);
+ }
+ else
+ {
+ r= ma_pvio_read(pvio, pvio->cache, PVIO_READ_AHEAD_CACHE_SIZE);
+ if (r > 0)
+ {
+ if (length < (size_t)r)
+ {
+ pvio->cache_size= r;
+ pvio->cache_pos= pvio->cache + length;
+ r= length;
+ }
+ memcpy(buffer, pvio->cache, r);
+ }
+ }
+ return r;
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_write_async */
+static ssize_t ma_pvio_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ int timeout= pvio->timeout[PVIO_WRITE_TIMEOUT];
+
+ for (;;)
+ {
+ res= pvio->methods->async_write(pvio, buffer, length);
+ if (res >= 0 || IS_BLOCKING_ERROR())
+ return res;
+ b->events_to_wait_for= MYSQL_WAIT_WRITE;
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
+ return -1;
+ }
+}
+/* }}} */
+
+/* {{{ size_t ma_pvio_write */
+ssize_t ma_pvio_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t r= 0;
+
+ if (!pvio)
+ return -1;
+
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ {
+ r=
+#if defined(HAVE_TLS) && !defined(HAVE_SCHANNEL)
+ (pvio->ctls) ? ma_tls_write_async(pvio, buffer, length) :
+#endif
+ ma_pvio_write_async(pvio, buffer, length);
+ goto end;
+ }
+ else
+ {
+ if (IS_PVIO_ASYNC(pvio))
+ {
+ /*
+ If switching from non-blocking to blocking API usage, set the socket
+ back to blocking mode.
+ */
+ my_bool old_mode;
+ ma_pvio_blocking(pvio, TRUE, &old_mode);
+ }
+ }
+ /* secure connection */
+#ifdef HAVE_TLS
+ if (pvio->ctls)
+ {
+ r= ma_pvio_tls_write(pvio->ctls, buffer, length);
+ goto end;
+ }
+#endif
+
+ if (pvio->methods->write)
+ r= pvio->methods->write(pvio, buffer, length);
+end:
+ if (pvio_callback)
+ {
+ void (*callback)(int mode, MYSQL *mysql, const uchar *buffer, size_t length);
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ callback= p->data;
+ callback(1, pvio->mysql, buffer, r);
+ p= p->next;
+ }
+ }
+ if (r > 0)
+ pvio->bytes_sent+= r;
+ return r;
+}
+/* }}} */
+
+/* {{{ void ma_pvio_close */
+void ma_pvio_close(MARIADB_PVIO *pvio)
+{
+ /* free internal structures and close connection */
+ if (pvio)
+ {
+#ifdef HAVE_TLS
+ if (pvio->ctls)
+ {
+ ma_pvio_tls_close(pvio->ctls);
+ free(pvio->ctls);
+ }
+#endif
+ if (pvio && pvio->methods->close)
+ pvio->methods->close(pvio);
+
+ if (pvio->cache)
+ free(pvio->cache);
+
+ free(pvio);
+ }
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_get_handle */
+my_bool ma_pvio_get_handle(MARIADB_PVIO *pvio, void *handle)
+{
+ if (pvio && pvio->methods->get_handle)
+ return pvio->methods->get_handle(pvio, handle);
+ return 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_wait_async */
+static my_bool
+ma_pvio_wait_async(struct mysql_async_context *b, enum enum_pvio_io_event event,
+ int timeout)
+{
+ switch (event)
+ {
+ case VIO_IO_EVENT_READ:
+ b->events_to_wait_for = MYSQL_WAIT_READ;
+ break;
+ case VIO_IO_EVENT_WRITE:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE;
+ break;
+ case VIO_IO_EVENT_CONNECT:
+ b->events_to_wait_for = MYSQL_WAIT_WRITE | IF_WIN(0, MYSQL_WAIT_EXCEPT);
+ break;
+ }
+
+ if (timeout >= 0)
+ {
+ b->events_to_wait_for |= MYSQL_WAIT_TIMEOUT;
+ b->timeout_value= timeout;
+ }
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ return (b->events_occurred & MYSQL_WAIT_TIMEOUT) ? 0 : 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_wait_io_or_timeout */
+int ma_pvio_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout)
+{
+ if (pvio)
+ {
+ if (IS_PVIO_ASYNC_ACTIVE(pvio))
+ return ma_pvio_wait_async(pvio->mysql->options.extension->async_context,
+ (is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE,
+ timeout);
+
+ if (pvio && pvio->methods->wait_io_or_timeout)
+ return pvio->methods->wait_io_or_timeout(pvio, is_read, timeout);
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_connect */
+my_bool ma_pvio_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
+{
+ if (pvio && pvio->methods->connect)
+ return pvio->methods->connect(pvio, cinfo);
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_blocking */
+my_bool ma_pvio_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode)
+{
+ if (pvio && pvio->methods->blocking)
+ return pvio->methods->blocking(pvio, block, previous_mode) != 0;
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_bool ma_pvio_is_blocking */
+my_bool ma_pvio_is_blocking(MARIADB_PVIO *pvio)
+{
+ if (pvio && pvio->methods->is_blocking)
+ return pvio->methods->is_blocking(pvio);
+ return 1;
+}
+/* }}} */
+
+/* {{{ ma_pvio_has_data */
+my_bool ma_pvio_has_data(MARIADB_PVIO *pvio, ssize_t *data_len)
+{
+ /* check if we still have unread data in cache */
+ if (pvio && pvio->cache)
+ if (pvio->cache_pos > pvio->cache)
+ return test(pvio->cache_pos - pvio->cache);
+ if (pvio && pvio->methods->has_data)
+ return pvio->methods->has_data(pvio, data_len);
+ return 1;
+}
+/* }}} */
+
+#ifdef HAVE_TLS
+/* {{{ my_bool ma_pvio_start_ssl */
+my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
+{
+ if (!pvio || !pvio->mysql)
+ return 1;
+ CLEAR_CLIENT_ERROR(pvio->mysql);
+ if (!(pvio->ctls= ma_pvio_tls_init(pvio->mysql)))
+ {
+ return 1;
+ }
+ if (ma_pvio_tls_connect(pvio->ctls))
+ {
+ free(pvio->ctls);
+ pvio->ctls= NULL;
+ return 1;
+ }
+
+ /* default behaviour:
+ 1. peer certificate verification
+ 2. verify CN (requires option ssl_verify_check)
+ 3. verrify finger print
+ */
+ if (pvio->mysql->options.extension->tls_verify_server_cert &&
+ ma_pvio_tls_verify_server_cert(pvio->ctls))
+ return 1;
+
+ if (pvio->mysql->options.extension &&
+ ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
+ (pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0])))
+ {
+ if (ma_pvio_tls_check_fp(pvio->ctls,
+ pvio->mysql->options.extension->tls_fp,
+ pvio->mysql->options.extension->tls_fp_list))
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+#endif
+
+/* {{{ ma_pvio_register_callback */
+int ma_pvio_register_callback(my_bool register_callback,
+ void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length))
+{
+ LIST *list;
+
+ if (!callback_function)
+ return 1;
+
+ /* plugin will unregister in it's deinit function */
+ if (register_callback)
+ {
+ list= (LIST *)malloc(sizeof(LIST));
+
+ list->data= (void *)callback_function;
+ pvio_callback= list_add(pvio_callback, list);
+ }
+ else /* unregister callback function */
+ {
+ LIST *p= pvio_callback;
+ while (p)
+ {
+ if (p->data == callback_function)
+ {
+ list_delete(pvio_callback, p);
+ break;
+ }
+ p= p->next;
+ }
+ }
+ return 0;
+}
+/* }}} */
diff --git a/libmariadb/libmariadb/ma_sha1.c b/libmariadb/libmariadb/ma_sha1.c
new file mode 100644
index 00000000..04c5760e
--- /dev/null
+++ b/libmariadb/libmariadb/ma_sha1.c
@@ -0,0 +1,326 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+ *****************************************************************************/
+
+/* This code came from the PHP project, initially written by
+ Stefan Esser */
+
+
+#include "ma_global.h"
+#include "string.h"
+
+/* This code is heavily based on the PHP md5 implementation */
+
+#include "ma_sha1.h"
+
+
+static void ma_SHA1Transform(uint32[5], const unsigned char[64]);
+static void ma_SHA1Encode(unsigned char *, uint32 *, unsigned int);
+static void ma_SHA1Decode(uint32 *, const unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic SHA1 functions.
+*/
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((x) ^ (y) ^ (z))
+#define H(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define I(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+*/
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* W[i]
+*/
+#define W(i) ( tmp=x[(i-3)&15]^x[(i-8)&15]^x[(i-14)&15]^x[i&15], \
+ (x[i&15]=ROTATE_LEFT(tmp, 1)) )
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+*/
+#define FF(a, b, c, d, e, w) { \
+ (e) += F ((b), (c), (d)) + (w) + (uint32)(0x5A827999); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define GG(a, b, c, d, e, w) { \
+ (e) += G ((b), (c), (d)) + (w) + (uint32)(0x6ED9EBA1); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define HH(a, b, c, d, e, w) { \
+ (e) += H ((b), (c), (d)) + (w) + (uint32)(0x8F1BBCDC); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+#define II(a, b, c, d, e, w) { \
+ (e) += I ((b), (c), (d)) + (w) + (uint32)(0xCA62C1D6); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+}
+
+
+/* {{{ ma_SHA1Init
+ * SHA1 initialization. Begins an SHA1 operation, writing a new context.
+ */
+void ma_SHA1Init(_MA_SHA1_CTX * context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xc3d2e1f0;
+}
+/* }}} */
+
+/* {{{ ma_SHA1Update
+ SHA1 block update operation. Continues an SHA1 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void ma_SHA1Update(_MA_SHA1_CTX * context, const unsigned char *input,
+ size_t inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((uint32) inputLen << 3))
+ < ((uint32) inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((uint32) inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy
+ ((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
+ ma_SHA1Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ ma_SHA1Transform(context->state, &input[i]);
+
+ index = 0;
+ } else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy
+ ((unsigned char*) & context->buffer[index], (unsigned char*) & input[i],
+ inputLen - i);
+}
+/* }}} */
+
+/* {{{ ma_SHA1Final
+ SHA1 finalization. Ends an SHA1 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void ma_SHA1Final(unsigned char digest[20], _MA_SHA1_CTX * context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ bits[7] = context->count[0] & 0xFF;
+ bits[6] = (context->count[0] >> 8) & 0xFF;
+ bits[5] = (context->count[0] >> 16) & 0xFF;
+ bits[4] = (context->count[0] >> 24) & 0xFF;
+ bits[3] = context->count[1] & 0xFF;
+ bits[2] = (context->count[1] >> 8) & 0xFF;
+ bits[1] = (context->count[1] >> 16) & 0xFF;
+ bits[0] = (context->count[1] >> 24) & 0xFF;
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ ma_SHA1Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ ma_SHA1Update(context, bits, 8);
+
+ /* Store state in digest */
+ ma_SHA1Encode(digest, context->state, 20);
+
+ /* Zeroize sensitive information.
+ */
+ memset((unsigned char*) context, 0, sizeof(*context));
+}
+/* }}} */
+
+/* {{{ ma_SHA1Transform
+ * SHA1 basic transformation. Transforms state based on block.
+ */
+static void ma_SHA1Transform(uint32 state[5], const unsigned char block[64])
+{
+ uint32 a = state[0], b = state[1], c = state[2];
+ uint32 d = state[3], e = state[4], x[16], tmp;
+
+ ma_SHA1Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, e, x[0]); /* 1 */
+ FF(e, a, b, c, d, x[1]); /* 2 */
+ FF(d, e, a, b, c, x[2]); /* 3 */
+ FF(c, d, e, a, b, x[3]); /* 4 */
+ FF(b, c, d, e, a, x[4]); /* 5 */
+ FF(a, b, c, d, e, x[5]); /* 6 */
+ FF(e, a, b, c, d, x[6]); /* 7 */
+ FF(d, e, a, b, c, x[7]); /* 8 */
+ FF(c, d, e, a, b, x[8]); /* 9 */
+ FF(b, c, d, e, a, x[9]); /* 10 */
+ FF(a, b, c, d, e, x[10]); /* 11 */
+ FF(e, a, b, c, d, x[11]); /* 12 */
+ FF(d, e, a, b, c, x[12]); /* 13 */
+ FF(c, d, e, a, b, x[13]); /* 14 */
+ FF(b, c, d, e, a, x[14]); /* 15 */
+ FF(a, b, c, d, e, x[15]); /* 16 */
+ FF(e, a, b, c, d, W(16)); /* 17 */
+ FF(d, e, a, b, c, W(17)); /* 18 */
+ FF(c, d, e, a, b, W(18)); /* 19 */
+ FF(b, c, d, e, a, W(19)); /* 20 */
+
+ /* Round 2 */
+ GG(a, b, c, d, e, W(20)); /* 21 */
+ GG(e, a, b, c, d, W(21)); /* 22 */
+ GG(d, e, a, b, c, W(22)); /* 23 */
+ GG(c, d, e, a, b, W(23)); /* 24 */
+ GG(b, c, d, e, a, W(24)); /* 25 */
+ GG(a, b, c, d, e, W(25)); /* 26 */
+ GG(e, a, b, c, d, W(26)); /* 27 */
+ GG(d, e, a, b, c, W(27)); /* 28 */
+ GG(c, d, e, a, b, W(28)); /* 29 */
+ GG(b, c, d, e, a, W(29)); /* 30 */
+ GG(a, b, c, d, e, W(30)); /* 31 */
+ GG(e, a, b, c, d, W(31)); /* 32 */
+ GG(d, e, a, b, c, W(32)); /* 33 */
+ GG(c, d, e, a, b, W(33)); /* 34 */
+ GG(b, c, d, e, a, W(34)); /* 35 */
+ GG(a, b, c, d, e, W(35)); /* 36 */
+ GG(e, a, b, c, d, W(36)); /* 37 */
+ GG(d, e, a, b, c, W(37)); /* 38 */
+ GG(c, d, e, a, b, W(38)); /* 39 */
+ GG(b, c, d, e, a, W(39)); /* 40 */
+
+ /* Round 3 */
+ HH(a, b, c, d, e, W(40)); /* 41 */
+ HH(e, a, b, c, d, W(41)); /* 42 */
+ HH(d, e, a, b, c, W(42)); /* 43 */
+ HH(c, d, e, a, b, W(43)); /* 44 */
+ HH(b, c, d, e, a, W(44)); /* 45 */
+ HH(a, b, c, d, e, W(45)); /* 46 */
+ HH(e, a, b, c, d, W(46)); /* 47 */
+ HH(d, e, a, b, c, W(47)); /* 48 */
+ HH(c, d, e, a, b, W(48)); /* 49 */
+ HH(b, c, d, e, a, W(49)); /* 50 */
+ HH(a, b, c, d, e, W(50)); /* 51 */
+ HH(e, a, b, c, d, W(51)); /* 52 */
+ HH(d, e, a, b, c, W(52)); /* 53 */
+ HH(c, d, e, a, b, W(53)); /* 54 */
+ HH(b, c, d, e, a, W(54)); /* 55 */
+ HH(a, b, c, d, e, W(55)); /* 56 */
+ HH(e, a, b, c, d, W(56)); /* 57 */
+ HH(d, e, a, b, c, W(57)); /* 58 */
+ HH(c, d, e, a, b, W(58)); /* 59 */
+ HH(b, c, d, e, a, W(59)); /* 60 */
+
+ /* Round 4 */
+ II(a, b, c, d, e, W(60)); /* 61 */
+ II(e, a, b, c, d, W(61)); /* 62 */
+ II(d, e, a, b, c, W(62)); /* 63 */
+ II(c, d, e, a, b, W(63)); /* 64 */
+ II(b, c, d, e, a, W(64)); /* 65 */
+ II(a, b, c, d, e, W(65)); /* 66 */
+ II(e, a, b, c, d, W(66)); /* 67 */
+ II(d, e, a, b, c, W(67)); /* 68 */
+ II(c, d, e, a, b, W(68)); /* 69 */
+ II(b, c, d, e, a, W(69)); /* 70 */
+ II(a, b, c, d, e, W(70)); /* 71 */
+ II(e, a, b, c, d, W(71)); /* 72 */
+ II(d, e, a, b, c, W(72)); /* 73 */
+ II(c, d, e, a, b, W(73)); /* 74 */
+ II(b, c, d, e, a, W(74)); /* 75 */
+ II(a, b, c, d, e, W(75)); /* 76 */
+ II(e, a, b, c, d, W(76)); /* 77 */
+ II(d, e, a, b, c, W(77)); /* 78 */
+ II(c, d, e, a, b, W(78)); /* 79 */
+ II(b, c, d, e, a, W(79)); /* 80 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Zeroize sensitive information. */
+ memset((unsigned char*) x, 0, sizeof(x));
+}
+/* }}} */
+
+/* {{{ ma_SHA1Encode
+ Encodes input (uint32) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void ma_SHA1Encode(unsigned char *output, uint32 *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char) ((input[i] >> 24) & 0xff);
+ output[j + 1] = (unsigned char) ((input[i] >> 16) & 0xff);
+ output[j + 2] = (unsigned char) ((input[i] >> 8) & 0xff);
+ output[j + 3] = (unsigned char) (input[i] & 0xff);
+ }
+}
+/* }}} */
+
+/* {{{ ma_SHA1Decode
+ Decodes input (unsigned char) into output (uint32). Assumes len is
+ a multiple of 4.
+ */
+static void ma_SHA1Decode(uint32 *output, const unsigned char * input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((uint32) input[j + 3]) | (((uint32) input[j + 2]) << 8) |
+ (((uint32) input[j + 1]) << 16) | (((uint32) input[j]) << 24);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/libmariadb/libmariadb/ma_stmt_codec.c b/libmariadb/libmariadb/ma_stmt_codec.c
new file mode 100644
index 00000000..afb46b25
--- /dev/null
+++ b/libmariadb/libmariadb/ma_stmt_codec.c
@@ -0,0 +1,1362 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program 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 <http://www.gnu.org/licenses>
+ 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
+*****************************************************************************/
+
+/* The implementation for prepared statements was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "mysql.h"
+#include <math.h> /* ceil() */
+#include <limits.h>
+
+#ifdef WIN32
+#include <malloc.h>
+#endif
+
+#define MYSQL_SILENT
+
+/* ranges for C-binding */
+#define UINT_MAX32 0xFFFFFFFFL
+#define UINT_MAX24 0x00FFFFFF
+#define UINT_MAX16 0xFFFF
+#ifndef INT_MIN8
+#define INT_MIN8 (~0x7F)
+#define INT_MAX8 0x7F
+#endif
+#define UINT_MAX8 0xFF
+
+ #define MAX_DOUBLE_STRING_REP_LENGTH 300
+#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
+#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
+#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
+#endif
+
+#define MAX_DBL_STR (3 + DBL_MANT_DIG - DBL_MIN_EXP)
+
+#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
+/* First check for ANSI C99 definition: */
+#ifdef ULLONG_MAX
+#define ULONGLONG_MAX ULLONG_MAX
+#else
+#define ULONGLONG_MAX ((unsigned long long)(~0ULL))
+#endif
+#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
+
+#define YY_PART_YEAR 70
+
+MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1];
+my_bool mysql_ps_subsystem_initialized= 0;
+
+
+#define NUMERIC_TRUNCATION(val,min_range, max_range)\
+ ((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
+
+
+void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
+{
+ while (len-- != 0) *--dst = *--src;
+}
+
+/* {{{ ps_fetch_from_1_to_8_bytes */
+void ps_fetch_from_1_to_8_bytes(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row, unsigned int byte_count)
+{
+ my_bool is_unsigned= test(field->flags & UNSIGNED_FLAG);
+ r_param->buffer_length= byte_count;
+ switch (byte_count) {
+ case 1:
+ *(uchar *)r_param->buffer= **row;
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(uchar *)r_param->buffer > INT_MAX8;
+ break;
+ case 2:
+ shortstore(r_param->buffer, ((ushort) sint2korr(*row)));
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(ushort *)r_param->buffer > INT_MAX16;
+ break;
+ case 4:
+ {
+ longstore(r_param->buffer, ((uint32)sint4korr(*row)));
+ *r_param->error= is_unsigned != r_param->is_unsigned && *(uint32 *)r_param->buffer > INT_MAX32;
+ }
+ break;
+ case 8:
+ {
+ ulonglong val= (ulonglong)sint8korr(*row);
+ longlongstore(r_param->buffer, val);
+ *r_param->error= is_unsigned != r_param->is_unsigned && val > LONGLONG_MAX ;
+ }
+ break;
+ default:
+ r_param->buffer_length= 0;
+ break;
+ }
+ (*row)+= byte_count;
+}
+/* }}} */
+
+static unsigned long long my_strtoull(const char *str, size_t len, const char **end, int *err)
+{
+ unsigned long long val = 0;
+ const char *p = str;
+ const char *end_str = p + len;
+
+ for (; p < end_str; p++)
+ {
+ if (*p < '0' || *p > '9')
+ break;
+
+ if (val > ULONGLONG_MAX /10 || val*10 > ULONGLONG_MAX - (*p - '0'))
+ {
+ *err = ERANGE;
+ break;
+ }
+ val = val * 10 + *p -'0';
+ }
+
+ if (p == str)
+ /* Did not parse anything.*/
+ *err = ERANGE;
+
+ *end = p;
+ return val;
+}
+
+static long long my_strtoll(const char *str, size_t len, const char **end, int *err)
+{
+ unsigned long long uval = 0;
+ const char *p = str;
+ const char *end_str = p + len;
+ int neg;
+
+ while (p < end_str && isspace(*p))
+ p++;
+
+ if (p == end_str)
+ {
+ *end = p;
+ *err = ERANGE;
+ return 0;
+ }
+
+ neg = *p == '-';
+ if (neg)
+ p++;
+
+ uval = my_strtoull(p, (end_str - p), &p, err);
+ *end = p;
+ if (*err)
+ return uval;
+
+ if (!neg)
+ {
+ /* Overflow of the long long range. */
+ if (uval > LONGLONG_MAX)
+ {
+ *end = p - 1;
+ uval = LONGLONG_MAX;
+ *err = ERANGE;
+ }
+ return uval;
+ }
+
+ if (uval == (unsigned long long) LONGLONG_MIN)
+ return LONGLONG_MIN;
+
+ if (uval > LONGLONG_MAX)
+ {
+ *end = p - 1;
+ uval = LONGLONG_MIN;
+ *err = ERANGE;
+ }
+
+ return -1LL * uval;
+}
+
+
+static long long my_atoll(const char *str, const char *end_str, int *error)
+{
+ const char *p=str;
+ const char *end;
+ long long ret;
+ while (p < end_str && isspace(*p))
+ p++;
+
+ ret = my_strtoll(p, end_str - p, &end, error);
+
+ while(end < end_str && isspace(*end))
+ end++;
+
+ if(end != end_str)
+ *error= 1;
+
+ return ret;
+}
+
+
+static unsigned long long my_atoull(const char *str, const char *end_str, int *error)
+{
+ const char *p = str;
+ const char *end;
+ unsigned long long ret;
+
+ while (p < end_str && isspace(*p))
+ p++;
+
+ ret = my_strtoull(p, end_str - p, &end, error);
+
+ while(end < end_str && isspace(*end))
+ end++;
+
+ if(end != end_str)
+ *error= 1;
+
+ return ret;
+}
+
+double my_atod(const char *number, const char *end, int *error)
+{
+ double val= 0.0;
+ char buffer[MAX_DBL_STR + 1];
+ int len= (int)(end - number);
+
+ *error= errno= 0;
+
+ if (len > MAX_DBL_STR)
+ {
+ *error= 1;
+ len= MAX_DBL_STR;
+ }
+
+ memcpy(buffer, number, len);
+ buffer[len]= '\0';
+
+ val= strtod(buffer, NULL);
+
+ if (errno)
+ *error= errno;
+
+ return val;
+}
+
+
+/*
+ strtoui() version, that works for non-null terminated strings
+*/
+static unsigned int my_strtoui(const char *str, size_t len, const char **end, int *err)
+{
+ unsigned long long ull = my_strtoull(str, len, end, err);
+ if (ull > UINT_MAX)
+ *err = ERANGE;
+ return (unsigned int)ull;
+}
+
+/*
+ Parse time, in MySQL format.
+
+ the input string needs is in form "hour:minute:second[.fraction]"
+ hour, minute and second can have leading zeroes or not,
+ they are not necessarily 2 chars.
+
+ Hour must be < 838, minute < 60, second < 60
+ Only 6 places of fraction are considered, the value is truncated after 6 places.
+*/
+static const unsigned int frac_mul[] = { 1000000,100000,10000,1000,100,10 };
+
+static int parse_time(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
+{
+ int err= 0;
+ const char *p = str;
+ const char *end = str + length;
+ size_t frac_len;
+ int ret=1;
+
+ tm->hour = my_strtoui(p, end-p, &p, &err);
+ if (err || tm->hour > 838 || p == end || *p != ':' )
+ goto end;
+
+ p++;
+ tm->minute = my_strtoui(p, end-p, &p, &err);
+ if (err || tm->minute > 59 || p == end || *p != ':')
+ goto end;
+
+ p++;
+ tm->second = my_strtoui(p, end-p, &p, &err);
+ if (err || tm->second > 59)
+ goto end;
+
+ ret = 0;
+ tm->second_part = 0;
+
+ if (p == end)
+ goto end;
+
+ /* Check for fractional part*/
+ if (*p != '.')
+ goto end;
+
+ p++;
+ frac_len = MIN(6,end-p);
+
+ tm->second_part = my_strtoui(p, frac_len, &p, &err);
+ if (err)
+ goto end;
+
+ if (frac_len < 6)
+ tm->second_part *= frac_mul[frac_len];
+
+ ret = 0;
+
+ /* Consume whole fractional part, even after 6 digits.*/
+ p += frac_len;
+ while(p < *end_ptr)
+ {
+ if (*p < '0' || *p > '9')
+ break;
+ p++;
+ }
+end:
+ *end_ptr = p;
+ return ret;
+}
+
+
+/*
+ Parse date, in MySQL format.
+
+ The input string needs is in form "year-month-day"
+ year, month and day can have leading zeroes or not,
+ they do not have fixed length.
+
+ Year must be < 10000, month < 12, day < 32
+
+ Years with 2 digits, are converted to values 1970-2069 according to
+ usual rules:
+
+ 00-69 is converted to 2000-2069.
+ 70-99 is converted to 1970-1999.
+*/
+static int parse_date(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
+{
+ int err = 0;
+ const char *p = str;
+ const char *end = str + length;
+ int ret = 1;
+
+ tm->year = my_strtoui(p, end - p, &p, &err);
+ if (err || tm->year > 9999 || p == end || *p != '-')
+ goto end;
+
+ if (p - str == 2) // 2-digit year
+ tm->year += (tm->year >= 70) ? 1900 : 2000;
+
+ p++;
+ tm->month = my_strtoui(p,end -p, &p, &err);
+ if (err || tm->month > 12 || p == end || *p != '-')
+ goto end;
+
+ p++;
+ tm->day = my_strtoui(p, end -p , &p, &err);
+ if (err || tm->day > 31)
+ goto end;
+
+ ret = 0;
+
+end:
+ *end_ptr = p;
+ return ret;
+}
+
+/*
+ Parse (not null terminated) string representing
+ TIME, DATE, or DATETIME into MYSQL_TIME structure
+
+ The supported formats by this functions are
+ - TIME : [-]hours:minutes:seconds[.fraction]
+ - DATE : year-month-day
+ - DATETIME : year-month-day<space>hours:minutes:seconds[.fraction]
+
+ cf https://dev.mysql.com/doc/refman/8.0/en/datetime.html
+
+ Whitespaces are trimmed from the start and end of the string.
+ The function ignores junk at the end of the string.
+
+ Parts of date of time do not have fixed length, so that parsing is compatible with server.
+ However server supports additional formats, e.g YYYYMMDD, HHMMSS, which this function does
+ not support.
+
+*/
+int str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
+{
+ const char *p = str;
+ const char *end = str + length;
+ int is_time = 0;
+
+ if (!p)
+ goto error;
+
+ while (p < end && isspace(*p))
+ p++;
+ while (p < end && isspace(end[-1]))
+ end--;
+
+ if (end -p < 5)
+ goto error;
+
+ if (*p == '-')
+ {
+ tm->neg = 1;
+ /* Only TIME can't be negative.*/
+ is_time = 1;
+ p++;
+ }
+ else
+ {
+ int i;
+ tm->neg = 0;
+ /*
+ Date parsing (in server) accepts leading zeroes, thus position of the delimiters
+ is not fixed. Scan the string to find out what we need to parse.
+ */
+ for (i = 1; p + i < end; i++)
+ {
+ if(p[i] == '-' || p [i] == ':')
+ {
+ is_time = p[i] == ':';
+ break;
+ }
+ }
+ }
+
+ if (is_time)
+ {
+ if (parse_time(p, end - p, &p, tm))
+ goto error;
+
+ tm->year = tm->month = tm->day = 0;
+ tm->time_type = MYSQL_TIMESTAMP_TIME;
+ return 0;
+ }
+
+ if (parse_date(p, end - p, &p, tm))
+ goto error;
+
+ if (p == end || p[0] != ' ')
+ {
+ tm->hour = tm->minute = tm->second = tm->second_part = 0;
+ tm->time_type = MYSQL_TIMESTAMP_DATE;
+ return 0;
+ }
+
+ /* Skip space. */
+ p++;
+ if (parse_time(p, end - p, &p, tm))
+ goto error;
+
+ /* In DATETIME, hours must be < 24.*/
+ if (tm->hour > 23)
+ goto error;
+
+ tm->time_type = MYSQL_TIMESTAMP_DATETIME;
+ return 0;
+
+error:
+ memset(tm, 0, sizeof(*tm));
+ tm->time_type = MYSQL_TIMESTAMP_ERROR;
+ return 1;
+}
+
+
+static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len)
+{
+ int error= 0;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8) || error > 0;
+ int1store(r_param->buffer, (uchar) val);
+ r_param->buffer_length= sizeof(uchar);
+ }
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16) || error > 0;
+ shortstore(r_param->buffer, (short)val);
+ r_param->buffer_length= sizeof(short);
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ longlong val= my_atoll(buffer, buffer + len, &error);
+ *r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0;
+ longstore(r_param->buffer, (int32)val);
+ r_param->buffer_length= sizeof(uint32);
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ longlong val= r_param->is_unsigned ? (longlong)my_atoull(buffer, buffer + len, &error) : my_atoll(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ longlongstore(r_param->buffer, val);
+ r_param->buffer_length= sizeof(longlong);
+ }
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double val= my_atod(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ doublestore((uchar *)r_param->buffer, val);
+ r_param->buffer_length= sizeof(double);
+ }
+ break;
+ case MYSQL_TYPE_FLOAT:
+ {
+ float val= (float)my_atod(buffer, buffer + len, &error);
+ *r_param->error= error > 0; /* no need to check for truncation */
+ floatstore((uchar *)r_param->buffer, val);
+ r_param->buffer_length= sizeof(float);
+ }
+ break;
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ MYSQL_TIME *tm= (MYSQL_TIME *)r_param->buffer;
+ str_to_TIME(buffer, len, tm);
+ break;
+ }
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ default:
+ {
+ if (len >= r_param->offset)
+ {
+ char *start= buffer + r_param->offset; /* stmt_fetch_column sets offset */
+ char *end= buffer + len;
+ size_t copylen= 0;
+
+ if (start < end)
+ {
+ copylen= end - start;
+ if (r_param->buffer_length)
+ memcpy(r_param->buffer, start, MIN(copylen, r_param->buffer_length));
+ }
+ if (copylen < r_param->buffer_length)
+ ((char *)r_param->buffer)[copylen]= 0;
+ *r_param->error= (copylen > r_param->buffer_length);
+
+ }
+ *r_param->length= (ulong)len;
+ }
+ break;
+ }
+}
+
+static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, longlong val, my_bool is_unsigned)
+{
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_TINY:
+ *(uchar *)r_param->buffer= (uchar)(val & 0xff);
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8);
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ shortstore(r_param->buffer, (short)(val & 0xffff));
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16);
+ r_param->buffer_length= 2;
+ break;
+ case MYSQL_TYPE_LONG:
+ longstore(r_param->buffer, (int32)(val & 0xffffffff));
+ *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32);
+ r_param->buffer_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ *r_param->error= (val < 0 && r_param->is_unsigned != is_unsigned);
+ longlongstore(r_param->buffer, val);
+ r_param->buffer_length= 8;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ volatile double dbl;
+
+ dbl= (is_unsigned) ? ulonglong2double((ulonglong)val) : (double)val;
+ doublestore(r_param->buffer, dbl);
+
+ *r_param->error = (dbl != ceil(dbl)) ||
+ (is_unsigned ? (ulonglong )dbl != (ulonglong)val :
+ (longlong)dbl != (longlong)val);
+
+ r_param->buffer_length= 8;
+ break;
+ }
+ case MYSQL_TYPE_FLOAT:
+ {
+ volatile float fval;
+ fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
+ floatstore((uchar *)r_param->buffer, fval);
+ *r_param->error= (fval != ceilf(fval)) ||
+ (is_unsigned ? (ulonglong)fval != (ulonglong)val :
+ (longlong)fval != val);
+ r_param->buffer_length= 4;
+ }
+ break;
+ default:
+ {
+ char *buffer;
+ char *endptr;
+ uint len;
+ my_bool zf_truncated= 0;
+
+ buffer= alloca(MAX(field->length, 22));
+ endptr= ma_ll2str(val, buffer, is_unsigned ? 10 : -10);
+ len= (uint)(endptr - buffer);
+
+ /* check if field flag is zerofill */
+ if (field->flags & ZEROFILL_FLAG)
+ {
+ uint display_width= MAX(field->length, len);
+ if (display_width < r_param->buffer_length)
+ {
+ ma_bmove_upp(buffer + display_width, buffer + len, len);
+ /* coverity[bad_memset] */
+ memset((void*) buffer, (int) '0', display_width - len);
+ len= display_width;
+ }
+ else
+ zf_truncated= 1;
+ }
+ convert_froma_string(r_param, buffer, len);
+ *r_param->error+= zf_truncated;
+ }
+ break;
+ }
+}
+
+
+/* {{{ ps_fetch_null */
+static
+void ps_fetch_null(MYSQL_BIND *r_param __attribute__((unused)),
+ const MYSQL_FIELD * field __attribute__((unused)),
+ unsigned char **row __attribute__((unused)))
+{
+ /* do nothing */
+}
+/* }}} */
+
+#define GET_LVALUE_FROM_ROW(is_unsigned, data, ucast, scast)\
+ (is_unsigned) ? (longlong)(ucast) *(longlong *)(data) : (longlong)(scast) *(longlong *)(data)
+/* {{{ ps_fetch_int8 */
+static
+void ps_fetch_int8(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch(r_param->buffer_type) {
+ case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ default:
+ {
+ uchar val= **row;
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong) val : (longlong)(signed char)val;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 1;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int16 */
+static
+void ps_fetch_int16(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break;
+ default:
+ {
+ short sval= sint2korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ushort) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 2;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int32 */
+static
+void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch (r_param->buffer_type) {
+/* case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break; */
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
+ break;
+ default:
+ {
+ int32 sval= sint4korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(uint32) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 4;
+ }
+ break;
+ }
+}
+/* }}} */
+
+
+/* {{{ ps_fetch_int64 */
+static
+void ps_fetch_int64(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
+ unsigned char **row)
+{
+ switch(r_param->buffer_type)
+ {
+/* case MYSQL_TYPE_TINY:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
+ break;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
+ break; */
+ case MYSQL_TYPE_LONGLONG:
+ ps_fetch_from_1_to_8_bytes(r_param, field, row, 8);
+ break;
+ default:
+ {
+ longlong sval= (longlong)sint8korr(*row);
+ longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ulonglong) sval : (longlong)sval;
+ convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
+ (*row) += 8;
+ }
+ break;
+ }
+}
+/* }}} */
+
+static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, float val, int size __attribute__((unused)))
+{
+ double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
+ char *buf= (char *)r_param->buffer;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
+ *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
+ (double)((int8)*buf));
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ {
+ if (r_param->is_unsigned)
+ {
+ ushort sval= (ushort)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ } else {
+ short sval= (short)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ }
+ r_param->buffer_length= 2;
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ uint32 lval= (uint32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ } else {
+ int32 lval= (int32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ }
+ r_param->buffer_length= 4;
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ ulonglong llval= (ulonglong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ } else {
+ longlong llval= (longlong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ }
+ r_param->buffer_length= 8;
+ }
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double dval= (double)val;
+ memcpy(buf, &dval, sizeof(double));
+ r_param->buffer_length= 8;
+ }
+ break;
+ default:
+ {
+ char buff[MAX_DOUBLE_STRING_REP_LENGTH];
+ size_t length;
+
+ length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
+
+ if (field->decimals >= NOT_FIXED_DEC)
+ {
+ length= ma_gcvt(val, MY_GCVT_ARG_FLOAT, (int)length, buff, NULL);
+ }
+ else
+ {
+ length= ma_fcvt(val, field->decimals, buff, NULL);
+ }
+
+ /* check if ZEROFILL flag is active */
+ if (field->flags & ZEROFILL_FLAG)
+ {
+ /* enough space available ? */
+ if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
+ break;
+ ma_bmove_upp(buff + field->length, buff + length, length);
+ /* coverity[bad_memset] */
+ memset((void*) buff, (int) '0', field->length - length);
+ length= field->length;
+ }
+
+ convert_froma_string(r_param, buff, length);
+ }
+ break;
+ }
+}
+
+static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size __attribute__((unused)))
+{
+ double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
+ char *buf= (char *)r_param->buffer;
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_TINY:
+ *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
+ *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
+ (double)((int8)*buf));
+ r_param->buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ {
+ if (r_param->is_unsigned)
+ {
+ ushort sval= (ushort)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ } else {
+ short sval= (short)val;
+ shortstore(buf, sval);
+ *r_param->error= check_trunc_val != (double)sval;
+ }
+ r_param->buffer_length= 2;
+ }
+ break;
+ case MYSQL_TYPE_LONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ uint32 lval= (uint32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ } else {
+ int32 lval= (int32)val;
+ longstore(buf, lval);
+ *r_param->error= (check_trunc_val != (double)lval);
+ }
+ r_param->buffer_length= 4;
+ }
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ if (r_param->is_unsigned)
+ {
+ ulonglong llval= (ulonglong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ } else {
+ longlong llval= (longlong)val;
+ longlongstore(buf, llval);
+ *r_param->error= (check_trunc_val != (double)llval);
+ }
+ r_param->buffer_length= 8;
+ }
+ break;
+ case MYSQL_TYPE_FLOAT:
+ {
+ float fval= (float)val;
+ memcpy(buf, &fval, sizeof(float));
+ *r_param->error= (*(float*)buf != fval);
+ r_param->buffer_length= 4;
+ }
+ break;
+ default:
+ {
+ char buff[MAX_DOUBLE_STRING_REP_LENGTH];
+ size_t length;
+
+ length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
+
+ if (field->decimals >= NOT_FIXED_DEC)
+ {
+ length= ma_gcvt(val, MY_GCVT_ARG_DOUBLE, (int)length, buff, NULL);
+ }
+ else
+ {
+ length= ma_fcvt(val, field->decimals, buff, NULL);
+ }
+
+ /* check if ZEROFILL flag is active */
+ if (field->flags & ZEROFILL_FLAG)
+ {
+ /* enough space available ? */
+ if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
+ break;
+ ma_bmove_upp(buff + field->length, buff + length, length);
+ /* coverity [bad_memset] */
+ memset((void*) buff, (int) '0', field->length - length);
+ length= field->length;
+ }
+ convert_froma_string(r_param, buff, length);
+ }
+ break;
+ }
+}
+
+
+/* {{{ ps_fetch_double */
+static
+void ps_fetch_double(MYSQL_BIND *r_param, const MYSQL_FIELD * field , unsigned char **row)
+{
+ switch (r_param->buffer_type)
+ {
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double *value= (double *)r_param->buffer;
+ float8get(*value, *row);
+ r_param->buffer_length= 8;
+ }
+ break;
+ default:
+ {
+ double value;
+ float8get(value, *row);
+ convert_from_double(r_param, field, value, sizeof(double));
+ }
+ break;
+ }
+ (*row)+= 8;
+}
+/* }}} */
+
+/* {{{ ps_fetch_float */
+static
+void ps_fetch_float(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
+{
+ switch(r_param->buffer_type)
+ {
+ case MYSQL_TYPE_FLOAT:
+ {
+ float *value= (float *)r_param->buffer;
+ float4get(*value, *row);
+ r_param->buffer_length= 4;
+ *r_param->error= 0;
+ }
+ break;
+ default:
+ {
+ float value;
+ memcpy(&value, *row, sizeof(float));
+ float4get(value, (char *)*row);
+ convert_from_float(r_param, field, value, sizeof(float));
+ }
+ break;
+ }
+ (*row)+= 4;
+}
+/* }}} */
+
+static void convert_to_datetime(MYSQL_TIME *t, unsigned char **row, uint len, enum enum_field_types type)
+{
+ memset(t, 0, sizeof(MYSQL_TIME));
+
+ /* binary protocol for datetime:
+ 4-bytes: DATE
+ 7-bytes: DATE + TIME
+ >7 bytes: DATE + TIME with second_part
+ */
+ if (len)
+ {
+ unsigned char *to= *row;
+ int has_date= 0;
+ uint offset= 7;
+
+ if (type == MYSQL_TYPE_TIME)
+ {
+ t->neg= to[0];
+ t->day= (ulong) sint4korr(to + 1);
+ t->time_type= MYSQL_TIMESTAMP_TIME;
+ offset= 8;
+ to++;
+ } else
+ {
+ t->year= (uint) sint2korr(to);
+ t->month= (uint) to[2];
+ t->day= (uint) to[3];
+ t->time_type= MYSQL_TIMESTAMP_DATE;
+ if (type == MYSQL_TYPE_DATE)
+ return;
+ has_date= 1;
+ }
+
+ if (len > 4)
+ {
+ t->hour= (uint) to[4];
+ if (type == MYSQL_TYPE_TIME)
+ t->hour+= t->day * 24;
+ t->minute= (uint) to[5];
+ t->second= (uint) to[6];
+ if (has_date)
+ t->time_type= MYSQL_TIMESTAMP_DATETIME;
+ }
+ if (len > offset)
+ {
+ t->second_part= (ulong)sint4korr(to+7);
+ }
+ }
+}
+
+
+/* {{{ ps_fetch_datetime */
+static
+void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field,
+ unsigned char **row)
+{
+ MYSQL_TIME *t= (MYSQL_TIME *)r_param->buffer;
+ unsigned int len= net_field_length(row);
+
+ switch (r_param->buffer_type) {
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ convert_to_datetime(t, row, len, field->type);
+ break;
+ case MYSQL_TYPE_DATE:
+ convert_to_datetime(t, row, len, field->type);
+ break;
+ case MYSQL_TYPE_TIME:
+ convert_to_datetime(t, row, len, field->type);
+ t->year= t->day= t->month= 0;
+ break;
+ case MYSQL_TYPE_YEAR:
+ {
+ MYSQL_TIME tm;
+ convert_to_datetime(&tm, row, len, field->type);
+ shortstore(r_param->buffer, tm.year);
+ break;
+ }
+ default:
+ {
+ char dtbuffer[60];
+ MYSQL_TIME tm;
+ size_t length;
+ convert_to_datetime(&tm, row, len, field->type);
+
+ switch(field->type) {
+ case MYSQL_TYPE_DATE:
+ length= sprintf(dtbuffer, "%04u-%02u-%02u", tm.year, tm.month, tm.day);
+ break;
+ case MYSQL_TYPE_TIME:
+ length= sprintf(dtbuffer, "%s%02u:%02u:%02u", (tm.neg ? "-" : ""), tm.hour, tm.minute, tm.second);
+ if (field->decimals && field->decimals <= 6)
+ {
+ char ms[8];
+ sprintf(ms, ".%06lu", tm.second_part);
+ if (field->decimals < 6)
+ ms[field->decimals + 1]= 0;
+ length+= strlen(ms);
+ strcat(dtbuffer, ms);
+ }
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ length= sprintf(dtbuffer, "%04u-%02u-%02u %02u:%02u:%02u", tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second);
+ if (field->decimals && field->decimals <= 6)
+ {
+ char ms[8];
+ sprintf(ms, ".%06lu", tm.second_part);
+ if (field->decimals < 6)
+ ms[field->decimals + 1]= 0;
+ length+= strlen(ms);
+ strcat(dtbuffer, ms);
+ }
+ break;
+ default:
+ dtbuffer[0]= 0;
+ length= 0;
+ break;
+ }
+ convert_froma_string(r_param, dtbuffer, length);
+ break;
+ }
+ }
+ (*row) += len;
+}
+/* }}} */
+
+/* {{{ ps_fetch_string */
+static
+void ps_fetch_string(MYSQL_BIND *r_param,
+ const MYSQL_FIELD *field __attribute__((unused)),
+ unsigned char **row)
+{
+ /* C-API differs from PHP. While PHP just converts string to string,
+ C-API needs to convert the string to the defined type with in
+ the result bind buffer.
+ */
+ ulong field_length= net_field_length(row);
+
+ convert_froma_string(r_param, (char *)*row, field_length);
+ (*row) += field_length;
+}
+/* }}} */
+
+/* {{{ ps_fetch_bin */
+static
+void ps_fetch_bin(MYSQL_BIND *r_param,
+ const MYSQL_FIELD *field,
+ unsigned char **row)
+{
+ if (field->charsetnr == 63)
+ {
+ ulong field_length= *r_param->length= net_field_length(row);
+ uchar *current_pos= (*row) + r_param->offset,
+ *end= (*row) + field_length;
+ size_t copylen= 0;
+
+ if (current_pos < end)
+ {
+ copylen= end - current_pos;
+ if (r_param->buffer_length)
+ memcpy(r_param->buffer, current_pos, MIN(copylen, r_param->buffer_length));
+ }
+ if (copylen < r_param->buffer_length &&
+ (r_param->buffer_type == MYSQL_TYPE_STRING ||
+ r_param->buffer_type == MYSQL_TYPE_JSON))
+ ((char *)r_param->buffer)[copylen]= 0;
+ *r_param->error= copylen > r_param->buffer_length;
+ (*row)+= field_length;
+ }
+ else
+ ps_fetch_string(r_param, field, row);
+}
+/* }}} */
+
+/* {{{ _mysqlnd_init_ps_subsystem */
+void mysql_init_ps_subsystem(void)
+{
+ memset(mysql_ps_fetch_functions, 0, sizeof(mysql_ps_fetch_functions));
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].func= ps_fetch_null;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NULL].max_len = 0;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY].max_len = 4;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].max_len = 6;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
+ mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].max_len = 4;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_INT24].max_len = 8;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len = 11;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len = 20;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
+ mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIME].max_len = 17;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATE].max_len = 10;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].max_len = 30;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].max_len = 30;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bin;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_BIT].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_STRING].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_JSON].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len = -1;
+
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR;
+ mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len = -1;
+
+ mysql_ps_subsystem_initialized= 1;
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+
diff --git a/libmariadb/libmariadb/ma_string.c b/libmariadb/libmariadb/ma_string.c
new file mode 100644
index 00000000..9a62e06e
--- /dev/null
+++ b/libmariadb/libmariadb/ma_string.c
@@ -0,0 +1,163 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+/*
+ Code for handling strings which can grow dynamically.
+ Copyright Monty Program KB.
+ By monty.
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+
+my_bool ma_init_dynamic_string(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc, size_t alloc_increment)
+{
+ uint length;
+
+ if (!alloc_increment)
+ alloc_increment=128;
+ length=1;
+ if (init_str && (length= (uint) 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 ma_dynstr_set(DYNAMIC_STRING *str, const char *init_str)
+{
+ uint length;
+
+ if (init_str && (length= (uint) 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 ma_dynstr_realloc(DYNAMIC_STRING *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 ma_dynstr_append(DYNAMIC_STRING *str, const char *append)
+{
+ return ma_dynstr_append_mem(str,append,strlen(append));
+}
+
+my_bool ma_dynstr_append_quoted(DYNAMIC_STRING *str,
+ const char *append, size_t len,
+ char quote)
+{
+ size_t additional= str->alloc_increment;
+ size_t lim= additional;
+ uint i;
+
+ if (ma_dynstr_realloc(str, len + additional + 2))
+ return TRUE;
+ str->str[str->length++]= quote;
+ for (i= 0; i < len; i++)
+ {
+ register char c= append[i];
+ if (c == quote || c == '\\')
+ {
+ if (!lim)
+ {
+ if (ma_dynstr_realloc(str, additional))
+ return TRUE;
+ lim= additional;
+ }
+ lim--;
+ str->str[str->length++]= '\\';
+ }
+ str->str[str->length++]= c;
+ }
+ str->str[str->length++]= quote;
+ return FALSE;
+}
+
+my_bool ma_dynstr_append_mem(DYNAMIC_STRING *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 ma_dynstr_free(DYNAMIC_STRING *str)
+{
+ if (str->str)
+ {
+ free(str->str);
+ str->str=0;
+ }
+}
+
+char *ma_strmake(register char *dst, register const char *src, size_t length)
+{
+ while (length--)
+ if (! (*dst++ = *src++))
+ return dst-1;
+ *dst=0;
+ return dst;
+}
diff --git a/libmariadb/libmariadb/ma_time.c b/libmariadb/libmariadb/ma_time.c
new file mode 100644
index 00000000..460c32d4
--- /dev/null
+++ b/libmariadb/libmariadb/ma_time.c
@@ -0,0 +1,65 @@
+/****************************************************************************
+ Copyright (C) 2013 Monty Program 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 <http://www.gnu.org/licenses>
+ 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
+*****************************************************************************/
+#include <ma_global.h>
+#include <mysql.h>
+#include <stdio.h>
+
+
+size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len,
+ unsigned int digits)
+{
+ size_t length;
+
+ if (!time_str || !len)
+ return 0;
+
+ if (digits == AUTO_SEC_PART_DIGITS)
+ digits= (tm->second_part) ? SEC_PART_DIGITS : 0;
+
+ switch(tm->time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ length= snprintf(time_str, len, "%04u-%02u-%02u", tm->year, tm->month, tm->day);
+ digits= 0;
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ length= snprintf(time_str, len, "%04u-%02u-%02u %02u:%02u:%02u",
+ tm->year, tm->month, tm->day, tm->hour, tm->minute, tm->second);
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ length= snprintf(time_str, len, "%s%02u:%02u:%02u",
+ (tm->neg ? "-" : ""), tm->hour, tm->minute, tm->second);
+ break;
+ default:
+ time_str[0]= '\0';
+ return 0;
+ break;
+ }
+ if (digits && (len < length))
+ {
+ char helper[16];
+ snprintf(helper, 16, ".%%0%du", digits);
+ length+= snprintf(time_str + length, len - length, helper, digits);
+ }
+ return length;
+}
+
diff --git a/libmariadb/libmariadb/ma_tls.c b/libmariadb/libmariadb/ma_tls.c
new file mode 100644
index 00000000..3f48ad8b
--- /dev/null
+++ b/libmariadb/libmariadb/ma_tls.c
@@ -0,0 +1,237 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+
+/*
+ * this is the abstraction layer for communication via SSL.
+ * The following SSL libraries/variants are currently supported:
+ * - openssl
+ * - gnutls
+ * - schannel (windows only)
+ *
+ * Different SSL variants are implemented as plugins
+ * On Windows schannel is implemented as (standard)
+ * built-in plugin.
+ */
+
+#ifdef HAVE_TLS
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <string.h>
+#include <errmsg.h>
+#include <ma_pvio.h>
+#include <ma_tls.h>
+#include <mysql/client_plugin.h>
+#include <mariadb/ma_io.h>
+
+#ifdef HAVE_NONBLOCK
+#include <mariadb_async.h>
+#include <ma_context.h>
+#endif
+
+/* Errors should be handled via pvio callback function */
+my_bool ma_tls_initialized= FALSE;
+unsigned int mariadb_deinitialize_ssl= 1;
+
+const char *tls_protocol_version[]=
+ {"SSLv3", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "Unknown"};
+
+MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql)
+{
+ MARIADB_TLS *ctls= NULL;
+
+ if (!ma_tls_initialized)
+ ma_tls_start(mysql->net.last_error, MYSQL_ERRMSG_SIZE);
+
+ if (!(ctls= (MARIADB_TLS *)calloc(1, sizeof(MARIADB_TLS))))
+ {
+ return NULL;
+ }
+
+ /* register error routine and methods */
+ ctls->pvio= mysql->net.pvio;
+ if (!(ctls->ssl= ma_tls_init(mysql)))
+ {
+ free(ctls);
+ ctls= NULL;
+ }
+ return ctls;
+}
+
+my_bool ma_pvio_tls_connect(MARIADB_TLS *ctls)
+{
+ my_bool rc;
+
+ if ((rc= ma_tls_connect(ctls)))
+ ma_tls_close(ctls);
+ return rc;
+}
+
+ssize_t ma_pvio_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ return ma_tls_read(ctls, buffer, length);
+}
+
+ssize_t ma_pvio_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ return ma_tls_write(ctls, buffer, length);
+}
+
+my_bool ma_pvio_tls_close(MARIADB_TLS *ctls)
+{
+ return ma_tls_close(ctls);
+}
+
+int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls)
+{
+ return ma_tls_verify_server_cert(ctls);
+}
+
+const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls)
+{
+ return ma_tls_get_cipher(ctls);
+}
+
+void ma_pvio_tls_end()
+{
+ ma_tls_end();
+}
+
+int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls)
+{
+ return ma_tls_get_protocol_version(ctls);
+}
+
+const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ int version;
+
+ version= ma_tls_get_protocol_version(ctls);
+ if (version < 0 || version > PROTOCOL_MAX)
+ return tls_protocol_version[PROTOCOL_UNKNOWN];
+ return tls_protocol_version[version];
+}
+
+static signed char ma_hex2int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+ return -1;
+}
+
+static my_bool ma_pvio_tls_compare_fp(const char *cert_fp,
+ unsigned int cert_fp_len,
+ const char *fp, unsigned int fp_len)
+{
+ char *p= (char *)fp,
+ *c;
+
+ /* check length */
+ if (cert_fp_len != 20)
+ return 1;
+
+ /* We support two formats:
+ 2 digits hex numbers, separated by colons (length=59)
+ 20 * 2 digits hex numbers without separators (length = 40)
+ */
+ if (fp_len != (strchr(fp, ':') ? 59 : 40))
+ return 1;
+
+ for(c= (char *)cert_fp; c < cert_fp + cert_fp_len; c++)
+ {
+ signed char d1, d2;
+ if (*p == ':')
+ p++;
+ if (p - fp > (int)fp_len -1)
+ return 1;
+ if ((d1 = ma_hex2int(*p)) == - 1 ||
+ (d2 = ma_hex2int(*(p+1))) == -1 ||
+ (char)(d1 * 16 + d2) != *c)
+ return 1;
+ p+= 2;
+ }
+ return 0;
+}
+
+my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
+{
+ unsigned int cert_fp_len= 64;
+ char *cert_fp= NULL;
+ my_bool rc=1;
+ MYSQL *mysql= ctls->pvio->mysql;
+
+ cert_fp= (char *)malloc(cert_fp_len);
+
+ if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
+ goto end;
+ if (fp)
+ rc= ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, fp, (unsigned int)strlen(fp));
+ else if (fp_list)
+ {
+ MA_FILE *fp;
+ char buff[255];
+
+ if (!(fp = ma_open(fp_list, "r", mysql)))
+ goto end;
+
+ while (ma_gets(buff, sizeof(buff)-1, fp))
+ {
+ /* remove trailing new line character */
+ char *pos= strchr(buff, '\r');
+ if (!pos)
+ pos= strchr(buff, '\n');
+ if (pos)
+ *pos= '\0';
+
+ if (!ma_pvio_tls_compare_fp(cert_fp, cert_fp_len, buff, (unsigned int)strlen(buff)))
+ {
+ /* finger print is valid: close file and exit */
+ ma_close(fp);
+ rc= 0;
+ goto end;
+ }
+ }
+
+ /* No finger print matched - close file and return error */
+ ma_close(fp);
+ }
+
+end:
+ if (cert_fp)
+ free(cert_fp);
+ if (rc)
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Fingerprint verification of server certificate failed");
+ }
+ return rc;
+}
+
+void ma_pvio_tls_set_connection(MYSQL *mysql)
+{
+ ma_tls_set_connection(mysql);
+}
+#endif /* HAVE_TLS */
diff --git a/libmariadb/libmariadb/mariadb_async.c b/libmariadb/libmariadb/mariadb_async.c
new file mode 100644
index 00000000..feb73329
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_async.c
@@ -0,0 +1,1946 @@
+/* Copyright (C) 2012 MariaDB Services and Kristian Nielsen
+ 2015 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+*/
+
+/*
+ MySQL non-blocking client library functions.
+*/
+
+#include "ma_global.h"
+#include "ma_sys.h"
+#include "mysql.h"
+#include "errmsg.h"
+#ifndef LIBMARIADB
+#include "sql_common.h"
+#else
+#include "ma_common.h"
+#endif
+#include "ma_context.h"
+#include "ma_pvio.h"
+#include "mariadb_async.h"
+#include <string.h>
+
+
+#ifdef _WIN32
+/*
+ Windows does not support MSG_DONTWAIT for send()/recv(). So we need to ensure
+ that the socket is non-blocking at the start of every operation.
+*/
+#define WIN_SET_NONBLOCKING(mysql) do { \
+ my_bool old_mode; \
+ if ((mysql)->net.pvio) ma_pvio_blocking((mysql)->net.pvio, FALSE, &old_mode); \
+ } while(0);
+#else
+#define WIN_SET_NONBLOCKING(mysql)
+#endif
+
+extern void mysql_close_slow_part(MYSQL *mysql);
+
+
+void
+my_context_install_suspend_resume_hook(struct mysql_async_context *b,
+ void (*hook)(my_bool, void *),
+ void *user_data)
+{
+ b->suspend_resume_hook= hook;
+ b->suspend_resume_hook_user_data= user_data;
+}
+
+
+/* Asynchronous connect(); socket must already be set non-blocking. */
+int
+my_connect_async(MARIADB_PVIO *pvio,
+ const struct sockaddr *name, uint namelen, int vio_timeout)
+{
+ int res;
+ size_socket s_err_size;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ my_socket sock;
+
+ ma_pvio_get_handle(pvio, &sock);
+
+ /* Make the socket non-blocking. */
+ ma_pvio_blocking(pvio, 0, 0);
+
+ b->events_to_wait_for= 0;
+ /*
+ Start to connect asynchronously.
+ If this will block, we suspend the call and return control to the
+ application context. The application will then resume us when the socket
+ polls ready for write, indicating that the connection attempt completed.
+ */
+ res= connect(sock, name, namelen);
+ if (res != 0)
+ {
+#ifdef _WIN32
+ int wsa_err= WSAGetLastError();
+ if (wsa_err != WSAEWOULDBLOCK)
+ return res;
+ b->events_to_wait_for|= MYSQL_WAIT_EXCEPT;
+#else
+ int err= errno;
+ if (err != EINPROGRESS && err != EALREADY && err != EAGAIN)
+ return res;
+#endif
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ if (vio_timeout >= 0)
+ {
+ b->timeout_value= vio_timeout;
+ b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT;
+ }
+ else
+ b->timeout_value= 0;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ if (b->events_occurred & MYSQL_WAIT_TIMEOUT)
+ return -1;
+
+ s_err_size= sizeof(res);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0)
+ return -1;
+ if (res)
+ {
+ errno= res;
+ return -1;
+ }
+ }
+ return res;
+}
+
+#define IS_BLOCKING_ERROR() \
+ IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \
+ (errno != EAGAIN && errno != EINTR))
+
+#ifdef _AIX
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+#endif
+
+#ifdef HAVE_TLS_FIXME
+static my_bool
+my_ssl_async_check_result(int res, struct mysql_async_context *b, MARIADB_SSL *cssl)
+{
+ int ssl_err;
+ b->events_to_wait_for= 0;
+ if (res >= 0)
+ return 1;
+ ssl_err= SSL_get_error(ssl, res);
+ if (ssl_err == SSL_ERROR_WANT_READ)
+ b->events_to_wait_for|= MYSQL_WAIT_READ;
+ else if (ssl_err == SSL_ERROR_WANT_WRITE)
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ else
+ return 1;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ return 0;
+}
+
+int
+my_ssl_read_async(struct mysql_async_context *b, SSL *ssl,
+ void *buf, int size)
+{
+ int res;
+
+ for (;;)
+ {
+ res= SSL_read(ssl, buf, size);
+ if (my_ssl_async_check_result(res, b, ssl))
+ return res;
+ }
+}
+
+int
+my_ssl_write_async(struct mysql_async_context *b, SSL *ssl,
+ const void *buf, int size)
+{
+ int res;
+
+ for (;;)
+ {
+ res= SSL_write(ssl, buf, size);
+ if (my_ssl_async_check_result(res, b, ssl))
+ return res;
+ }
+}
+#endif /* HAVE_OPENSSL */
+
+
+
+
+/*
+ Now create non-blocking definitions for all the calls that may block.
+
+ Each call FOO gives rise to FOO_start() that prepares the MYSQL object for
+ doing non-blocking calls that can suspend operation mid-way, and then starts
+ the call itself. And a FOO_start_internal trampoline to assist with running
+ the real call in a co-routine that can be suspended. And a FOO_cont() that
+ can continue a suspended operation.
+*/
+
+#define MK_ASYNC_INTERNAL_BODY(call, invoke_args, mysql_val, ret_type, ok_val)\
+ struct call ## _params *parms= (struct call ## _params *)d; \
+ ret_type ret; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ \
+ ret= call invoke_args; \
+ b->ret_result. ok_val = ret; \
+ b->events_to_wait_for= 0;
+
+#define MK_ASYNC_START_BODY(call, mysql_val, parms_assign, err_val, ok_val, extra1) \
+ int res; \
+ struct mysql_async_context *b; \
+ struct call ## _params parms; \
+ \
+ extra1 \
+ b= mysql_val->options.extension->async_context; \
+ parms_assign \
+ \
+ b->active= 1; \
+ res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \
+ b->active= b->suspended= 0; \
+ if (res > 0) \
+ { \
+ /* Suspended. */ \
+ b->suspended= 1; \
+ return b->events_to_wait_for; \
+ } \
+ if (res < 0) \
+ { \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ *ret= err_val; \
+ } \
+ else \
+ *ret= b->ret_result. ok_val; \
+ return 0;
+
+#define MK_ASYNC_CONT_BODY(mysql_val, err_val, ok_val) \
+ int res; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ if (!b->suspended) \
+ { \
+ set_mariadb_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \
+ *ret= err_val; \
+ return 0; \
+ } \
+ \
+ b->active= 1; \
+ b->events_occurred= ready_status; \
+ res= my_context_continue(&b->async_context); \
+ b->active= 0; \
+ if (res > 0) \
+ return b->events_to_wait_for; /* (Still) suspended */ \
+ b->suspended= 0; \
+ if (res < 0) \
+ { \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ *ret= err_val; \
+ } \
+ else \
+ *ret= b->ret_result. ok_val; /* Finished. */ \
+ return 0;
+
+#define MK_ASYNC_INTERNAL_BODY_VOID_RETURN(call, invoke_args, mysql_val) \
+ struct call ## _params *parms= (struct call ## _params *)d; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ \
+ call invoke_args; \
+ b->events_to_wait_for= 0;
+
+#define MK_ASYNC_START_BODY_VOID_RETURN(call, mysql_val, parms_assign, extra1)\
+ int res; \
+ struct mysql_async_context *b; \
+ struct call ## _params parms; \
+ \
+ extra1 \
+ b= mysql_val->options.extension->async_context; \
+ parms_assign \
+ \
+ b->active= 1; \
+ res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \
+ b->active= b->suspended= 0; \
+ if (res > 0) \
+ { \
+ /* Suspended. */ \
+ b->suspended= 1; \
+ return b->events_to_wait_for; \
+ } \
+ if (res < 0) \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ return 0;
+
+#define MK_ASYNC_CONT_BODY_VOID_RETURN(mysql_val) \
+ int res; \
+ struct mysql_async_context *b= \
+ (mysql_val)->options.extension->async_context; \
+ if (!b->suspended) \
+ { \
+ set_mariadb_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \
+ return 0; \
+ } \
+ \
+ b->active= 1; \
+ b->events_occurred= ready_status; \
+ res= my_context_continue(&b->async_context); \
+ b->active= 0; \
+ if (res > 0) \
+ return b->events_to_wait_for; /* (Still) suspended */ \
+ b->suspended= 0; \
+ if (res < 0) \
+ set_mariadb_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \
+ return 0;
+
+
+/* Structure used to pass parameters from mysql_real_connect_start(). */
+struct mysql_real_connect_params {
+ MYSQL *mysql;
+ const char *host;
+ const char *user;
+ const char *passwd;
+ const char *db;
+ unsigned int port;
+ const char *unix_socket;
+ unsigned long client_flags;
+};
+static void
+mysql_real_connect_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_real_connect,
+ (parms->mysql, parms->host, parms->user, parms->passwd, parms->db,
+ parms->port, parms->unix_socket, parms->client_flags),
+ parms->mysql,
+ MYSQL *,
+ r_ptr)
+}
+int STDCALL
+mysql_real_connect_start(MYSQL **ret, MYSQL *mysql, const char *host,
+ const char *user, const char *passwd, const char *db,
+ unsigned int port, const char *unix_socket,
+ unsigned long client_flags)
+{
+MK_ASYNC_START_BODY(
+ mysql_real_connect,
+ mysql,
+ {
+ parms.mysql= mysql;
+ parms.host= host;
+ parms.user= user;
+ parms.passwd= passwd;
+ parms.db= db;
+ parms.port= port;
+ parms.unix_socket= unix_socket;
+ parms.client_flags= client_flags | CLIENT_REMEMBER_OPTIONS;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_real_connect_cont(MYSQL **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_real_query_start(). */
+struct mysql_real_query_params {
+ MYSQL *mysql;
+ const char *stmt_str;
+ unsigned long length;
+};
+static void
+mysql_real_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_real_query,
+ (parms->mysql, parms->stmt_str, parms->length),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_real_query_start(int *ret, MYSQL *mysql, const char *stmt_str, unsigned long length)
+{
+ int res;
+ struct mysql_async_context *b;
+ struct mysql_real_query_params parms;
+
+ b= mysql->options.extension->async_context;
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.stmt_str= stmt_str;
+ parms.length= length;
+ }
+
+ b->active= 1;
+ res= my_context_spawn(&b->async_context, mysql_real_query_start_internal, &parms);
+ b->active= b->suspended= 0;
+ if (res > 0)
+ {
+ /* Suspended. */
+ b->suspended= 1;
+ return b->events_to_wait_for;
+ }
+ if (res < 0)
+ {
+ set_mariadb_error((mysql), CR_OUT_OF_MEMORY, unknown_sqlstate);
+ *ret= 1;
+ }
+ else
+ *ret= b->ret_result.r_int;
+ return 0;
+
+}
+int STDCALL
+mysql_real_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_fetch_row_start(). */
+struct mysql_fetch_row_params {
+ MYSQL_RES *result;
+};
+static void
+mysql_fetch_row_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_fetch_row,
+ (parms->result),
+ parms->result->handle,
+ MYSQL_ROW,
+ r_ptr)
+}
+int STDCALL
+mysql_fetch_row_start(MYSQL_ROW *ret, MYSQL_RES *result)
+{
+MK_ASYNC_START_BODY(
+ mysql_fetch_row,
+ result->handle,
+ {
+ WIN_SET_NONBLOCKING(result->handle)
+ parms.result= result;
+ },
+ NULL,
+ r_ptr,
+ /*
+ If we already fetched all rows from server (eg. mysql_store_result()),
+ then result->handle will be NULL and we cannot suspend. But that is fine,
+ since in this case mysql_fetch_row cannot block anyway. Just return
+ directly.
+ */
+ if (!result->handle)
+ {
+ *ret= mysql_fetch_row(result);
+ return 0;
+ })
+}
+int STDCALL
+mysql_fetch_row_cont(MYSQL_ROW *ret, MYSQL_RES *result, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ result->handle,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_set_character_set_start(). */
+struct mysql_set_character_set_params {
+ MYSQL *mysql;
+ const char *csname;
+};
+static void
+mysql_set_character_set_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_set_character_set,
+ (parms->mysql, parms->csname),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_set_character_set_start(int *ret, MYSQL *mysql, const char *csname)
+{
+MK_ASYNC_START_BODY(
+ mysql_set_character_set,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.csname= csname;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_set_character_set_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_sekect_db_start(). */
+struct mysql_select_db_params {
+ MYSQL *mysql;
+ const char *db;
+};
+static void
+mysql_select_db_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_select_db,
+ (parms->mysql, parms->db),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_select_db_start(int *ret, MYSQL *mysql, const char *db)
+{
+MK_ASYNC_START_BODY(
+ mysql_select_db,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.db= db;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_select_db_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_send_query_start(). */
+struct mysql_send_query_params {
+ MYSQL *mysql;
+ const char *q;
+ unsigned long length;
+};
+static void
+mysql_send_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_send_query,
+ (parms->mysql, parms->q, parms->length),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_send_query_start(int *ret, MYSQL *mysql, const char *q, unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_send_query,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.q= q;
+ parms.length= length;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_send_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_store_result_start(). */
+struct mysql_store_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_store_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_store_result,
+ (parms->mysql),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_store_result_start(MYSQL_RES **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_store_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_store_result_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_free_result_start(). */
+struct mysql_free_result_params {
+ MYSQL_RES *result;
+};
+static void
+mysql_free_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY_VOID_RETURN(
+ mysql_free_result,
+ (parms->result),
+ parms->result->handle)
+}
+int STDCALL
+mysql_free_result_start(MYSQL_RES *result)
+{
+MK_ASYNC_START_BODY_VOID_RETURN(
+ mysql_free_result,
+ result->handle,
+ {
+ WIN_SET_NONBLOCKING(result->handle)
+ parms.result= result;
+ },
+ /*
+ mysql_free_result() can have NULL in result->handle (this happens when all
+ rows have been fetched and mysql_fetch_row() returned NULL.)
+ So we cannot suspend, but it does not matter, as in this case
+ mysql_free_result() cannot block.
+ It is also legitimate to have NULL result, which will do nothing.
+ */
+ if (!result || !result->handle)
+ {
+ mysql_free_result(result);
+ return 0;
+ })
+}
+int STDCALL
+mysql_free_result_cont(MYSQL_RES *result, int ready_status)
+{
+MK_ASYNC_CONT_BODY_VOID_RETURN(result->handle)
+}
+
+/* Structure used to pass parameters from mysql_close_slow_part_start(). */
+struct mysql_close_slow_part_params {
+ MYSQL *sock;
+};
+/*
+ We need special handling for mysql_close(), as the first part may block,
+ while the last part needs to free our extra library context stack.
+
+ So we do the first part (mysql_close_slow_part()) non-blocking, but the last
+ part blocking.
+*/
+static void
+mysql_close_slow_part_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY_VOID_RETURN(
+ mysql_close_slow_part,
+ (parms->sock),
+ parms->sock)
+}
+
+int STDCALL
+mysql_close_slow_part_start(MYSQL *sock)
+{
+MK_ASYNC_START_BODY_VOID_RETURN(
+ mysql_close_slow_part,
+ sock,
+ {
+ WIN_SET_NONBLOCKING(sock)
+ parms.sock= sock;
+ },
+ /* Nothing */)
+}
+int STDCALL
+mysql_close_slow_part_cont(MYSQL *sock, int ready_status)
+{
+MK_ASYNC_CONT_BODY_VOID_RETURN(sock)
+}
+int STDCALL
+mysql_close_start(MYSQL *sock)
+{
+ int res;
+
+ /* It is legitimate to have NULL sock argument, which will do nothing. */
+ if (sock && sock->net.pvio)
+ {
+ res= mysql_close_slow_part_start(sock);
+ /* If we need to block, return now and do the rest in mysql_close_cont(). */
+ if (res)
+ return res;
+ }
+ mysql_close(sock);
+ return 0;
+}
+int STDCALL
+mysql_close_cont(MYSQL *sock, int ready_status)
+{
+ int res;
+
+ res= mysql_close_slow_part_cont(sock, ready_status);
+ if (res)
+ return res;
+ mysql_close(sock);
+ return 0;
+}
+
+/*
+ These following are not available inside the server (neither blocking or
+ non-blocking).
+*/
+#ifndef MYSQL_SERVER
+/* Structure used to pass parameters from mysql_change_user_start(). */
+struct mysql_change_user_params {
+ MYSQL *mysql;
+ const char *user;
+ const char *passwd;
+ const char *db;
+};
+static void
+mysql_change_user_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_change_user,
+ (parms->mysql, parms->user, parms->passwd, parms->db),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_change_user_start(my_bool *ret, MYSQL *mysql, const char *user, const char *passwd, const char *db)
+{
+MK_ASYNC_START_BODY(
+ mysql_change_user,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.user= user;
+ parms.passwd= passwd;
+ parms.db= db;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_change_user_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_query_start(). */
+struct mysql_query_params {
+ MYSQL *mysql;
+ const char *q;
+};
+static void
+mysql_query_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_query,
+ (parms->mysql, parms->q),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_query_start(int *ret, MYSQL *mysql, const char *q)
+{
+MK_ASYNC_START_BODY(
+ mysql_query,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.q= q;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_query_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_shutdown_start(). */
+struct mysql_shutdown_params {
+ MYSQL *mysql;
+ enum mysql_enum_shutdown_level shutdown_level;
+};
+static void
+mysql_shutdown_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_shutdown,
+ (parms->mysql, parms->shutdown_level),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_shutdown_start(int *ret, MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
+{
+MK_ASYNC_START_BODY(
+ mysql_shutdown,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.shutdown_level= shutdown_level;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_shutdown_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_dump_debug_info_start(). */
+struct mysql_dump_debug_info_params {
+ MYSQL *mysql;
+};
+static void
+mysql_dump_debug_info_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_dump_debug_info,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_dump_debug_info_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_dump_debug_info,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_dump_debug_info_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_refresh_start(). */
+struct mysql_refresh_params {
+ MYSQL *mysql;
+ unsigned int refresh_options;
+};
+static void
+mysql_refresh_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_refresh,
+ (parms->mysql, parms->refresh_options),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_refresh_start(int *ret, MYSQL *mysql, unsigned int refresh_options)
+{
+MK_ASYNC_START_BODY(
+ mysql_refresh,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.refresh_options= refresh_options;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_refresh_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_kill_start(). */
+struct mysql_kill_params {
+ MYSQL *mysql;
+ unsigned long pid;
+};
+static void
+mysql_kill_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_kill,
+ (parms->mysql, parms->pid),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_kill_start(int *ret, MYSQL *mysql, unsigned long pid)
+{
+MK_ASYNC_START_BODY(
+ mysql_kill,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.pid= pid;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_kill_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_set_server_option_start(). */
+struct mysql_set_server_option_params {
+ MYSQL *mysql;
+ enum enum_mysql_set_option option;
+};
+static void
+mysql_set_server_option_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_set_server_option,
+ (parms->mysql, parms->option),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_set_server_option_start(int *ret, MYSQL *mysql,
+ enum enum_mysql_set_option option)
+{
+MK_ASYNC_START_BODY(
+ mysql_set_server_option,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.option= option;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_set_server_option_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_ping_start(). */
+struct mysql_ping_params {
+ MYSQL *mysql;
+};
+static void
+mysql_ping_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_ping,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_ping_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_ping,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_ping_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_reset_connection_start(). */
+struct mysql_reset_connection_params {
+ MYSQL *mysql;
+};
+static void
+mysql_reset_connection_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_reset_connection,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_reset_connection_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_reset_connection,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_reset_connection_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stat_start(). */
+struct mysql_stat_params {
+ MYSQL *mysql;
+};
+static void
+mysql_stat_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stat,
+ (parms->mysql),
+ parms->mysql,
+ const char *,
+ r_const_ptr)
+}
+int STDCALL
+mysql_stat_start(const char **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_stat,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_const_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_stat_cont(const char **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_const_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_dbs_start(). */
+struct mysql_list_dbs_params {
+ MYSQL *mysql;
+ const char *wild;
+};
+static void
+mysql_list_dbs_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_dbs,
+ (parms->mysql, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_dbs_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_dbs,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_dbs_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_tables_start(). */
+struct mysql_list_tables_params {
+ MYSQL *mysql;
+ const char *wild;
+};
+static void
+mysql_list_tables_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_tables,
+ (parms->mysql, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_tables_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_tables,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_tables_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_processes_start(). */
+struct mysql_list_processes_params {
+ MYSQL *mysql;
+};
+static void
+mysql_list_processes_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_processes,
+ (parms->mysql),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_processes_start(MYSQL_RES **ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_processes,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_processes_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_list_fields_start(). */
+struct mysql_list_fields_params {
+ MYSQL *mysql;
+ const char *table;
+ const char *wild;
+};
+static void
+mysql_list_fields_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_list_fields,
+ (parms->mysql, parms->table, parms->wild),
+ parms->mysql,
+ MYSQL_RES *,
+ r_ptr)
+}
+int STDCALL
+mysql_list_fields_start(MYSQL_RES **ret, MYSQL *mysql, const char *table,
+ const char *wild)
+{
+MK_ASYNC_START_BODY(
+ mysql_list_fields,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.table= table;
+ parms.wild= wild;
+ },
+ NULL,
+ r_ptr,
+ /* Nothing */)
+}
+int STDCALL
+mysql_list_fields_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ NULL,
+ r_ptr)
+}
+
+/* Structure used to pass parameters from mysql_read_query_result_start(). */
+struct mysql_read_query_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_read_query_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_read_query_result,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_read_query_result_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_read_query_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_read_query_result_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_prepare_start(). */
+struct mysql_stmt_prepare_params {
+ MYSQL_STMT *stmt;
+ const char *query;
+ unsigned long length;
+};
+static void
+mysql_stmt_prepare_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_prepare,
+ (parms->stmt, parms->query, parms->length),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_prepare_start(int *ret, MYSQL_STMT *stmt, const char *query,
+ unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_prepare,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ parms.query= query;
+ parms.length= length;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_prepare(stmt, query, length);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_prepare_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_execute_start(). */
+struct mysql_stmt_execute_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_execute_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_execute,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_execute_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_execute,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /*
+ If eg. mysql_change_user(), stmt->mysql will be NULL.
+ In this case, we cannot block.
+ */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_execute(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_execute_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_fetch_start(). */
+struct mysql_stmt_fetch_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_fetch_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_fetch,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_fetch_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_fetch,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_fetch(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_fetch_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_store_result_start(). */
+struct mysql_stmt_store_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_store_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_store_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_store_result_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_store_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_store_result(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_store_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_close_start(). */
+struct mysql_stmt_close_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_close_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_close,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_close_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_close,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_close(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_close_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_reset_start(). */
+struct mysql_stmt_reset_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_reset_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_reset,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_reset_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_reset,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_reset(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_reset_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_free_result_start(). */
+struct mysql_stmt_free_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_free_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_free_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_free_result_start(my_bool *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_free_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_free_result(stmt);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_free_result_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_stmt_send_long_data_start(). */
+struct mysql_stmt_send_long_data_params {
+ MYSQL_STMT *stmt;
+ unsigned int param_number;
+ const char *data;
+ unsigned long length;
+};
+static void
+mysql_stmt_send_long_data_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_send_long_data,
+ (parms->stmt, parms->param_number, parms->data, parms->length),
+ parms->stmt->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_stmt_send_long_data_start(my_bool *ret, MYSQL_STMT *stmt,
+ unsigned int param_number,
+ const char *data, unsigned long length)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_send_long_data,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ parms.param_number= param_number;
+ parms.data= data;
+ parms.length= length;
+ },
+ TRUE,
+ r_my_bool,
+ /* If stmt->mysql==NULL then we will not block so can call directly. */
+ if (!stmt->mysql)
+ {
+ *ret= mysql_stmt_send_long_data(stmt, param_number, data, length);
+ return 0;
+ })
+}
+int STDCALL
+mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_commit_start(). */
+struct mysql_commit_params {
+ MYSQL *mysql;
+};
+static void
+mysql_commit_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_commit,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_commit_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_commit,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_commit_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_rollback_start(). */
+struct mysql_rollback_params {
+ MYSQL *mysql;
+};
+static void
+mysql_rollback_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_rollback,
+ (parms->mysql),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_rollback_start(my_bool *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_rollback,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_rollback_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_autocommit_start(). */
+struct mysql_autocommit_params {
+ MYSQL *mysql;
+ my_bool auto_mode;
+};
+static void
+mysql_autocommit_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_autocommit,
+ (parms->mysql, parms->auto_mode),
+ parms->mysql,
+ my_bool,
+ r_my_bool)
+}
+int STDCALL
+mysql_autocommit_start(my_bool *ret, MYSQL *mysql, my_bool auto_mode)
+{
+MK_ASYNC_START_BODY(
+ mysql_autocommit,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ parms.auto_mode= auto_mode;
+ },
+ TRUE,
+ r_my_bool,
+ /* Nothing */)
+}
+int STDCALL
+mysql_autocommit_cont(my_bool *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ TRUE,
+ r_my_bool)
+}
+
+/* Structure used to pass parameters from mysql_next_result_start(). */
+struct mysql_next_result_params {
+ MYSQL *mysql;
+};
+static void
+mysql_next_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_next_result,
+ (parms->mysql),
+ parms->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_next_result_start(int *ret, MYSQL *mysql)
+{
+MK_ASYNC_START_BODY(
+ mysql_next_result,
+ mysql,
+ {
+ WIN_SET_NONBLOCKING(mysql)
+ parms.mysql= mysql;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_next_result_cont(int *ret, MYSQL *mysql, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ mysql,
+ 1,
+ r_int)
+}
+
+/* Structure used to pass parameters from mysql_stmt_next_result_start(). */
+struct mysql_stmt_next_result_params {
+ MYSQL_STMT *stmt;
+};
+static void
+mysql_stmt_next_result_start_internal(void *d)
+{
+MK_ASYNC_INTERNAL_BODY(
+ mysql_stmt_next_result,
+ (parms->stmt),
+ parms->stmt->mysql,
+ int,
+ r_int)
+}
+int STDCALL
+mysql_stmt_next_result_start(int *ret, MYSQL_STMT *stmt)
+{
+MK_ASYNC_START_BODY(
+ mysql_stmt_next_result,
+ stmt->mysql,
+ {
+ WIN_SET_NONBLOCKING(stmt->mysql)
+ parms.stmt= stmt;
+ },
+ 1,
+ r_int,
+ /* Nothing */)
+}
+int STDCALL
+mysql_stmt_next_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status)
+{
+MK_ASYNC_CONT_BODY(
+ stmt->mysql,
+ 1,
+ r_int)
+}
+#endif
+
+
+/*
+ The following functions are deprecated, and so have no non-blocking version:
+
+ mysql_connect
+ mysql_create_db
+ mysql_drop_db
+*/
+
+/*
+ The following functions can newer block, and so do not have special
+ non-blocking versions:
+
+ mysql_num_rows()
+ mysql_num_fields()
+ mysql_eof()
+ mysql_fetch_field_direct()
+ mysql_fetch_fields()
+ mysql_row_tell()
+ mysql_field_tell()
+ mysql_field_count()
+ mysql_affected_rows()
+ mysql_insert_id()
+ mysql_errno()
+ mysql_error()
+ mysql_sqlstate()
+ mysql_warning_count()
+ mysql_info()
+ mysql_thread_id()
+ mysql_character_set_name()
+ mysql_init()
+ mysql_ssl_set()
+ mysql_get_ssl_cipher()
+ mysql_use_result()
+ mysql_get_character_set_info()
+ mysql_set_local_infile_handler()
+ mysql_set_local_infile_default()
+ mysql_get_server_info()
+ mysql_get_server_name()
+ mysql_get_client_info()
+ mysql_get_client_version()
+ mysql_get_host_info()
+ mysql_get_server_version()
+ mysql_get_proto_info()
+ mysql_options()
+ mysql_data_seek()
+ mysql_row_seek()
+ mysql_field_seek()
+ mysql_fetch_lengths()
+ mysql_fetch_field()
+ mysql_escape_string()
+ mysql_hex_string()
+ mysql_real_escape_string()
+ mysql_debug()
+ myodbc_remove_escape()
+ mysql_thread_safe()
+ mysql_embedded()
+ mariadb_connection()
+ mysql_stmt_init()
+ mysql_stmt_fetch_column()
+ mysql_stmt_param_count()
+ mysql_stmt_attr_set()
+ mysql_stmt_attr_get()
+ mysql_stmt_bind_param()
+ mysql_stmt_bind_result()
+ mysql_stmt_result_metadata()
+ mysql_stmt_param_metadata()
+ mysql_stmt_errno()
+ mysql_stmt_error()
+ mysql_stmt_sqlstate()
+ mysql_stmt_row_seek()
+ mysql_stmt_row_tell()
+ mysql_stmt_data_seek()
+ mysql_stmt_num_rows()
+ mysql_stmt_affected_rows()
+ mysql_stmt_insert_id()
+ mysql_stmt_field_count()
+ mysql_more_results()
+ mysql_get_socket()
+ mysql_get_timeout_value()
+*/
diff --git a/libmariadb/libmariadb/mariadb_charset.c b/libmariadb/libmariadb/mariadb_charset.c
new file mode 100644
index 00000000..a406af2f
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_charset.c
@@ -0,0 +1,74 @@
+/* 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, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02111-1301, USA */
+
+#include <ma_global.h>
+#include <ma_sys.h>
+// #include "mysys_err.h"
+#include <mariadb_ctype.h>
+#include <ma_string.h>
+
+MARIADB_CHARSET_INFO *ma_default_charset_info; /* will be set in mysql_server_init */
+MARIADB_CHARSET_INFO *ma_charset_bin= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[32];
+MARIADB_CHARSET_INFO *ma_charset_latin1= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[5];
+MARIADB_CHARSET_INFO *ma_charset_utf8_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[21];
+MARIADB_CHARSET_INFO *ma_charset_utf16le_general_ci= (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[68];
+
+MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint cs_number)
+{
+ int i= 0;
+
+ while (mariadb_compiled_charsets[i].nr && cs_number != mariadb_compiled_charsets[i].nr)
+ i++;
+
+ return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
+}
+
+my_bool set_default_charset(uint cs, myf flags __attribute__((unused)))
+{
+ MARIADB_CHARSET_INFO *new_charset;
+ new_charset = mysql_get_charset_by_nr(cs);
+ if (!new_charset)
+ {
+ return(TRUE); /* error */
+ }
+ ma_default_charset_info = new_charset;
+ return(FALSE);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *cs_name)
+{
+ int i= 0;
+
+ while (mariadb_compiled_charsets[i].nr && strcmp(cs_name, mariadb_compiled_charsets[i].csname) != 0)
+ i++;
+
+ return (mariadb_compiled_charsets[i].nr) ? (MARIADB_CHARSET_INFO *)&mariadb_compiled_charsets[i] : NULL;
+}
+
+my_bool set_default_charset_by_name(const char *cs_name, myf flags __attribute__((unused)))
+{
+ MARIADB_CHARSET_INFO *new_charset;
+ new_charset = mysql_get_charset_by_name(cs_name);
+ if (!new_charset)
+ {
+ return(TRUE); /* error */
+ }
+
+ ma_default_charset_info = new_charset;
+ return(FALSE);
+}
diff --git a/libmariadb/libmariadb/mariadb_dyncol.c b/libmariadb/libmariadb/mariadb_dyncol.c
new file mode 100644
index 00000000..42de69ee
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_dyncol.c
@@ -0,0 +1,4369 @@
+/* Copyright (c) 2011,2013 Monty Program Ab;
+ Copyright (c) 2011,2012 Oleksandr Byelkin
+ 2013, 2022 MariaDB Corporation AB
+
+ 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 <COPYRIGHT HOLDER> ``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 <COPYRIGHT HOLDER> 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.
+*/
+
+/*
+ Numeric format:
+ ===============
+ * Fixed header part
+ 1 byte flags:
+ 0,1 bits - <offset size> - 1
+ 2-7 bits - 0
+ 2 bytes column counter
+ * Columns directory sorted by column number, each entry contains of:
+ 2 bytes column number
+ <offset size> bytes (1-4) combined offset from beginning of
+ the data segment + 3 bit type
+ * Data of above columns size of data and length depend on type
+
+ Columns with names:
+ ===================
+ * Fixed header part
+ 1 byte flags:
+ 0,1 bits - <offset size> - 2
+ 2 bit - 1 (means format with names)
+ 3,4 bits - 00 (means <names offset size> - 2,
+ now 2 is the only supported size)
+ 5-7 bits - 0
+ 2 bytes column counter
+ * Variable header part (now it is actually fixed part)
+ <names offset size> (2) bytes size of stored names pool
+ * Column directory sorted by names, each consists of
+ <names offset size> (2) bytes offset of name
+ <offset size> bytes (2-5)bytes combined offset from beginning of
+ the data segment + 4 bit type
+ * Names stored one after another
+ * Data of above columns size of data and length depend on type
+*/
+
+#include <stdio.h>
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_string.h>
+//#include <ma_hashtbl.h>
+#include <mariadb_dyncol.h>
+#include <mysql.h>
+
+
+
+#ifndef LIBMARIADB
+uint32 copy_and_convert(char *to, uint32 to_length, MARIADB_CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ MARIADB_CHARSET_INFO *from_cs, uint *errors);
+#else
+
+size_t mariadb_time_to_string(const MYSQL_TIME *tm, char *time_str, size_t len,
+ unsigned int digits);
+size_t STDCALL mariadb_convert_string(const char *from, size_t *from_len, MARIADB_CHARSET_INFO *from_cs,
+ char *to, size_t *to_len, MARIADB_CHARSET_INFO *to_cs, int *errorcode);
+#endif
+/*
+ Flag byte bits
+
+ 2 bits which determinate size of offset in the header -1
+*/
+/* mask to get above bits */
+#define DYNCOL_FLG_OFFSET (1|2)
+#define DYNCOL_FLG_NAMES 4
+#define DYNCOL_FLG_NMOFFSET (8|16)
+/**
+ All known flags mask that could be set.
+
+ @note DYNCOL_FLG_NMOFFSET should be 0 for now.
+*/
+#define DYNCOL_FLG_KNOWN (1|2|4)
+
+/* formats */
+enum enum_dyncol_format
+{
+ dyncol_fmt_num= 0,
+ dyncol_fmt_str= 1
+};
+
+/* dynamic column size reserve */
+#define DYNCOL_SYZERESERVE 80
+
+#define DYNCOL_OFFSET_ERROR 0xffffffff
+
+/* length of fixed string header 1 byte - flags, 2 bytes - columns counter */
+#define FIXED_HEADER_SIZE 3
+/*
+ length of fixed string header with names
+ 1 byte - flags, 2 bytes - columns counter, 2 bytes - name pool size
+*/
+#define FIXED_HEADER_SIZE_NM 5
+
+#define COLUMN_NUMBER_SIZE 2
+/* 2 bytes offset from the name pool */
+#define COLUMN_NAMEPTR_SIZE 2
+
+#define MAX_OFFSET_LENGTH 4
+#define MAX_OFFSET_LENGTH_NM 5
+
+#define DYNCOL_NUM_CHAR 6
+
+my_bool mariadb_dyncol_has_names(DYNAMIC_COLUMN *str)
+{
+ if (str->length < 1)
+ return FALSE;
+ return test(str->str[0] & DYNCOL_FLG_NAMES);
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_time_store(DYNAMIC_COLUMN *str,
+ MYSQL_TIME *value, enum enum_dyncol_format format);
+static enum enum_dyncol_func_result
+dynamic_column_date_store(DYNAMIC_COLUMN *str,
+ MYSQL_TIME *value);
+static enum enum_dyncol_func_result
+dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length);
+static enum enum_dyncol_func_result
+dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length);
+static enum enum_dyncol_func_result
+dynamic_column_get_internal(DYNAMIC_COLUMN *str,
+ DYNAMIC_COLUMN_VALUE *store_it_here,
+ uint num_key, LEX_STRING *str_key);
+static enum enum_dyncol_func_result
+dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
+ LEX_STRING *str_key);
+static enum enum_dyncol_func_result
+dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool string_keys);
+static int plan_sort_num(const void *a, const void *b);
+static int plan_sort_named(const void *a, const void *b);
+
+/*
+ Structure to hold information about dynamic columns record and
+ iterate through it.
+*/
+
+struct st_dyn_header
+{
+ uchar *header, *nmpool, *dtpool, *data_end;
+ size_t offset_size;
+ size_t entry_size;
+ size_t header_size;
+ size_t nmpool_size;
+ size_t data_size;
+ /* dyncol_fmt_num - numeric columns, dyncol_fmt_str - column names */
+ enum enum_dyncol_format format;
+ uint column_count;
+
+ uchar *entry, *data, *name;
+ size_t offset;
+ size_t length;
+ enum enum_dynamic_column_type type;
+};
+
+typedef struct st_dyn_header DYN_HEADER;
+
+static inline my_bool read_fixed_header(DYN_HEADER *hdr,
+ DYNAMIC_COLUMN *str);
+static void set_fixed_header(DYNAMIC_COLUMN *str,
+ uint offset_size,
+ uint column_count);
+
+/*
+ Calculate entry size (E) and header size (H) by offset size (O) and column
+ count (C) and fixed part of entry size (F).
+*/
+
+#define calc_param(E,H,F,O,C) do { \
+ (*(E))= (O) + F; \
+ (*(H))= (*(E)) * (C); \
+}while(0);
+
+
+/**
+ Name pool size functions, for numeric format it is 0
+*/
+
+static size_t name_size_num(void *keys __attribute__((unused)),
+ uint i __attribute__((unused)))
+{
+ return 0;
+}
+
+
+/**
+ Name pool size functions.
+*/
+static size_t name_size_named(void *keys, uint i)
+{
+ return ((LEX_STRING *) keys)[i].length;
+}
+
+
+/**
+ Comparator function for references on column numbers for qsort
+ (numeric format)
+*/
+
+static int column_sort_num(const void *a, const void *b)
+{
+ return **((uint **)a) - **((uint **)b);
+}
+
+/**
+ Comparator function for references on column numbers for qsort
+ (names format)
+*/
+
+int mariadb_dyncol_column_cmp_named(const LEX_STRING *s1, const LEX_STRING *s2)
+{
+ /*
+ We compare instead of subtraction to avoid data loss in case of huge
+ length difference (more then fit in int).
+ */
+ int rc= (s1->length > s2->length ? 1 :
+ (s1->length < s2->length ? -1 : 0));
+ if (rc == 0)
+ rc= memcmp((void *)s1->str, (void *)s2->str,
+ (size_t) s1->length);
+ return rc;
+}
+
+
+/**
+ Comparator function for references on column numbers for qsort
+ (names format)
+*/
+
+static int column_sort_named(const void *a, const void *b)
+{
+ return mariadb_dyncol_column_cmp_named(*((LEX_STRING **)a),
+ *((LEX_STRING **)b));
+}
+
+
+/**
+ Check limit function (numeric format)
+*/
+
+static my_bool check_limit_num(const void *val)
+{
+ return **((uint **)val) > UINT_MAX16;
+}
+
+
+/**
+ Check limit function (names format)
+*/
+
+static my_bool check_limit_named(const void *val)
+{
+ return (*((LEX_STRING **)val))->length > MAX_NAME_LENGTH;
+}
+
+
+/**
+ Write numeric format static header part.
+*/
+
+static void set_fixed_header_num(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
+{
+ set_fixed_header(str, (uint)hdr->offset_size, hdr->column_count);
+ hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE;
+ hdr->nmpool= hdr->dtpool= hdr->header + hdr->header_size;
+}
+
+
+/**
+ Write names format static header part.
+*/
+
+static void set_fixed_header_named(DYNAMIC_COLUMN *str, DYN_HEADER *hdr)
+{
+ DBUG_ASSERT(hdr->column_count <= 0xffff);
+ DBUG_ASSERT(hdr->offset_size <= MAX_OFFSET_LENGTH_NM);
+ /* size of data offset, named format flag, size of names offset (0 means 2) */
+ str->str[0]=
+ (char) ((str->str[0] & ~(DYNCOL_FLG_OFFSET | DYNCOL_FLG_NMOFFSET)) |
+ (hdr->offset_size - 2) | DYNCOL_FLG_NAMES);
+ int2store(str->str + 1, hdr->column_count); /* columns number */
+ int2store(str->str + 3, hdr->nmpool_size);
+ hdr->header= (uchar *)str->str + FIXED_HEADER_SIZE_NM;
+ hdr->nmpool= hdr->header + hdr->header_size;
+ hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
+}
+
+
+/**
+ Store offset and type information in the given place
+
+ @param place Beginning of the index entry
+ @param offset_size Size of offset field in bytes
+ @param type Type to be written
+ @param offset Offset to be written
+*/
+
+static my_bool type_and_offset_store_num(uchar *place, size_t offset_size,
+ DYNAMIC_COLUMN_TYPE type,
+ size_t offset)
+{
+ ulong val = (((ulong) offset) << 3) | (type - 1);
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ DBUG_ASSERT(((type - 1) & (~7)) == 0); /* fit in 3 bits */
+ DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
+
+ /* Index entry starts with column number; jump over it */
+ place+= COLUMN_NUMBER_SIZE;
+
+ switch (offset_size) {
+ case 1:
+ if (offset >= 0x1f) /* all 1 value is reserved */
+ return TRUE;
+ place[0]= (uchar)val;
+ break;
+ case 2:
+ if (offset >= 0x1fff) /* all 1 value is reserved */
+ return TRUE;
+ int2store(place, val);
+ break;
+ case 3:
+ if (offset >= 0x1fffff) /* all 1 value is reserved */
+ return TRUE;
+ int3store(place, val);
+ break;
+ case 4:
+ if (offset >= 0x1fffffff) /* all 1 value is reserved */
+ return TRUE;
+ int4store(place, val);
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static my_bool type_and_offset_store_named(uchar *place, size_t offset_size,
+ DYNAMIC_COLUMN_TYPE type,
+ size_t offset)
+{
+ ulonglong val = (((ulong) offset) << 4) | (type - 1);
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ DBUG_ASSERT(((type - 1) & (~0xf)) == 0); /* fit in 4 bits */
+ DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
+
+ /* Index entry starts with name offset; jump over it */
+ place+= COLUMN_NAMEPTR_SIZE;
+ switch (offset_size) {
+ case 2:
+ if (offset >= 0xfff) /* all 1 value is reserved */
+ return TRUE;
+ int2store(place, val);
+ break;
+ case 3:
+ if (offset >= 0xfffff) /* all 1 value is reserved */
+ return TRUE;
+ int3store(place, val);
+ break;
+ case 4:
+ if (offset >= 0xfffffff) /* all 1 value is reserved */
+ return TRUE;
+ int4store(place, val);
+ break;
+ case 5:
+#if SIZEOF_SIZE_T > 4
+ if (offset >= 0xfffffffffull) /* all 1 value is reserved */
+ return TRUE;
+#endif
+ int5store(place, val);
+ break;
+ case 1:
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Write numeric format header entry
+ 2 bytes - column number
+ 1-4 bytes - data offset combined with type
+
+ @param hdr descriptor of dynamic column record
+ @param column_key pointer to uint (column number)
+ @param value value which will be written (only type used)
+ @param offset offset of the data
+*/
+
+static my_bool put_header_entry_num(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset)
+{
+ uint *column_number= (uint *)column_key;
+ int2store(hdr->entry, *column_number);
+ DBUG_ASSERT(hdr->nmpool_size == 0);
+ if (type_and_offset_store_num(hdr->entry, hdr->offset_size,
+ value->type,
+ offset))
+ return TRUE;
+ hdr->entry= hdr->entry + hdr->entry_size;
+ return FALSE;
+}
+
+
+/**
+ Write names format header entry
+ 1 byte - name length
+ 2 bytes - name offset in the name pool
+ 1-4 bytes - data offset combined with type
+
+ @param hdr descriptor of dynamic column record
+ @param column_key pointer to LEX_STRING (column name)
+ @param value value which will be written (only type used)
+ @param offset offset of the data
+*/
+
+static my_bool put_header_entry_named(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset)
+{
+ LEX_STRING *column_name= (LEX_STRING *)column_key;
+ DBUG_ASSERT(column_name->length <= MAX_NAME_LENGTH);
+ DBUG_ASSERT(hdr->name - hdr->nmpool < (long) 0x10000L);
+ int2store(hdr->entry, hdr->name - hdr->nmpool);
+ memcpy(hdr->name, column_name->str, column_name->length);
+ DBUG_ASSERT(hdr->nmpool_size != 0 || column_name->length == 0);
+ if (type_and_offset_store_named(hdr->entry, hdr->offset_size,
+ value->type,
+ offset))
+ return TRUE;
+ hdr->entry+= hdr->entry_size;
+ hdr->name+= column_name->length;
+ return FALSE;
+}
+
+
+/**
+ Calculate length of offset field for given data length
+
+ @param data_length Length of the data segment
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_offset_bytes_num(size_t data_length)
+{
+ if (data_length < 0x1f) /* all 1 value is reserved */
+ return 1;
+ if (data_length < 0x1fff) /* all 1 value is reserved */
+ return 2;
+ if (data_length < 0x1fffff) /* all 1 value is reserved */
+ return 3;
+ if (data_length < 0x1fffffff) /* all 1 value is reserved */
+ return 4;
+ return MAX_OFFSET_LENGTH + 1; /* For an error generation*/
+}
+
+static size_t dynamic_column_offset_bytes_named(size_t data_length)
+{
+ if (data_length < 0xfff) /* all 1 value is reserved */
+ return 2;
+ if (data_length < 0xfffff) /* all 1 value is reserved */
+ return 3;
+ if (data_length < 0xfffffff) /* all 1 value is reserved */
+ return 4;
+#if SIZEOF_SIZE_T > 4
+ if (data_length < 0xfffffffffull) /* all 1 value is reserved */
+#endif
+ return 5;
+ return MAX_OFFSET_LENGTH_NM + 1; /* For an error generation */
+}
+
+/**
+ Read offset and type information from index entry
+
+ @param type Where to put type info
+ @param offset Where to put offset info
+ @param place beginning of the type and offset
+ @param offset_size Size of offset field in bytes
+*/
+
+static my_bool type_and_offset_read_num(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size)
+{
+ ulong UNINIT_VAR(val);
+ ulong UNINIT_VAR(lim);
+
+ DBUG_ASSERT(offset_size >= 1 && offset_size <= 4);
+
+ switch (offset_size) {
+ case 1:
+ val= (ulong)place[0];
+ lim= 0x1f;
+ break;
+ case 2:
+ val= uint2korr(place);
+ lim= 0x1fff;
+ break;
+ case 3:
+ val= uint3korr(place);
+ lim= 0x1fffff;
+ break;
+ case 4:
+ val= uint4korr(place);
+ lim= 0x1fffffff;
+ break;
+ default:
+ DBUG_ASSERT(0); /* impossible */
+ return 1;
+ }
+ *type= (val & 0x7) + 1;
+ *offset= val >> 3;
+ return (*offset >= lim);
+}
+
+static my_bool type_and_offset_read_named(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size)
+{
+ ulonglong UNINIT_VAR(val);
+ ulonglong UNINIT_VAR(lim);
+ DBUG_ASSERT(offset_size >= 2 && offset_size <= 5);
+
+ switch (offset_size) {
+ case 2:
+ val= uint2korr(place);
+ lim= 0xfff;
+ break;
+ case 3:
+ val= uint3korr(place);
+ lim= 0xfffff;
+ break;
+ case 4:
+ val= uint4korr(place);
+ lim= 0xfffffff;
+ break;
+ case 5:
+ val= uint5korr(place);
+ lim= 0xfffffffffull;
+ break;
+ case 1:
+ default:
+ DBUG_ASSERT(0); /* impossible */
+ return 1;
+ }
+ *type= (val & 0xf) + 1;
+ *offset= (size_t)(val >> 4);
+ return (*offset >= lim);
+}
+
+/**
+ Format descriptor, contain constants and function references for
+ format processing
+*/
+
+struct st_service_funcs
+{
+ /* size of fixed header */
+ uint fixed_hdr;
+ /* size of fixed part of header entry */
+ uint fixed_hdr_entry;
+
+ /*size of array element which stores keys */
+ uint key_size_in_array;
+
+ /* Maximum data offset size in bytes */
+ size_t max_offset_size;
+
+ size_t (*name_size)
+ (void *, uint);
+ int (*column_sort)
+ (const void *a, const void *b);
+ my_bool (*check_limit)
+ (const void *val);
+ void (*set_fixed_hdr)
+ (DYNAMIC_COLUMN *str, DYN_HEADER *hdr);
+ my_bool (*put_header_entry)(DYN_HEADER *hdr,
+ void *column_key,
+ DYNAMIC_COLUMN_VALUE *value,
+ size_t offset);
+ int (*plan_sort)(const void *a, const void *b);
+ size_t (*dynamic_column_offset_bytes)(size_t data_length);
+ my_bool (*type_and_offset_read)(DYNAMIC_COLUMN_TYPE *type,
+ size_t *offset,
+ uchar *place, size_t offset_size);
+
+};
+
+
+/**
+ Actual our 2 format descriptors
+*/
+
+static struct st_service_funcs fmt_data[2]=
+{
+ {
+ FIXED_HEADER_SIZE,
+ COLUMN_NUMBER_SIZE,
+ sizeof(uint),
+ MAX_OFFSET_LENGTH,
+ &name_size_num,
+ &column_sort_num,
+ &check_limit_num,
+ &set_fixed_header_num,
+ &put_header_entry_num,
+ &plan_sort_num,
+ &dynamic_column_offset_bytes_num,
+ &type_and_offset_read_num
+ },
+ {
+ FIXED_HEADER_SIZE_NM,
+ COLUMN_NAMEPTR_SIZE,
+ sizeof(LEX_STRING),
+ MAX_OFFSET_LENGTH_NM,
+ &name_size_named,
+ &column_sort_named,
+ &check_limit_named,
+ &set_fixed_header_named,
+ &put_header_entry_named,
+ &plan_sort_named,
+ &dynamic_column_offset_bytes_named,
+ &type_and_offset_read_named
+ }
+};
+
+
+/**
+ Read dynamic column record header and fill the descriptor
+
+ @param hdr dynamic columns record descriptor to fill
+ @param str dynamic columns record
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+init_read_hdr(DYN_HEADER *hdr, DYNAMIC_COLUMN *str)
+{
+ if (read_fixed_header(hdr, str))
+ return ER_DYNCOL_FORMAT;
+ hdr->header= (uchar*)str->str + fmt_data[hdr->format].fixed_hdr;
+ calc_param(&hdr->entry_size, &hdr->header_size,
+ fmt_data[hdr->format].fixed_hdr_entry, hdr->offset_size,
+ hdr->column_count);
+ hdr->nmpool= hdr->header + hdr->header_size;
+ hdr->dtpool= hdr->nmpool + hdr->nmpool_size;
+ hdr->data_size= str->length - fmt_data[hdr->format].fixed_hdr -
+ hdr->header_size - hdr->nmpool_size;
+ hdr->data_end= (uchar*)str->str + str->length;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Initialize dynamic column string with (make it empty but correct format)
+
+ @param str The string to initialize
+ @param size Amount of preallocated memory for the string.
+
+ @retval FALSE OK
+ @retval TRUE error
+*/
+
+static my_bool dynamic_column_init_named(DYNAMIC_COLUMN *str, size_t size)
+{
+ DBUG_ASSERT(size != 0);
+
+ /*
+ Make string with no fields (empty header)
+ - First \0 is flags
+ - other 2 \0 is number of fields
+ */
+ if (ma_init_dynamic_string(str, NULL, size, DYNCOL_SYZERESERVE))
+ return TRUE;
+ return FALSE;
+}
+
+
+/**
+ Calculate how many bytes needed to store val as variable length integer
+ where first bit indicate continuation of the sequence.
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_var_uint_bytes(ulonglong val)
+{
+ size_t len= 0;
+ do
+ {
+ len++;
+ val>>= 7;
+ } while (val);
+ return len;
+}
+
+
+/**
+ Stores variable length unsigned integer value to a string
+
+ @param str The string where to append the value
+ @param val The value to put in the string
+
+ @return ER_DYNCOL_* return code
+
+ @notes
+ This is used to store a number together with other data in the same
+ object. (Like decimals, length of string etc)
+ (As we don't know the length of this object, we can't store 0 in 0 bytes)
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_var_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
+{
+ if (ma_dynstr_realloc(str, 10)) /* max what we can use */
+ return ER_DYNCOL_RESOURCE;
+
+ do
+ {
+ ulonglong rest= val >> 7;
+ str->str[str->length++]= ((val & 0x7f) | (rest ? 0x80 : 0x00));
+ val= rest;
+ } while (val);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Reads variable length unsigned integer value from a string
+
+ @param data The string from which the int should be read
+ @param data_length Max length of data
+ @param len Where to put length of the string read in bytes
+
+ @return value of the unsigned integer read from the string
+
+ In case of error, *len is set to 0
+*/
+
+static ulonglong
+dynamic_column_var_uint_get(uchar *data, size_t data_length,
+ size_t *len)
+{
+ ulonglong val= 0;
+ uint length;
+ uchar *end= data + data_length;
+
+ for (length=0; data < end ; data++)
+ {
+ val+= (((ulonglong)((*data) & 0x7f)) << (length * 7));
+ length++;
+ if (!((*data) & 0x80))
+ {
+ /* End of data */
+ *len= length;
+ return val;
+ }
+ }
+ /* Something was wrong with data */
+ *len= 0; /* Mark error */
+ return 0;
+}
+
+
+/**
+ Calculate how many bytes needed to store val as unsigned.
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes (0-8)
+*/
+
+static size_t dynamic_column_uint_bytes(ulonglong val)
+{
+ size_t len;
+
+ for (len= 0; val ; val>>= 8, len++)
+ ;
+ return len;
+}
+
+
+/**
+ Append the string with given unsigned int value.
+
+ @param str The string where to put the value
+ @param val The value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_uint_store(DYNAMIC_COLUMN *str, ulonglong val)
+{
+ if (ma_dynstr_realloc(str, 8)) /* max what we can use */
+ return ER_DYNCOL_RESOURCE;
+
+ for (; val; val>>= 8)
+ str->str[str->length++]= (char) (val & 0xff);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read unsigned int value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_uint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ ulonglong value= 0;
+ size_t i;
+
+ for (i= 0; i < length; i++)
+ value+= ((ulonglong)data[i]) << (i*8);
+
+ store_it_here->x.ulong_value= value;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Calculate how many bytes needed to store val as signed in following encoding:
+ 0 -> 0
+ -1 -> 1
+ 1 -> 2
+ -2 -> 3
+ 2 -> 4
+ ...
+
+ @param val The value for which we are calculating length
+
+ @return number of bytes
+*/
+
+static size_t dynamic_column_sint_bytes(longlong val)
+{
+ return dynamic_column_uint_bytes((val << 1) ^
+ (val < 0 ? 0xffffffffffffffffull : 0));
+}
+
+
+/**
+ Append the string with given signed int value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_sint_store(DYNAMIC_COLUMN *str, longlong val)
+{
+ return dynamic_column_uint_store(str,
+ (val << 1) ^
+ (val < 0 ? 0xffffffffffffffffULL : 0));
+}
+
+
+/**
+ Read signed int value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_sint_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ ulonglong val;
+ dynamic_column_uint_read(store_it_here, data, length);
+ val= store_it_here->x.ulong_value;
+ if (val & 1)
+ val= (val >> 1) ^ 0xffffffffffffffffULL;
+ else
+ val>>= 1;
+ store_it_here->x.long_value= (longlong) val;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Calculate how many bytes needed to store the value.
+
+ @param value The value for which we are calculating length
+
+ @return
+ Error: (size_t) ~0
+ ok number of bytes
+*/
+
+static size_t
+dynamic_column_value_len(DYNAMIC_COLUMN_VALUE *value,
+ enum enum_dyncol_format format)
+{
+ switch (value->type) {
+ case DYN_COL_NULL:
+ return 0;
+ case DYN_COL_INT:
+ return dynamic_column_sint_bytes(value->x.long_value);
+ case DYN_COL_UINT:
+ return dynamic_column_uint_bytes(value->x.ulong_value);
+ case DYN_COL_DOUBLE:
+ return 8;
+ case DYN_COL_STRING:
+#ifdef LIBMARIADB
+ return (dynamic_column_var_uint_bytes(value->x.string.charset->nr) +
+ value->x.string.value.length);
+#else
+ return (dynamic_column_var_uint_bytes(value->x.string.charset->number) +
+ value->x.string.value.length);
+#endif
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ {
+ int precision= value->x.decimal.value.intg + value->x.decimal.value.frac;
+ int scale= value->x.decimal.value.frac;
+
+ if (precision == 0 || decimal_is_zero(&value->x.decimal.value))
+ {
+ /* This is here to simplify dynamic_column_decimal_store() */
+ value->x.decimal.value.intg= value->x.decimal.value.frac= 0;
+ return 0;
+ }
+ /*
+ Check if legal decimal; This is needed to not get an assert in
+ decimal_bin_size(). However this should be impossible as all
+ decimals entered here should be valid and we have the special check
+ above to handle the unlikely but possible case that decimal.value.intg
+ and decimal.frac is 0.
+ */
+ if (scale < 0 || precision <= 0)
+ {
+ DBUG_ASSERT(0); /* Impossible */
+ return (size_t) ~0;
+ }
+ return (dynamic_column_var_uint_bytes(value->x.decimal.value.intg) +
+ dynamic_column_var_uint_bytes(value->x.decimal.value.frac) +
+ decimal_bin_size(precision, scale));
+ }
+#endif
+ case DYN_COL_DATETIME:
+ if (format == dyncol_fmt_num || value->x.time_value.second_part)
+ /* date+time in bits: 14 + 4 + 5 + 10 + 6 + 6 + 20 + 1 66bits ~= 9 bytes*/
+ return 9;
+ else
+ return 6;
+ case DYN_COL_DATE:
+ /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
+ return 3;
+ case DYN_COL_TIME:
+ if (format == dyncol_fmt_num || value->x.time_value.second_part)
+ /* time in bits: 10 + 6 + 6 + 20 + 1 = 43bits ~= 6bytes*/
+ return 6;
+ else
+ return 3;
+ case DYN_COL_DYNCOL:
+ return value->x.string.value.length;
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+/**
+ Append double value to a string
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_double_store(DYNAMIC_COLUMN *str, double val)
+{
+ if (ma_dynstr_realloc(str, 8))
+ return ER_DYNCOL_RESOURCE;
+ float8store(str->str + str->length, val);
+ str->length+= 8;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read double value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_double_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ if (length != 8)
+ return ER_DYNCOL_FORMAT;
+ float8get(store_it_here->x.double_value, data);
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Append the string with given string value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_string_store(DYNAMIC_COLUMN *str, LEX_STRING *string,
+ MARIADB_CHARSET_INFO *charset)
+{
+ enum enum_dyncol_func_result rc;
+#ifdef LIBMARIADB
+ if ((rc= dynamic_column_var_uint_store(str, charset->nr)))
+#else
+ if ((rc= dynamic_column_var_uint_store(str, charset->number)))
+#endif
+ return rc;
+ if (ma_dynstr_append_mem(str, string->str, string->length))
+ return ER_DYNCOL_RESOURCE;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Append the string with given string value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_dyncol_store(DYNAMIC_COLUMN *str, LEX_STRING *string)
+{
+ if (ma_dynstr_append_mem(str, string->str, string->length))
+ return ER_DYNCOL_RESOURCE;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Read string value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_string_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ size_t len;
+ uint charset_nr= (uint)dynamic_column_var_uint_get(data, length, &len);
+ if (len == 0) /* Wrong packed number */
+ return ER_DYNCOL_FORMAT;
+#ifndef LIBMARIADB
+ store_it_here->x.string.charset= get_charset_by_nr(charset_nr);
+#else
+ store_it_here->x.string.charset= mariadb_get_charset_by_nr(charset_nr);
+#endif
+ if (store_it_here->x.string.charset == NULL)
+ return ER_DYNCOL_UNKNOWN_CHARSET;
+ data+= len;
+ store_it_here->x.string.value.length= (length-= len);
+ store_it_here->x.string.value.str= (char*) data;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Read Dynamic columns packet string value of given length
+ from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_dyncol_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.string.charset= ma_charset_bin;
+ store_it_here->x.string.value.length= length;
+ store_it_here->x.string.value.str= (char*) data;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Append the string with given decimal value.
+
+ @param str the string where to put the value
+ @param val the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+#ifndef LIBMARIADB
+static enum enum_dyncol_func_result
+dynamic_column_decimal_store(DYNAMIC_COLUMN *str,
+ decimal_t *value)
+{
+ uint bin_size;
+ int precision= value->intg + value->frac;
+
+ /* Store decimal zero as empty string */
+ if (precision == 0)
+ return ER_DYNCOL_OK;
+
+ bin_size= decimal_bin_size(precision, value->frac);
+ if (ma_dynstr_realloc(str, bin_size + 20))
+ return ER_DYNCOL_RESOURCE;
+
+ /* The following can't fail as memory is already allocated */
+ (void) dynamic_column_var_uint_store(str, value->intg);
+ (void) dynamic_column_var_uint_store(str, value->frac);
+
+ decimal2bin(value, (uchar *) str->str + str->length,
+ precision, value->frac);
+ str->length+= bin_size;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Prepare the value to be used as decimal.
+
+ @param value The value structure which should be setup.
+*/
+
+void mariadb_dyncol_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
+{
+ value->x.decimal.value.buf= value->x.decimal.buffer;
+ value->x.decimal.value.len= DECIMAL_BUFF_LENGTH;
+ /* just to be safe */
+ value->type= DYN_COL_DECIMAL;
+ decimal_make_zero(&value->x.decimal.value);
+}
+
+void dynamic_column_prepare_decimal(DYNAMIC_COLUMN_VALUE *value)
+{
+ mariadb_dyncol_prepare_decimal(value);
+}
+
+
+
+/**
+ Read decimal value of given length from the string
+
+ @param store_it_here The structure to store the value
+ @param data The string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_decimal_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ size_t intg_len, frac_len;
+ int intg, frac, precision, scale;
+
+ dynamic_column_prepare_decimal(store_it_here);
+ /* Decimals 0.0 is stored as a zero length string */
+ if (length == 0)
+ return ER_DYNCOL_OK; /* value contains zero */
+
+ intg= (int)dynamic_column_var_uint_get(data, length, &intg_len);
+ data+= intg_len;
+ frac= (int)dynamic_column_var_uint_get(data, length - intg_len, &frac_len);
+ data+= frac_len;
+
+ /* Check the size of data is correct */
+ precision= intg + frac;
+ scale= frac;
+ if (scale < 0 || precision <= 0 || scale > precision ||
+ (length - intg_len - frac_len) >
+ (size_t) (DECIMAL_BUFF_LENGTH*sizeof(decimal_digit_t)) ||
+ decimal_bin_size(intg + frac, frac) !=
+ (int) (length - intg_len - frac_len))
+ return ER_DYNCOL_FORMAT;
+
+ if (bin2decimal(data, &store_it_here->x.decimal.value, precision, scale) !=
+ E_DEC_OK)
+ return ER_DYNCOL_FORMAT;
+ return ER_DYNCOL_OK;
+}
+#endif
+
+/**
+ Append the string with given datetime value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
+ enum enum_dyncol_format format)
+{
+ enum enum_dyncol_func_result rc;
+ /*
+ 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
+ 12345678901234123412345 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456><123456><123456><123456>
+ */
+ if ((rc= dynamic_column_date_store(str, value)) ||
+ (rc= dynamic_column_time_store(str, value, format)))
+ return rc;
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read datetime value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ /*
+ 0<----year----><mn><day>00000!<-hours--><min-><sec-><---microseconds--->
+ 12345678901234123412345 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456><123456><123456><123456>
+ */
+ if (length != 9 && length != 6)
+ goto err;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATETIME;
+ if ((rc= dynamic_column_date_read_internal(store_it_here, data, 3)) ||
+ (rc= dynamic_column_time_read_internal(store_it_here, data + 3,
+ length - 3)))
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return rc;
+}
+
+
+/**
+ Append the string with given time value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value,
+ enum enum_dyncol_format format)
+{
+ uchar *buf;
+ if (ma_dynstr_realloc(str, 6))
+ return ER_DYNCOL_RESOURCE;
+
+ buf= ((uchar *)str->str) + str->length;
+
+ if (value->time_type == MYSQL_TIMESTAMP_NONE ||
+ value->time_type == MYSQL_TIMESTAMP_ERROR ||
+ value->time_type == MYSQL_TIMESTAMP_DATE)
+ {
+ value->neg= 0;
+ value->second_part= 0;
+ value->hour= 0;
+ value->minute= 0;
+ value->second= 0;
+ }
+ DBUG_ASSERT(value->hour <= 838);
+ DBUG_ASSERT(value->minute <= 59);
+ DBUG_ASSERT(value->second <= 59);
+ DBUG_ASSERT(value->second_part <= 999999);
+ if (format == dyncol_fmt_num || value->second_part)
+ {
+ /*
+ 00000!<-hours--><min-><sec-><---microseconds--->
+ 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456>
+ */
+ buf[0]= (value->second_part & 0xff);
+ buf[1]= ((value->second_part & 0xff00) >> 8);
+ buf[2]= (uchar)(((value->second & 0xf) << 4) |
+ ((value->second_part & 0xf0000) >> 16));
+ buf[3]= ((value->minute << 2) | ((value->second & 0x30) >> 4));
+ buf[4]= (value->hour & 0xff);
+ buf[5]= ((value->neg ? 0x4 : 0) | (value->hour >> 8));
+ str->length+= 6;
+ }
+ else
+ {
+ /*
+ !<-hours--><min-><sec->
+ 11234567890123456123456
+ <123456><123456><123456>
+ */
+ buf[0]= (value->second) | ((value->minute & 0x3) << 6);
+ buf[1]= (value->minute >> 2) | ((value->hour & 0xf) << 4);
+ buf[2]= (value->hour >> 4) | (value->neg ? 0x80 : 0);
+ str->length+= 3;
+ }
+
+ return ER_DYNCOL_OK;
+}
+
+
+/**
+ Read time value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.time_value.year= store_it_here->x.time_value.month=
+ store_it_here->x.time_value.day= 0;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_TIME;
+ return dynamic_column_time_read_internal(store_it_here, data, length);
+}
+
+/**
+ Internal function for reading time part from the string.
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_time_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ if (length != 6 && length != 3)
+ goto err;
+ if (length == 6)
+ {
+ /*
+ 00000!<-hours--><min-><sec-><---microseconds--->
+ 1123456789012345612345612345678901234567890
+ <123456><123456><123456><123456><123456><123456>
+ */
+ store_it_here->x.time_value.second_part= (data[0] |
+ (data[1] << 8) |
+ ((data[2] & 0xf) << 16));
+ store_it_here->x.time_value.second= ((data[2] >> 4) |
+ ((data[3] & 0x3) << 4));
+ store_it_here->x.time_value.minute= (data[3] >> 2);
+ store_it_here->x.time_value.hour= (((((uint)data[5]) & 0x3 ) << 8) | data[4]);
+ store_it_here->x.time_value.neg= ((data[5] & 0x4) ? 1 : 0);
+ }
+ else
+ {
+ /*
+ !<-hours--><min-><sec->
+ 11234567890123456123456
+ <123456><123456><123456>
+ */
+ store_it_here->x.time_value.second_part= 0;
+ store_it_here->x.time_value.second= (data[0] & 0x3f);
+ store_it_here->x.time_value.minute= (data[0] >> 6) | ((data[1] & 0xf) << 2);
+ store_it_here->x.time_value.hour= (data[1] >> 4) | ((data[2] & 0x3f) << 4);
+ store_it_here->x.time_value.neg= ((data[2] & 0x80) ? 1 : 0);
+ }
+ if (store_it_here->x.time_value.second > 59 ||
+ store_it_here->x.time_value.minute > 59 ||
+ store_it_here->x.time_value.hour > 838 ||
+ store_it_here->x.time_value.second_part > 999999)
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return ER_DYNCOL_FORMAT;
+}
+
+
+/**
+ Append the string with given date value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_store(DYNAMIC_COLUMN *str, MYSQL_TIME *value)
+{
+ uchar *buf;
+ if (ma_dynstr_realloc(str, 3))
+ return ER_DYNCOL_RESOURCE;
+
+ buf= ((uchar *)str->str) + str->length;
+ if (value->time_type == MYSQL_TIMESTAMP_NONE ||
+ value->time_type == MYSQL_TIMESTAMP_ERROR ||
+ value->time_type == MYSQL_TIMESTAMP_TIME)
+ value->year= value->month= value->day = 0;
+ DBUG_ASSERT(value->year <= 9999);
+ DBUG_ASSERT(value->month <= 12);
+ DBUG_ASSERT(value->day <= 31);
+ /*
+ 0<----year----><mn><day>
+ 012345678901234123412345
+ <123456><123456><123456>
+ */
+ buf[0]= (value->day |
+ ((value->month & 0x7) << 5));
+ buf[1]= ((value->month >> 3) | ((value->year & 0x7F) << 1));
+ buf[2]= (value->year >> 7);
+ str->length+= 3;
+ return ER_DYNCOL_OK;
+}
+
+
+
+/**
+ Read date value of given length from the packed string
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_read(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data, size_t length)
+{
+ store_it_here->x.time_value.neg= 0;
+ store_it_here->x.time_value.second_part= 0;
+ store_it_here->x.time_value.hour= 0;
+ store_it_here->x.time_value.minute= 0;
+ store_it_here->x.time_value.second= 0;
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_DATE;
+ return dynamic_column_date_read_internal(store_it_here, data, length);
+}
+
+/**
+ Internal function for reading date part from the string.
+
+ @param store_it_here The structure to store the value
+ @param data The packed string which should be read
+ @param length The length (in bytes) of the value in nthe string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_date_read_internal(DYNAMIC_COLUMN_VALUE *store_it_here,
+ uchar *data,
+ size_t length)
+{
+ if (length != 3)
+ goto err;
+ /*
+ 0<----year----><mn><day>
+ 12345678901234123412345
+ <123456><123456><123456>
+ */
+ store_it_here->x.time_value.day= (data[0] & 0x1f);
+ store_it_here->x.time_value.month= (((data[1] & 0x1) << 3) |
+ (data[0] >> 5));
+ store_it_here->x.time_value.year= ((((uint)data[2]) << 7) |
+ (data[1] >> 1));
+ if (store_it_here->x.time_value.day > 31 ||
+ store_it_here->x.time_value.month > 12 ||
+ store_it_here->x.time_value.year > 9999)
+ goto err;
+ return ER_DYNCOL_OK;
+
+err:
+ store_it_here->x.time_value.time_type= MYSQL_TIMESTAMP_ERROR;
+ return ER_DYNCOL_FORMAT;
+}
+
+
+/**
+ Append the string with given value.
+
+ @param str the string where to put the value
+ @param value the value to put in the string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+data_store(DYNAMIC_COLUMN *str, DYNAMIC_COLUMN_VALUE *value,
+ enum enum_dyncol_format format)
+{
+ switch (value->type) {
+ case DYN_COL_INT:
+ return dynamic_column_sint_store(str, value->x.long_value);
+ case DYN_COL_UINT:
+ return dynamic_column_uint_store(str, value->x.ulong_value);
+ case DYN_COL_DOUBLE:
+ return dynamic_column_double_store(str, value->x.double_value);
+ case DYN_COL_STRING:
+ return dynamic_column_string_store(str, &value->x.string.value,
+ value->x.string.charset);
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ return dynamic_column_decimal_store(str, &value->x.decimal.value);
+#endif
+ case DYN_COL_DATETIME:
+ /* date+time in bits: 14 + 4 + 5 + 5 + 6 + 6 40bits = 5 bytes */
+ return dynamic_column_date_time_store(str, &value->x.time_value, format);
+ case DYN_COL_DATE:
+ /* date in dits: 14 + 4 + 5 = 23bits ~= 3bytes*/
+ return dynamic_column_date_store(str, &value->x.time_value);
+ case DYN_COL_TIME:
+ /* time in bits: 5 + 6 + 6 = 17bits ~= 3bytes*/
+ return dynamic_column_time_store(str, &value->x.time_value, format);
+ case DYN_COL_DYNCOL:
+ return dynamic_column_dyncol_store(str, &value->x.string.value);
+ case DYN_COL_NULL:
+ break; /* Impossible */
+ default:
+ break;
+ }
+ DBUG_ASSERT(0);
+ return ER_DYNCOL_OK; /* Impossible */
+}
+
+
+/**
+ Write information to the fixed header
+
+ @param str String where to write the header
+ @param offset_size Size of offset field in bytes
+ @param column_count Number of columns
+*/
+
+static void set_fixed_header(DYNAMIC_COLUMN *str,
+ uint offset_size,
+ uint column_count)
+{
+ DBUG_ASSERT(column_count <= 0xffff);
+ DBUG_ASSERT(offset_size <= MAX_OFFSET_LENGTH);
+ str->str[0]= ((str->str[0] & ~DYNCOL_FLG_OFFSET) |
+ (offset_size - 1)); /* size of offset */
+ int2store(str->str + 1, column_count); /* columns number */
+ DBUG_ASSERT((str->str[0] & (~DYNCOL_FLG_KNOWN)) == 0);
+}
+
+/**
+ Adds columns into the empty string
+
+ @param str String where to write the data (the record)
+ @param hdr Dynamic columns record descriptor
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (uint or LEX_STRING)
+ @param values Array of columns values
+ @param new_str True if we need to allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_new_column_store(DYNAMIC_COLUMN *str,
+ DYN_HEADER *hdr,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_str)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ void **columns_order;
+ uchar *element;
+ uint i;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_RESOURCE;
+ size_t all_headers_size;
+
+ if (!(columns_order= malloc(sizeof(void*)*column_count)))
+ return ER_DYNCOL_RESOURCE;
+ if (new_str || str->str == 0)
+ {
+ if (column_count)
+ {
+ if (dynamic_column_init_named(str,
+ fmt->fixed_hdr +
+ hdr->header_size +
+ hdr->nmpool_size +
+ hdr->data_size +
+ DYNCOL_SYZERESERVE))
+ goto err;
+ }
+ else
+ {
+ dynamic_column_initialize(str);
+ }
+ }
+ else
+ {
+ str->length= 0;
+ if (ma_dynstr_realloc(str,
+ fmt->fixed_hdr +
+ hdr->header_size +
+ hdr->nmpool_size +
+ hdr->data_size +
+ DYNCOL_SYZERESERVE))
+ goto err;
+ }
+ if (!column_count)
+ {
+ free(columns_order);
+ return ER_DYNCOL_OK;
+ }
+
+ memset(str->str, 0, fmt->fixed_hdr);
+ str->length= fmt->fixed_hdr;
+
+ /* sort columns for the header */
+ for (i= 0, element= (uchar *) column_keys;
+ i < column_count;
+ i++, element+= fmt->key_size_in_array)
+ columns_order[i]= (void *)element;
+ qsort(columns_order, (size_t)column_count, sizeof(void*), fmt->column_sort);
+
+ /*
+ For now we don't allow creating two columns with the same number
+ at the time of create. This can be fixed later to just use the later
+ by comparing the pointers.
+ */
+ for (i= 0; i < column_count - 1; i++)
+ {
+ if ((*fmt->check_limit)(&columns_order[i]) ||
+ (*fmt->column_sort)(&columns_order[i], &columns_order[i + 1]) == 0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto err;
+ }
+ }
+ if ((*fmt->check_limit)(&columns_order[i]))
+ {
+ rc= ER_DYNCOL_DATA;
+ goto err;
+ }
+
+ (*fmt->set_fixed_hdr)(str, hdr);
+ /* reserve place for header and name pool */
+ str->length+= hdr->header_size + hdr->nmpool_size;
+
+ hdr->entry= hdr->header;
+ hdr->name= hdr->nmpool;
+ all_headers_size= fmt->fixed_hdr + hdr->header_size + hdr->nmpool_size;
+ for (i= 0; i < column_count; i++)
+ {
+ uint ord= (uint)(((uchar*)columns_order[i] - (uchar*)column_keys) /
+ fmt->key_size_in_array);
+ if (values[ord].type != DYN_COL_NULL)
+ {
+ /* Store header first in the str */
+ if ((*fmt->put_header_entry)(hdr, columns_order[i], values + ord,
+ str->length - all_headers_size))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+
+ /* Store value in 'str + str->length' and increase str->length */
+ if ((rc= data_store(str, values + ord, hdr->format)))
+ goto err;
+ }
+ }
+ rc= ER_DYNCOL_OK;
+err:
+ free(columns_order);
+ return rc;
+}
+
+/**
+ Calculate size of header, name pool and data pool
+
+ @param hdr descriptor of dynamic column record
+ @param column_count number of elements in arrays
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (uint or LEX_STRING)
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+calc_var_sizes(DYN_HEADER *hdr,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ uint i;
+ hdr->nmpool_size= hdr->data_size= 0;
+ hdr->column_count= 0;
+ for (i= 0; i < column_count; i++)
+ {
+ if (values[i].type != DYN_COL_NULL)
+ {
+ size_t tmp;
+ hdr->column_count++;
+ hdr->data_size+= (tmp= dynamic_column_value_len(values + i,
+ hdr->format));
+ if (tmp == (size_t) ~0)
+ return ER_DYNCOL_DATA;
+ hdr->nmpool_size+= (*fmt->name_size)(column_keys, i);
+ }
+ }
+ /*
+ We can handle data up to 0x1fffffff (old format) and
+ 0xfffffffff (new format) bytes now.
+ */
+ if ((hdr->offset_size= fmt->dynamic_column_offset_bytes(hdr->data_size)) >=
+ fmt->max_offset_size)
+ return ER_DYNCOL_LIMIT;
+
+ /* header entry is column number or string pointer + offset & type */
+ hdr->entry_size= fmt->fixed_hdr_entry + hdr->offset_size;
+ hdr->header_size= hdr->column_count * hdr->entry_size;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Create packed string which contains given columns (internal multi format)
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys (format dependent)
+ @param values Array of columns values
+ @param new_str True if we need allocate new string
+ @param string_keys keys are strings
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_create_many_internal_fmt(DYNAMIC_COLUMN *str,
+ uint column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_str,
+ my_bool string_keys)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+ memset(&header, 0, sizeof(header));
+ header.format= (string_keys ? 1 : 0);
+
+ if (new_str)
+ {
+ /* to make dynstr_free() working in case of errors */
+ memset(str, 0, sizeof(DYNAMIC_COLUMN));
+ }
+
+ if ((rc= calc_var_sizes(&header, column_count, column_keys, values)) < 0)
+ return rc;
+
+ return dynamic_new_column_store(str, &header,
+ column_count,
+ column_keys, values,
+ new_str);
+}
+
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_create_many(DYNAMIC_COLUMN *str,
+ uint column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_numbers, values,
+ TRUE, FALSE));
+}
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+ @param new_string True if we need allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_create_many_num(DYNAMIC_COLUMN *str,
+ uint column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_string)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_numbers, values,
+ new_string, FALSE));
+}
+
+/**
+ Create packed string which contains given columns
+
+ @param str String where to write the data
+ @param column_count Number of columns in the arrays
+ @param column_keys Array of columns keys
+ @param values Array of columns value
+ @param new_string True if we need allocate new string
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_create_many_named(DYNAMIC_COLUMN *str,
+ uint column_count,
+ LEX_STRING *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool new_string)
+{
+ return(dynamic_column_create_many_internal_fmt(str, column_count,
+ column_keys, values,
+ new_string, TRUE));
+}
+
+/**
+ Create packed string which contains given column
+
+ @param str String where to write the data
+ @param column_number Column number
+ @param value The columns value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_create(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *value)
+{
+ return(dynamic_column_create_many(str, 1, &column_nr, value));
+}
+
+
+/**
+ Calculate length of data between given two header entries
+
+ @param entry Pointer to the first entry
+ @param entry_next Pointer to the last entry
+ @param header_end Pointer to the header end
+ @param offset_size Size of offset field in bytes
+ @param last_offset Size of the data segment
+
+ @return number of bytes
+*/
+
+static size_t get_length_interval(uchar *entry, uchar *entry_next,
+ uchar *header_end, size_t offset_size,
+ size_t last_offset)
+{
+ size_t offset, offset_next;
+ DYNAMIC_COLUMN_TYPE type, type_next;
+ DBUG_ASSERT(entry < entry_next);
+
+ if (type_and_offset_read_num(&type, &offset, entry + COLUMN_NUMBER_SIZE,
+ offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ if (entry_next >= header_end)
+ return (last_offset - offset);
+ if (type_and_offset_read_num(&type_next, &offset_next,
+ entry_next + COLUMN_NUMBER_SIZE, offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ return (offset_next - offset);
+}
+
+
+/**
+ Calculate length of data between given hdr->entry and next_entry
+
+ @param hdr descriptor of dynamic column record
+ @param next_entry next header entry (can point just after last header
+ entry)
+
+ @return number of bytes
+*/
+
+static size_t hdr_interval_length(DYN_HEADER *hdr, uchar *next_entry)
+{
+ struct st_service_funcs *fmt= fmt_data + hdr->format;
+ size_t next_entry_offset;
+ DYNAMIC_COLUMN_TYPE next_entry_type;
+ DBUG_ASSERT(hdr->entry < next_entry);
+ DBUG_ASSERT(hdr->entry >= hdr->header);
+ DBUG_ASSERT(next_entry <= hdr->header + hdr->header_size);
+
+ if ((*fmt->type_and_offset_read)(&hdr->type, &hdr->offset,
+ hdr->entry + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ if (next_entry == hdr->header + hdr->header_size)
+ return hdr->data_size - hdr->offset;
+ if ((*fmt->type_and_offset_read)(&next_entry_type, &next_entry_offset,
+ next_entry + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ return DYNCOL_OFFSET_ERROR;
+ return (next_entry_offset - hdr->offset);
+}
+
+
+/**
+ Comparator function for references to header entries for qsort
+*/
+
+static int header_compar_num(const void *a, const void *b)
+{
+ uint va= uint2korr((uchar*)a), vb= uint2korr((uchar*)b);
+ return (va > vb ? 1 : (va < vb ? -1 : 0));
+}
+
+
+/**
+ Find entry in the numeric format header by the column number
+
+ @param hdr descriptor of dynamic column record
+ @param key number to find
+
+ @return pointer to the entry or NULL
+*/
+
+static uchar *find_entry_num(DYN_HEADER *hdr, uint key)
+{
+ uchar header_entry[2+4];
+ DBUG_ASSERT(hdr->format == dyncol_fmt_num);
+ int2store(header_entry, key);
+ return hdr->entry= bsearch(header_entry, hdr->header,
+ (size_t)hdr->column_count,
+ hdr->entry_size, &header_compar_num);
+}
+
+
+/**
+ Read name from header entry
+
+ @param hdr descriptor of dynamic column record
+ @param entry pointer to the header entry
+ @param name where to put name
+
+ @return 0 ok
+ @return 1 error in data
+*/
+
+static my_bool read_name(DYN_HEADER *hdr, uchar *entry, LEX_STRING *name)
+{
+ size_t nmoffset= uint2korr(entry);
+ uchar *next_entry= entry + hdr->entry_size;
+
+ if (nmoffset > hdr->nmpool_size)
+ return 1;
+
+ name->str= (char *)hdr->nmpool + nmoffset;
+ if (next_entry == hdr->header + hdr->header_size)
+ name->length= hdr->nmpool_size - nmoffset;
+ else
+ {
+ size_t next_nmoffset= uint2korr(next_entry);
+ if (next_nmoffset > hdr->nmpool_size)
+ return 1;
+ name->length= next_nmoffset - nmoffset;
+ }
+ return 0;
+}
+
+
+/**
+ Find entry in the names format header by the column number
+
+ @param hdr descriptor of dynamic column record
+ @param key name to find
+
+ @return pointer to the entry or NULL
+*/
+static uchar *find_entry_named(DYN_HEADER *hdr, LEX_STRING *key)
+{
+ uchar *min= hdr->header;
+ uchar *max= hdr->header + (hdr->column_count - 1) * hdr->entry_size;
+ uchar *mid;
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ DBUG_ASSERT(hdr->nmpool != NULL);
+ while (max >= min)
+ {
+ LEX_STRING name;
+ int cmp;
+ mid= hdr->header + ((min - hdr->header) +
+ (max - hdr->header)) /
+ 2 /
+ hdr->entry_size * hdr->entry_size;
+ if (read_name(hdr, mid, &name))
+ return NULL;
+ cmp= mariadb_dyncol_column_cmp_named(&name, key);
+ if (cmp < 0)
+ min= mid + hdr->entry_size;
+ else if (cmp > 0)
+ max= mid - hdr->entry_size;
+ else
+ return mid;
+ }
+ return NULL;
+}
+
+
+/**
+ Write number in the buffer (backward direction - starts from the buffer end)
+
+ @return pointer on the number beginning
+*/
+
+static char *backwritenum(char *chr, uint numkey)
+{
+ if (numkey == 0)
+ *(--chr)= '0';
+ else
+ while (numkey > 0)
+ {
+ *(--chr)= '0' + numkey % 10;
+ numkey/= 10;
+ }
+ return chr;
+}
+
+
+/**
+ Find column and fill information about it
+
+ @param hdr descriptor of dynamic column record
+ @param numkey Number of the column to fetch (if strkey is NULL)
+ @param strkey Name of the column to fetch (or NULL)
+
+ @return 0 ok
+ @return 1 error in data
+*/
+
+static my_bool
+find_column(DYN_HEADER *hdr, uint numkey, LEX_STRING *strkey)
+{
+ LEX_STRING nmkey;
+ char nmkeybuff[DYNCOL_NUM_CHAR]; /* to fit max 2 bytes number */
+ DBUG_ASSERT(hdr->header != NULL);
+
+ if (hdr->header + hdr->header_size > hdr->data_end)
+ return TRUE;
+
+ /* fix key */
+ if (hdr->format == dyncol_fmt_num && strkey != NULL)
+ {
+ char *end;
+ numkey= (uint) strtoul(strkey->str, &end, 10);
+ if (end != strkey->str + strkey->length)
+ {
+ /* we can't find non-numeric key among numeric ones */
+ hdr->type= DYN_COL_NULL;
+ return 0;
+ }
+ }
+ else if (hdr->format == dyncol_fmt_str && strkey == NULL)
+ {
+ nmkey.str= backwritenum(nmkeybuff + sizeof(nmkeybuff), numkey);
+ nmkey.length= (nmkeybuff + sizeof(nmkeybuff)) - nmkey.str;
+ strkey= &nmkey;
+ }
+ if (hdr->format == dyncol_fmt_num)
+ hdr->entry= find_entry_num(hdr, numkey);
+ else
+ hdr->entry= find_entry_named(hdr, strkey);
+
+ if (!hdr->entry)
+ {
+ /* Column not found */
+ hdr->type= DYN_COL_NULL;
+ return 0;
+ }
+ hdr->length= hdr_interval_length(hdr, hdr->entry + hdr->entry_size);
+ hdr->data= hdr->dtpool + hdr->offset;
+ /*
+ Check that the found data is within the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (hdr->length == DYNCOL_OFFSET_ERROR ||
+ hdr->length > INT_MAX || hdr->offset > hdr->data_size)
+ return 1;
+
+ return 0;
+}
+
+
+/**
+ Read and check the header of the dynamic string
+
+ @param hdr descriptor of dynamic column record
+ @param str Dynamic string
+
+ @retval FALSE OK
+ @retval TRUE error
+
+ Note
+ We don't check for str->length == 0 as all code that calls this
+ already have handled this case.
+*/
+
+static inline my_bool read_fixed_header(DYN_HEADER *hdr,
+ DYNAMIC_COLUMN *str)
+{
+ DBUG_ASSERT(str != NULL && str->length != 0);
+ if ((str->length < 1) ||
+ (str->str[0] & (~DYNCOL_FLG_KNOWN)))
+ return 1;
+ hdr->format= ((str->str[0] & DYNCOL_FLG_NAMES) ?
+ dyncol_fmt_str:
+ dyncol_fmt_num);
+ if ((str->length < fmt_data[hdr->format].fixed_hdr))
+ return 1; /* Wrong header */
+ hdr->offset_size= (str->str[0] & DYNCOL_FLG_OFFSET) + 1 +
+ (hdr->format == dyncol_fmt_str ? 1 : 0);
+ hdr->column_count= uint2korr(str->str + 1);
+ if (hdr->format == dyncol_fmt_str)
+ hdr->nmpool_size= uint2korr(str->str + 3); // only 2 bytes supported for now
+ else
+ hdr->nmpool_size= 0;
+ return 0;
+}
+
+
+/**
+ Get dynamic column value by column number
+
+ @param str The packed string to extract the column
+ @param column_nr Number of column to fetch
+ @param store_it_here Where to store the extracted value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+dynamic_column_get(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_get_num(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ return dynamic_column_get_internal(str, store_it_here, column_nr, NULL);
+}
+
+
+/**
+ Get dynamic column value by name
+
+ @param str The packed string to extract the column
+ @param name Name of column to fetch
+ @param store_it_here Where to store the extracted value
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_get_named(DYNAMIC_COLUMN *str, LEX_STRING *name,
+ DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ DBUG_ASSERT(name != NULL);
+ return dynamic_column_get_internal(str, store_it_here, 0, name);
+}
+
+
+static enum enum_dyncol_func_result
+dynamic_column_get_value(DYN_HEADER *hdr, DYNAMIC_COLUMN_VALUE *store_it_here)
+{
+ static enum enum_dyncol_func_result rc;
+ switch ((store_it_here->type= hdr->type)) {
+ case DYN_COL_INT:
+ rc= dynamic_column_sint_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_UINT:
+ rc= dynamic_column_uint_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_DOUBLE:
+ rc= dynamic_column_double_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_STRING:
+ rc= dynamic_column_string_read(store_it_here, hdr->data, hdr->length);
+ break;
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ rc= dynamic_column_decimal_read(store_it_here, hdr->data, hdr->length);
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ rc= dynamic_column_date_time_read(store_it_here, hdr->data,
+ hdr->length);
+ break;
+ case DYN_COL_DATE:
+ rc= dynamic_column_date_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_TIME:
+ rc= dynamic_column_time_read(store_it_here, hdr->data, hdr->length);
+ break;
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_OK;
+ break;
+ case DYN_COL_DYNCOL:
+ rc= dynamic_column_dyncol_read(store_it_here, hdr->data, hdr->length);
+ break;
+ default:
+ rc= ER_DYNCOL_FORMAT;
+ store_it_here->type= DYN_COL_NULL;
+ break;
+ }
+ return rc;
+}
+
+/**
+ Get dynamic column value by number or name
+
+ @param str The packed string to extract the column
+ @param store_it_here Where to store the extracted value
+ @param numkey Number of the column to fetch (if strkey is NULL)
+ @param strkey Name of the column to fetch (or NULL)
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_get_internal(DYNAMIC_COLUMN *str,
+ DYNAMIC_COLUMN_VALUE *store_it_here,
+ uint num_key, LEX_STRING *str_key)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ memset(&header, 0, sizeof(header));
+
+ if (str->length == 0)
+ goto null;
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto err;
+
+ if (header.column_count == 0)
+ goto null;
+
+ if (find_column(&header, num_key, str_key))
+ goto err;
+
+ rc= dynamic_column_get_value(&header, store_it_here);
+ return rc;
+
+null:
+ rc= ER_DYNCOL_OK;
+err:
+ store_it_here->type= DYN_COL_NULL;
+ return rc;
+}
+
+
+/**
+ Check existence of the column in the packed string (by number)
+
+ @param str The packed string to check the column
+ @param column_nr Number of column to check
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_exists_num(DYNAMIC_COLUMN *str, uint column_nr)
+{
+ return dynamic_column_exists_internal(str, column_nr, NULL);
+}
+
+/**
+ Check existence of the column in the packed string (by name)
+
+ @param str The packed string to check the column
+ @param name Name of column to check
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_exists_named(DYNAMIC_COLUMN *str, LEX_STRING *name)
+{
+ DBUG_ASSERT(name != NULL);
+ return dynamic_column_exists_internal(str, 0, name);
+}
+
+
+/**
+ Check existence of the column in the packed string (by name of number)
+
+ @param str The packed string to check the column
+ @param num_key Number of the column to fetch (if strkey is NULL)
+ @param str_key Name of the column to fetch (or NULL)
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_exists_internal(DYNAMIC_COLUMN *str, uint num_key,
+ LEX_STRING *str_key)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+ memset(&header, 0, sizeof(header));
+
+ if (str->length == 0)
+ return ER_DYNCOL_NO; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.column_count == 0)
+ return ER_DYNCOL_NO; /* no columns */
+
+ if (find_column(&header, num_key, str_key))
+ return ER_DYNCOL_FORMAT;
+
+ return (header.type != DYN_COL_NULL ? ER_DYNCOL_YES : ER_DYNCOL_NO);
+}
+
+
+/**
+ List not-null columns in the packed string (only numeric format)
+
+ @param str The packed string
+ @param array_of_uint Where to put reference on created array
+
+ @return ER_DYNCOL_* return code
+*/
+enum enum_dyncol_func_result
+dynamic_column_list(DYNAMIC_COLUMN *str, DYNAMIC_ARRAY *array_of_uint)
+{
+ DYN_HEADER header;
+ uchar *read;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ memset(array_of_uint, 0, sizeof(*array_of_uint)); /* In case of errors */
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.format != dyncol_fmt_num)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (ma_init_dynamic_array(array_of_uint, sizeof(uint), header.column_count, 0))
+ return ER_DYNCOL_RESOURCE;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ uint nm= uint2korr(read);
+ /* Insert can't never fail as it's pre-allocated above */
+ (void) ma_insert_dynamic(array_of_uint, (uchar *)&nm);
+ }
+ return ER_DYNCOL_OK;
+}
+
+/**
+ List not-null columns in the packed string (only numeric format)
+
+ @param str The packed string
+ @param array_of_uint Where to put reference on created array
+
+ @return ER_DYNCOL_* return code
+*/
+enum enum_dyncol_func_result
+mariadb_dyncol_list_num(DYNAMIC_COLUMN *str, uint *count, uint **nums)
+{
+ DYN_HEADER header;
+ uchar *read;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ (*nums)= 0; (*count)= 0; /* In case of errors */
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ if (header.format != dyncol_fmt_num)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (!((*nums)= (uint *)malloc(sizeof(uint) * header.column_count)))
+ return ER_DYNCOL_RESOURCE;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ (*nums)[i]= uint2korr(read);
+ }
+ (*count)= header.column_count;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ List not-null columns in the packed string (any format)
+
+ @param str The packed string
+ @param count Number of names in the list
+ @param names Where to put names list (should be freed)
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_list_named(DYNAMIC_COLUMN *str, uint *count, LEX_STRING **names)
+{
+ DYN_HEADER header;
+ uchar *read;
+ char *pool;
+ struct st_service_funcs *fmt;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ (*names)= 0; (*count)= 0;
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+ fmt= fmt_data + header.format;
+
+ if (header.entry_size * header.column_count + fmt->fixed_hdr >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ if (header.format == dyncol_fmt_num)
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ DYNCOL_NUM_CHAR * header.column_count);
+ else
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ header.nmpool_size + header.column_count);
+ if (!(*names))
+ return ER_DYNCOL_RESOURCE;
+ pool= ((char *)(*names)) + sizeof(LEX_STRING) * header.column_count;
+
+ for (i= 0, read= header.header;
+ i < header.column_count;
+ i++, read+= header.entry_size)
+ {
+ if (header.format == dyncol_fmt_num)
+ {
+ uint nm= uint2korr(read);
+ (*names)[i].str= pool;
+ pool+= DYNCOL_NUM_CHAR;
+ (*names)[i].length=
+ ma_ll2str(nm, (*names)[i].str, 10) - (*names)[i].str;
+ }
+ else
+ {
+ LEX_STRING tmp;
+ if (read_name(&header, read, &tmp))
+ return ER_DYNCOL_FORMAT;
+ (*names)[i].length= tmp.length;
+ (*names)[i].str= pool;
+ pool+= tmp.length + 1;
+ memcpy((*names)[i].str, (const void *)tmp.str, tmp.length);
+ (*names)[i].str[tmp.length]= '\0'; // just for safety
+ }
+ }
+ (*count)= header.column_count;
+ return ER_DYNCOL_OK;
+}
+
+/**
+ Find the place of the column in the header or place where it should be put
+
+ @param hdr descriptor of dynamic column record
+ @param key Name or number of column to fetch
+ (depends on string_key)
+ @param string_key True if we gave pointer to LEX_STRING.
+
+ @retval TRUE found
+ @retval FALSE pointer set to the next row
+*/
+
+static my_bool
+find_place(DYN_HEADER *hdr, void *key, my_bool string_keys)
+{
+ uint mid, start, end, val;
+ int UNINIT_VAR(flag);
+ LEX_STRING str;
+ char buff[DYNCOL_NUM_CHAR];
+ my_bool need_conversion= ((string_keys ? dyncol_fmt_str : dyncol_fmt_num) !=
+ hdr->format);
+ /* new format can't be numeric if the old one is names */
+ DBUG_ASSERT(string_keys ||
+ hdr->format == dyncol_fmt_num);
+
+ start= 0;
+ end= hdr->column_count -1;
+ mid= 1;
+ while (start != end)
+ {
+ uint val;
+ mid= (start + end) / 2;
+ hdr->entry= hdr->header + mid * hdr->entry_size;
+ if (!string_keys)
+ {
+ val= uint2korr(hdr->entry);
+ flag= CMP_NUM(*((uint *)key), val);
+ }
+ else
+ {
+ if (need_conversion)
+ {
+ str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
+ str.length= (buff + sizeof(buff)) - str.str;
+ }
+ else
+ {
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ if (read_name(hdr, hdr->entry, &str))
+ return 0;
+ }
+ flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
+ }
+ if (flag <= 0)
+ end= mid;
+ else
+ start= mid + 1;
+ }
+ hdr->entry= hdr->header + start * hdr->entry_size;
+ if (start != mid)
+ {
+ if (!string_keys)
+ {
+ val= uint2korr(hdr->entry);
+ flag= CMP_NUM(*((uint *)key), val);
+ }
+ else
+ {
+ if (need_conversion)
+ {
+ str.str= backwritenum(buff + sizeof(buff), uint2korr(hdr->entry));
+ str.length= (buff + sizeof(buff)) - str.str;
+ }
+ else
+ {
+ DBUG_ASSERT(hdr->format == dyncol_fmt_str);
+ if (read_name(hdr, hdr->entry, &str))
+ return 0;
+ }
+ flag= mariadb_dyncol_column_cmp_named((LEX_STRING *)key, &str);
+ }
+ }
+ if (flag > 0)
+ hdr->entry+= hdr->entry_size; /* Point at next bigger key */
+ return flag == 0;
+}
+
+
+/*
+ It is internal structure which describes a plan of changing the record
+ of dynamic columns
+*/
+
+typedef enum {PLAN_REPLACE, PLAN_ADD, PLAN_DELETE, PLAN_NOP} PLAN_ACT;
+
+struct st_plan {
+ DYNAMIC_COLUMN_VALUE *val;
+ void *key;
+ uchar *place;
+ size_t length;
+ long long hdelta, ddelta, ndelta;
+ long long mv_offset, mv_length;
+ uint mv_end;
+ PLAN_ACT act;
+};
+typedef struct st_plan PLAN;
+
+
+/**
+ Sort function for plan by column number
+*/
+
+static int plan_sort_num(const void *a, const void *b)
+{
+ return *((uint *)((PLAN *)a)->key) - *((uint *)((PLAN *)b)->key);
+}
+
+
+/**
+ Sort function for plan by column name
+*/
+
+static int plan_sort_named(const void *a, const void *b)
+{
+ return mariadb_dyncol_column_cmp_named((LEX_STRING *)((PLAN *)a)->key,
+ (LEX_STRING *)((PLAN *)b)->key);
+}
+
+#define DELTA_CHECK(S, D, C) \
+ if ((S) == 0) \
+ (S)= (D); \
+ else if (((S) > 0 && (D) < 0) || \
+ ((S) < 0 && (D) > 0)) \
+ { \
+ (C)= TRUE; \
+ }
+
+/**
+ Update dynamic column by copying in a new record (string).
+
+ @param str Dynamic column record to change
+ @param plan Plan of changing the record
+ @param add_column_count number of records in the plan array.
+ @param hdr descriptor of old dynamic column record
+ @param new_hdr descriptor of new dynamic column record
+ @param convert need conversion from numeric to names format
+
+ @return ER_DYNCOL_* return code
+*/
+
+static enum enum_dyncol_func_result
+dynamic_column_update_copy(DYNAMIC_COLUMN *str, PLAN *plan,
+ uint add_column_count,
+ DYN_HEADER *hdr, DYN_HEADER *new_hdr,
+ my_bool convert)
+{
+ DYNAMIC_COLUMN tmp;
+ struct st_service_funcs *fmt= fmt_data + hdr->format,
+ *new_fmt= fmt_data + new_hdr->format;
+ uint i, j, k;
+ size_t all_headers_size;
+
+ if (dynamic_column_init_named(&tmp,
+ (new_fmt->fixed_hdr + new_hdr->header_size +
+ new_hdr->nmpool_size +
+ new_hdr->data_size + DYNCOL_SYZERESERVE)))
+ {
+ return ER_DYNCOL_RESOURCE;
+ }
+ memset(tmp.str, 0, new_fmt->fixed_hdr);
+ (*new_fmt->set_fixed_hdr)(&tmp, new_hdr);
+ /* Adjust tmp to contain whole the future header */
+ tmp.length= new_fmt->fixed_hdr + new_hdr->header_size + new_hdr->nmpool_size;
+
+
+ /*
+ Copy data to the new string
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ new_hdr->entry= new_hdr->header;
+ new_hdr->name= new_hdr->nmpool;
+ all_headers_size= new_fmt->fixed_hdr +
+ new_hdr->header_size + new_hdr->nmpool_size;
+ for (i= 0, j= 0; i < add_column_count || j < hdr->column_count; i++)
+ {
+ size_t first_offset= 0;
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= hdr->column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (hdr->header + j * hdr->entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= hdr->header + k * hdr->entry_size;
+ void *key;
+ LEX_STRING name;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+ char buff[DYNCOL_NUM_CHAR];
+
+ if (hdr->format == dyncol_fmt_num)
+ {
+ if (convert)
+ {
+ name.str= backwritenum(buff + sizeof(buff), uint2korr(read));
+ name.length= (buff + sizeof(buff)) - name.str;
+ key= &name;
+ }
+ else
+ {
+ nm= uint2korr(read); /* Column number */
+ key= &nm;
+ }
+ }
+ else
+ {
+ if (read_name(hdr, read, &name))
+ goto err;
+ key= &name;
+ }
+ if (fmt->type_and_offset_read(&tp, &offs,
+ read + fmt->fixed_hdr_entry,
+ hdr->offset_size))
+ goto err;
+ if (k == start)
+ first_offset= offs;
+ else if (offs < first_offset)
+ goto err;
+
+ offs+= (size_t)plan[i].ddelta;
+ {
+ DYNAMIC_COLUMN_VALUE val;
+ val.type= tp; // only the type used in the header
+ if ((*new_fmt->put_header_entry)(new_hdr, key, &val, offs))
+ goto err;
+ }
+ }
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end)
+ {
+ size_t data_size;
+ /* Add old data last in 'tmp' */
+ hdr->entry= hdr->header + start * hdr->entry_size;
+ data_size=
+ hdr_interval_length(hdr, hdr->header + end * hdr->entry_size);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > hdr->data_size - first_offset)
+ goto err;
+
+ memcpy(tmp.str + tmp.length, (char *)hdr->dtpool + first_offset,
+ data_size);
+ tmp.length+= data_size;
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ if ((*new_fmt->put_header_entry)(new_hdr, plan[i].key,
+ plan[i].val,
+ tmp.length - all_headers_size))
+ goto err;
+ data_store(&tmp, plan[i].val, new_hdr->format); /* Append new data */
+ }
+ }
+ }
+ dynamic_column_column_free(str);
+ *str= tmp;
+ return ER_DYNCOL_OK;
+err:
+ dynamic_column_column_free(&tmp);
+ return ER_DYNCOL_FORMAT;
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_update_move_left(DYNAMIC_COLUMN *str, PLAN *plan,
+ size_t offset_size,
+ size_t entry_size,
+ size_t header_size,
+ size_t new_offset_size,
+ size_t new_entry_size,
+ size_t new_header_size,
+ uint column_count,
+ uint new_column_count,
+ uint add_column_count,
+ uchar *header_end,
+ size_t max_offset)
+{
+ uchar *write;
+ uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
+ uint i, j, k;
+ size_t curr_offset;
+
+ write= (uchar *)str->str + FIXED_HEADER_SIZE;
+ set_fixed_header(str, (uint)new_offset_size, new_column_count);
+
+ /*
+ Move headers first.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ for (curr_offset= 0, i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ size_t UNINIT_VAR(first_offset);
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (header_base + j * entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+ plan[i].mv_end= end;
+
+ {
+ DYNAMIC_COLUMN_TYPE tp;
+ if (type_and_offset_read_num(&tp, &first_offset,
+ header_base + start * entry_size +
+ COLUMN_NUMBER_SIZE, offset_size))
+ return ER_DYNCOL_FORMAT;
+ }
+ /* find data to be moved */
+ if (start < end)
+ {
+ size_t data_size=
+ get_length_interval(header_base + start * entry_size,
+ header_base + end * entry_size,
+ header_end, offset_size, max_offset);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > max_offset - first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
+ plan[i].mv_offset= first_offset;
+ plan[i].mv_length= data_size;
+ curr_offset+= data_size;
+ }
+ else
+ {
+ plan[i].mv_length= 0;
+ plan[i].mv_offset= curr_offset;
+ }
+
+ if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
+ plan[i].act != PLAN_DELETE)
+ write+= entry_size * (end - start);
+ else
+ {
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= header_base + k * entry_size;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+
+ nm= uint2korr(read); /* Column number */
+ if (type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
+ offset_size))
+ return ER_DYNCOL_FORMAT;
+
+ if (k > start && offs < first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+
+ offs+= (size_t)plan[i].ddelta;
+ int2store(write, nm);
+ /* write rest of data at write + COLUMN_NUMBER_SIZE */
+ type_and_offset_store_num(write, new_offset_size, tp, offs);
+ write+= new_entry_size;
+ }
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ int2store(write, *((uint *)plan[i].key));
+ type_and_offset_store_num(write, new_offset_size,
+ plan[i].val[0].type,
+ curr_offset);
+ write+= new_entry_size;
+ curr_offset+= plan[i].length;
+ }
+ }
+ }
+
+ /*
+ Move data.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ str->length= (FIXED_HEADER_SIZE + new_header_size);
+ for (i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ j= end= plan[i].mv_end;
+ if (i != add_column_count &&
+ (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++;
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end && plan[i].mv_length)
+ {
+ memmove((header_base + new_header_size +
+ (size_t)plan[i].mv_offset + (size_t)plan[i].ddelta),
+ header_base + header_size + (size_t)plan[i].mv_offset,
+ (size_t)plan[i].mv_length);
+ }
+ str->length+= (size_t)plan[i].mv_length;
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ data_store(str, plan[i].val, dyncol_fmt_num);/* Append new data */
+ }
+ }
+ }
+ return ER_DYNCOL_OK;
+}
+
+#ifdef UNUSED
+static enum enum_dyncol_func_result
+dynamic_column_update_move_right(DYNAMIC_COLUMN *str, PLAN *plan,
+ size_t offset_size,
+ size_t entry_size,
+ size_t header_size,
+ size_t new_offset_size,
+ size_t new_entry_size,
+ size_t new_header_size,
+ uint column_count,
+ uint new_column_count,
+ uint add_column_count,
+ uchar *header_end,
+ size_t max_offset)
+{
+ uchar *write;
+ uchar *header_base= (uchar *)str->str + FIXED_HEADER_SIZE;
+ uint i, j, k;
+ size_t curr_offset;
+
+ write= (uchar *)str->str + FIXED_HEADER_SIZE;
+ set_fixed_header(str, new_offset_size, new_column_count);
+
+ /*
+ Move data first.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ for (curr_offset= 0, i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ size_t UNINIT_VAR(first_offset);
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ if (i == add_column_count)
+ j= end= column_count;
+ else
+ {
+ /*
+ old data portion. We don't need to check that j < column_count
+ as plan[i].place is guaranteed to have a pointer inside the
+ data.
+ */
+ while (header_base + j * entry_size < plan[i].place)
+ j++;
+ end= j;
+ if ((plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++; /* data at 'j' will be removed */
+ }
+ plan[i].mv_end= end;
+
+ {
+ DYNAMIC_COLUMN_TYPE tp;
+ type_and_offset_read_num(&tp, &first_offset,
+ header_base +
+ start * entry_size + COLUMN_NUMBER_SIZE,
+ offset_size);
+ }
+ /* find data to be moved */
+ if (start < end)
+ {
+ size_t data_size=
+ get_length_interval(header_base + start * entry_size,
+ header_base + end * entry_size,
+ header_end, offset_size, max_offset);
+ if (data_size == DYNCOL_OFFSET_ERROR ||
+ (long) data_size < 0 ||
+ data_size > max_offset - first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ DBUG_ASSERT(curr_offset == first_offset + plan[i].ddelta);
+ plan[i].mv_offset= first_offset;
+ plan[i].mv_length= data_size;
+ curr_offset+= data_size;
+ }
+ else
+ {
+ plan[i].mv_length= 0;
+ plan[i].mv_offset= curr_offset;
+ }
+
+ if (plan[i].ddelta == 0 && offset_size == new_offset_size &&
+ plan[i].act != PLAN_DELETE)
+ write+= entry_size * (end - start);
+ else
+ {
+ /*
+ Adjust all headers since last loop.
+ We have to do this as the offset for data has moved
+ */
+ for (k= start; k < end; k++)
+ {
+ uchar *read= header_base + k * entry_size;
+ size_t offs;
+ uint nm;
+ DYNAMIC_COLUMN_TYPE tp;
+
+ nm= uint2korr(read); /* Column number */
+ type_and_offset_read_num(&tp, &offs, read + COLUMN_NUMBER_SIZE,
+ offset_size);
+ if (k > start && offs < first_offset)
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+
+ offs+= plan[i].ddelta;
+ int2store(write, nm);
+ /* write rest of data at write + COLUMN_NUMBER_SIZE */
+ if (type_and_offset_store_num(write, new_offset_size, tp, offs))
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ write+= new_entry_size;
+ }
+ }
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ int2store(write, *((uint *)plan[i].key));
+ if (type_and_offset_store_num(write, new_offset_size,
+ plan[i].val[0].type,
+ curr_offset))
+ {
+ str->length= 0; // just something valid
+ return ER_DYNCOL_FORMAT;
+ }
+ write+= new_entry_size;
+ curr_offset+= plan[i].length;
+ }
+ }
+ }
+
+ /*
+ Move headers.
+ i= index in array of changes
+ j= index in packed string header index
+ */
+ str->length= (FIXED_HEADER_SIZE + new_header_size);
+ for (i= 0, j= 0;
+ i < add_column_count || j < column_count;
+ i++)
+ {
+ uint start= j, end;
+
+ /*
+ Search in i and j for the next column to add from i and where to
+ add.
+ */
+
+ while (i < add_column_count && plan[i].act == PLAN_NOP)
+ i++; /* skip NOP */
+
+ j= end= plan[i].mv_end;
+ if (i != add_column_count &&
+ (plan[i].act == PLAN_REPLACE || plan[i].act == PLAN_DELETE))
+ j++;
+
+ /* copy first the data that was not replaced in original packed data */
+ if (start < end && plan[i].mv_length)
+ {
+ memmove((header_base + new_header_size +
+ plan[i].mv_offset + plan[i].ddelta),
+ header_base + header_size + plan[i].mv_offset,
+ plan[i].mv_length);
+ }
+ str->length+= plan[i].mv_length;
+
+ /* new data adding */
+ if (i < add_column_count)
+ {
+ if( plan[i].act == PLAN_ADD || plan[i].act == PLAN_REPLACE)
+ {
+ data_store(str, plan[i].val, dyncol_fmt_num); /* Append new data */
+ }
+ }
+ }
+ return ER_DYNCOL_OK;
+}
+#endif
+
+/**
+ Update the packed string with the given columns
+
+ @param str String where to write the data
+ @param add_column_count Number of columns in the arrays
+ @param column_numbers Array of columns numbers
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+/* plan allocated on the stack */
+#define IN_PLACE_PLAN 4
+
+enum enum_dyncol_func_result
+dynamic_column_update_many(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
+ values, FALSE);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_update_many_num(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ uint *column_numbers,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_numbers,
+ values, FALSE);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_update_many_named(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ LEX_STRING *column_names,
+ DYNAMIC_COLUMN_VALUE *values)
+{
+ return dynamic_column_update_many_fmt(str, add_column_count, column_names,
+ values, TRUE);
+}
+
+static uint numlen(uint val)
+{
+ uint res;
+ if (val == 0)
+ return 1;
+ res= 0;
+ while(val)
+ {
+ res++;
+ val/=10;
+ }
+ return res;
+}
+
+static enum enum_dyncol_func_result
+dynamic_column_update_many_fmt(DYNAMIC_COLUMN *str,
+ uint add_column_count,
+ void *column_keys,
+ DYNAMIC_COLUMN_VALUE *values,
+ my_bool string_keys)
+{
+ PLAN *plan, *alloc_plan= NULL, in_place_plan[IN_PLACE_PLAN];
+ uchar *element;
+ DYN_HEADER header, new_header;
+ struct st_service_funcs *fmt, *new_fmt;
+ long long data_delta= 0, name_delta= 0;
+ uint i;
+ uint not_null;
+ long long header_delta= 0;
+ long long header_delta_sign, data_delta_sign;
+ int copy= FALSE;
+ enum enum_dyncol_func_result rc;
+ my_bool convert;
+
+ if (add_column_count == 0)
+ return ER_DYNCOL_OK;
+
+ memset(&header, 0, sizeof(header));
+ memset(&new_header, 0, sizeof(new_header));
+ new_header.format= (string_keys ? dyncol_fmt_str : dyncol_fmt_num);
+ new_fmt= fmt_data + new_header.format;
+
+ /*
+ Get columns in column order. As the data in 'str' is already
+ in column order this allows to replace all columns in one loop.
+ */
+ if (IN_PLACE_PLAN > add_column_count)
+ plan= in_place_plan;
+ else if (!(alloc_plan= plan=
+ (PLAN *)malloc(sizeof(PLAN) * (add_column_count + 1))))
+ return ER_DYNCOL_RESOURCE;
+
+ not_null= add_column_count;
+ for (i= 0, element= (uchar *) column_keys;
+ i < add_column_count;
+ i++, element+= new_fmt->key_size_in_array)
+ {
+ if ((*new_fmt->check_limit)(&element))
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+
+ plan[i].val= values + i;
+ plan[i].key= element;
+ if (values[i].type == DYN_COL_NULL)
+ not_null--;
+
+ }
+
+ if (str->length == 0)
+ {
+ /*
+ Just add new columns. If there was no columns to add we return
+ an empty string.
+ */
+ goto create_new_string;
+ }
+
+ /* Check that header is ok */
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto end;
+ fmt= fmt_data + header.format;
+ /* new format can't be numeric if the old one is names */
+ DBUG_ASSERT(new_header.format == dyncol_fmt_str ||
+ header.format == dyncol_fmt_num);
+ if (header.column_count == 0)
+ goto create_new_string;
+
+ qsort(plan, (size_t)add_column_count, sizeof(PLAN), new_fmt->plan_sort);
+
+ new_header.column_count= header.column_count;
+ new_header.nmpool_size= header.nmpool_size;
+ if ((convert= (new_header.format == dyncol_fmt_str &&
+ header.format == dyncol_fmt_num)))
+ {
+ DBUG_ASSERT(new_header.nmpool_size == 0);
+ for(i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ new_header.nmpool_size+= numlen(uint2korr(header.entry));
+ }
+ }
+
+ if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+
+ /*
+ Calculate how many columns and data is added/deleted and make a 'plan'
+ for each of them.
+ */
+ for (i= 0; i < add_column_count; i++)
+ {
+ /*
+ For now we don't allow creating two columns with the same number
+ at the time of create. This can be fixed later to just use the later
+ by comparing the pointers.
+ */
+ if (i < add_column_count - 1 &&
+ new_fmt->column_sort(&plan[i].key, &plan[i + 1].key) == 0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+
+ /* Set common variables for all plans */
+ plan[i].ddelta= data_delta;
+ plan[i].ndelta= name_delta;
+ /* get header delta in entries */
+ plan[i].hdelta= header_delta;
+ plan[i].length= 0; /* Length if NULL */
+
+ if (find_place(&header, plan[i].key, string_keys))
+ {
+ size_t entry_data_size, entry_name_size= 0;
+
+ /* Data existed; We have to replace or delete it */
+
+ entry_data_size= hdr_interval_length(&header, header.entry +
+ header.entry_size);
+ if (entry_data_size == DYNCOL_OFFSET_ERROR ||
+ (long) entry_data_size < 0)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+
+ if (new_header.format == dyncol_fmt_str)
+ {
+ if (header.format == dyncol_fmt_str)
+ {
+ LEX_STRING name;
+ if (read_name(&header, header.entry, &name))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+ entry_name_size= name.length;
+ }
+ else
+ entry_name_size= numlen(uint2korr(header.entry));
+ }
+
+ if (plan[i].val->type == DYN_COL_NULL)
+ {
+ /* Inserting a NULL means delete the old data */
+
+ plan[i].act= PLAN_DELETE; /* Remove old value */
+ header_delta--; /* One row less in header */
+ data_delta-= entry_data_size; /* Less data to store */
+ name_delta-= entry_name_size;
+ }
+ else
+ {
+ /* Replace the value */
+
+ plan[i].act= PLAN_REPLACE;
+ /* get data delta in bytes */
+ if ((plan[i].length= dynamic_column_value_len(plan[i].val,
+ new_header.format)) ==
+ (size_t) ~0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+ data_delta+= plan[i].length - entry_data_size;
+ if (new_header.format == dyncol_fmt_str)
+ {
+ name_delta+= ((LEX_STRING *)(plan[i].key))->length - entry_name_size;
+ }
+ }
+ }
+ else
+ {
+ /* Data did not exists. Add if it it's not NULL */
+
+ if (plan[i].val->type == DYN_COL_NULL)
+ {
+ plan[i].act= PLAN_NOP; /* Mark entry to be skipped */
+ }
+ else
+ {
+ /* Add new value */
+
+ plan[i].act= PLAN_ADD;
+ header_delta++; /* One more row in header */
+ /* get data delta in bytes */
+ if ((plan[i].length= dynamic_column_value_len(plan[i].val,
+ new_header.format)) ==
+ (size_t) ~0)
+ {
+ rc= ER_DYNCOL_DATA;
+ goto end;
+ }
+ data_delta+= plan[i].length;
+ if (new_header.format == dyncol_fmt_str)
+ name_delta+= ((LEX_STRING *)plan[i].key)->length;
+ }
+ }
+ plan[i].place= header.entry;
+ }
+ plan[add_column_count].hdelta= header_delta;
+ plan[add_column_count].ddelta= data_delta;
+ plan[add_column_count].act= PLAN_NOP;
+ plan[add_column_count].place= header.dtpool;
+
+ new_header.column_count= (uint)(header.column_count + header_delta);
+
+ /*
+ Check if it is only "increasing" or only "decreasing" plan for (header
+ and data separately).
+ */
+ new_header.data_size= header.data_size + (size_t)data_delta;
+ new_header.nmpool_size= new_header.nmpool_size + (size_t)name_delta;
+ DBUG_ASSERT(new_header.format != dyncol_fmt_num ||
+ new_header.nmpool_size == 0);
+ if ((new_header.offset_size=
+ new_fmt->dynamic_column_offset_bytes(new_header.data_size)) >=
+ new_fmt->max_offset_size)
+ {
+ rc= ER_DYNCOL_LIMIT;
+ goto end;
+ }
+
+ copy= ((header.format != new_header.format) ||
+ (new_header.format == dyncol_fmt_str));
+ /* if (new_header.offset_size!=offset_size) then we have to rewrite header */
+ header_delta_sign=
+ ((int)new_header.offset_size + new_fmt->fixed_hdr_entry) -
+ ((int)header.offset_size + fmt->fixed_hdr_entry);
+ data_delta_sign= 0;
+ // plan[add_column_count] contains last deltas.
+ for (i= 0; i <= add_column_count && !copy; i++)
+ {
+ /* This is the check for increasing/decreasing */
+ DELTA_CHECK(header_delta_sign, plan[i].hdelta, copy);
+ DELTA_CHECK(data_delta_sign, plan[i].ddelta, copy);
+ }
+ calc_param(&new_header.entry_size, &new_header.header_size,
+ new_fmt->fixed_hdr_entry,
+ new_header.offset_size, new_header.column_count);
+
+ /*
+ Need copy because:
+ 1, Header/data parts moved in different directions.
+ 2. There is no enough allocated space in the string.
+ 3. Header and data moved in different directions.
+ */
+ if (copy || /*1.*/
+ str->max_length < str->length + header_delta + data_delta || /*2.*/
+ ((header_delta_sign < 0 && data_delta_sign > 0) ||
+ (header_delta_sign > 0 && data_delta_sign < 0))) /*3.*/
+ rc= dynamic_column_update_copy(str, plan, add_column_count,
+ &header, &new_header,
+ convert);
+ else
+ if (header_delta_sign < 0)
+ rc= dynamic_column_update_move_left(str, plan, header.offset_size,
+ header.entry_size,
+ header.header_size,
+ new_header.offset_size,
+ new_header.entry_size,
+ new_header.header_size,
+ header.column_count,
+ new_header.column_count,
+ add_column_count, header.dtpool,
+ header.data_size);
+ else
+ /*
+ rc= dynamic_column_update_move_right(str, plan, offset_size,
+ entry_size, header_size,
+ new_header.offset_size,
+ new_header.entry_size,
+ new_heder.header_size, column_count,
+ new_header.column_count,
+ add_column_count, header_end,
+ header.data_size);
+ */
+ rc= dynamic_column_update_copy(str, plan, add_column_count,
+ &header, &new_header,
+ convert);
+end:
+ free(alloc_plan);
+ return rc;
+
+create_new_string:
+ /* There is no columns from before, so let's just add the new ones */
+ rc= ER_DYNCOL_OK;
+ if (not_null != 0)
+ rc= dynamic_column_create_many_internal_fmt(str, add_column_count,
+ (uint*)column_keys, values,
+ str->str == NULL,
+ string_keys);
+ goto end;
+}
+
+
+/**
+ Update the packed string with the given column
+
+ @param str String where to write the data
+ @param column_number Array of columns number
+ @param values Array of columns values
+
+ @return ER_DYNCOL_* return code
+*/
+
+
+int dynamic_column_update(DYNAMIC_COLUMN *str, uint column_nr,
+ DYNAMIC_COLUMN_VALUE *value)
+{
+ return dynamic_column_update_many(str, 1, &column_nr, value);
+}
+
+
+enum enum_dyncol_func_result
+mariadb_dyncol_check(DYNAMIC_COLUMN *str)
+{
+ struct st_service_funcs *fmt;
+ enum enum_dyncol_func_result rc= ER_DYNCOL_FORMAT;
+ DYN_HEADER header;
+ uint i;
+ size_t data_offset= 0, name_offset= 0;
+ size_t prev_data_offset= 0, prev_name_offset= 0;
+ LEX_STRING name= {0,0}, prev_name= {0,0};
+ uint num= 0, prev_num= 0;
+ void *key, *prev_key;
+ enum enum_dynamic_column_type type= DYN_COL_NULL, prev_type= DYN_COL_NULL;
+
+ if (str->length == 0)
+ {
+ return(ER_DYNCOL_OK);
+ }
+
+ memset(&header, 0, sizeof(header));
+
+ /* Check that header is OK */
+ if (read_fixed_header(&header, str))
+ {
+ goto end;
+ }
+ fmt= fmt_data + header.format;
+ calc_param(&header.entry_size, &header.header_size,
+ fmt->fixed_hdr_entry, header.offset_size,
+ header.column_count);
+ /* headers are out of string length (no space for data and part of headers) */
+ if (fmt->fixed_hdr + header.header_size + header.nmpool_size > str->length)
+ {
+ goto end;
+ }
+ header.header= (uchar*)str->str + fmt->fixed_hdr;
+ header.nmpool= header.header + header.header_size;
+ header.dtpool= header.nmpool + header.nmpool_size;
+ header.data_size= str->length - fmt->fixed_hdr -
+ header.header_size - header.nmpool_size;
+
+ /* read and check headers */
+ if (header.format == dyncol_fmt_num)
+ {
+ key= &num;
+ prev_key= &prev_num;
+ }
+ else
+ {
+ key= &name;
+ prev_key= &prev_name;
+ }
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+
+ if (header.format == dyncol_fmt_num)
+ {
+ num= uint2korr(header.entry);
+ }
+ else
+ {
+ DBUG_ASSERT(header.format == dyncol_fmt_str);
+ if (read_name(&header, header.entry, &name))
+ {
+ goto end;
+ }
+ name_offset= name.str - (char *)header.nmpool;
+ }
+ if ((*fmt->type_and_offset_read)(&type, &data_offset,
+ header.entry + fmt->fixed_hdr_entry,
+ header.offset_size))
+ goto end;
+
+ DBUG_ASSERT(type != DYN_COL_NULL);
+ if (data_offset > header.data_size)
+ {
+ goto end;
+ }
+ if (prev_type != DYN_COL_NULL)
+ {
+ /* It is not first entry */
+ if (prev_data_offset >= data_offset)
+ {
+ goto end;
+ }
+ if (prev_name_offset > name_offset)
+ {
+ goto end;
+ }
+ if ((*fmt->column_sort)(&prev_key, &key) >= 0)
+ {
+ goto end;
+ }
+ }
+ prev_num= num;
+ prev_name= name;
+ prev_data_offset= data_offset;
+ prev_name_offset= name_offset;
+ prev_type= type;
+ }
+
+ /* check data, which we can */
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ DYNAMIC_COLUMN_VALUE store;
+ /* already checked by previous pass */
+ (*fmt->type_and_offset_read)(&header.type, &header.offset,
+ header.entry + fmt->fixed_hdr_entry,
+ header.offset_size);
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ switch ((header.type)) {
+ case DYN_COL_INT:
+ rc= dynamic_column_sint_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_UINT:
+ rc= dynamic_column_uint_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_DOUBLE:
+ rc= dynamic_column_double_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_STRING:
+ rc= dynamic_column_string_read(&store, header.data, header.length);
+ break;
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ rc= dynamic_column_decimal_read(&store, header.data, header.length);
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ rc= dynamic_column_date_time_read(&store, header.data,
+ header.length);
+ break;
+ case DYN_COL_DATE:
+ rc= dynamic_column_date_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_TIME:
+ rc= dynamic_column_time_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_DYNCOL:
+ rc= dynamic_column_dyncol_read(&store, header.data, header.length);
+ break;
+ case DYN_COL_NULL:
+ default:
+ rc= ER_DYNCOL_FORMAT;
+ goto end;
+ }
+ if (rc != ER_DYNCOL_OK)
+ {
+ DBUG_ASSERT(rc < 0);
+ goto end;
+ }
+ }
+
+ rc= ER_DYNCOL_OK;
+end:
+ return(rc);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val,
+ MARIADB_CHARSET_INFO *cs, char quote)
+{
+ char buff[40];
+ size_t len;
+ switch (val->type) {
+ case DYN_COL_INT:
+ len= snprintf(buff, sizeof(buff), "%lld", val->x.long_value);
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ case DYN_COL_UINT:
+ len= snprintf(buff, sizeof(buff), "%llu", val->x.ulong_value);
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ case DYN_COL_DOUBLE:
+ len= snprintf(buff, sizeof(buff), "%g", val->x.double_value);
+ if (ma_dynstr_realloc(str, len + (quote ? 2 : 0)))
+ return ER_DYNCOL_RESOURCE;
+ if (quote)
+ str->str[str->length++]= quote;
+ ma_dynstr_append_mem(str, buff, len);
+ if (quote)
+ str->str[str->length++]= quote;
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_STRING:
+ {
+ char *alloc= NULL;
+ char *from= val->x.string.value.str;
+ ulong bufflen;
+ my_bool conv= ((val->x.string.charset == cs) ||
+ !strcmp(val->x.string.charset->name, cs->name));
+ my_bool rc;
+ len= val->x.string.value.length;
+ bufflen= (ulong)(len * (conv ? cs->char_maxlen : 1));
+ if (ma_dynstr_realloc(str, bufflen))
+ return ER_DYNCOL_RESOURCE;
+
+ // guaranty UTF-8 string for value
+ if (!conv)
+ {
+#ifndef LIBMARIADB
+ uint dumma_errors;
+#else
+ int dumma_errors;
+#endif
+ if (!quote)
+ {
+ /* convert to the destination */
+ str->length+=
+#ifndef LIBMARIADB
+ copy_and_convert_extended(str->str, bufflen,
+ cs,
+ from, (uint32)len,
+ val->x.string.charset,
+ &dumma_errors);
+#else
+ mariadb_convert_string(from, &len, val->x.string.charset,
+ str->str, (size_t *)&bufflen, cs, &dumma_errors);
+#endif
+ return ER_DYNCOL_OK;
+ }
+ if ((alloc= (char *)malloc(bufflen)))
+ {
+ len=
+#ifndef LIBMARIADB
+ copy_and_convert_extended(alloc, bufflen, cs,
+ from, (uint32)len,
+ val->x.string.charset,
+ &dumma_errors);
+#else
+ mariadb_convert_string(from, &len, val->x.string.charset,
+ alloc, (size_t *)&bufflen, cs, &dumma_errors);
+#endif
+ from= alloc;
+ }
+ else
+ return ER_DYNCOL_RESOURCE;
+ }
+ if (quote)
+ rc= ma_dynstr_append_quoted(str, from, len, quote);
+ else
+ rc= ma_dynstr_append_mem(str, from, len);
+ if (alloc)
+ free(alloc);
+ if (rc)
+ return ER_DYNCOL_RESOURCE;
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ {
+ int len= sizeof(buff);
+ decimal2string(&val->x.decimal.value, buff, &len,
+ 0, val->x.decimal.value.frac,
+ '0');
+ if (ma_dynstr_append_mem(str, buff, len))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ }
+#endif
+ case DYN_COL_DATETIME:
+ case DYN_COL_DATE:
+ case DYN_COL_TIME:
+#ifndef LIBMARIADB
+ len= my_TIME_to_str(&val->x.time_value, buff, AUTO_SEC_PART_DIGITS);
+#else
+ len= mariadb_time_to_string(&val->x.time_value, buff, 39, AUTO_SEC_PART_DIGITS);
+#endif
+ if (ma_dynstr_realloc(str, len + (quote ? 2 : 0)))
+ return ER_DYNCOL_RESOURCE;
+ if (quote)
+ str->str[str->length++]= '"';
+ ma_dynstr_append_mem(str, buff, len);
+ if (quote)
+ str->str[str->length++]= '"';
+ break;
+ case DYN_COL_NULL:
+ if (ma_dynstr_append_mem(str, "null", 4))
+ return ER_DYNCOL_RESOURCE;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(ER_DYNCOL_OK);
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_long(longlong *ll, DYNAMIC_COLUMN_VALUE *val)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
+ *ll= 0;
+ switch (val->type) {
+ case DYN_COL_INT:
+ *ll= val->x.long_value;
+ break;
+ case DYN_COL_UINT:
+ *ll= (longlong)val->x.ulong_value;
+ if (*ll > (longlong)ULONGLONG_MAX)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_DOUBLE:
+ *ll= (longlong)val->x.double_value;
+ if (((double) *ll) != val->x.double_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_STRING:
+ {
+ char *src= val->x.string.value.str;
+ size_t len= val->x.string.value.length;
+ longlong i= 0, sign= 1;
+
+ while (len && isspace(*src)) src++,len--;
+
+ if (len)
+ {
+ if (*src == '-')
+ {
+ sign= -1;
+ src++;
+ }
+ while(len && isdigit(*src))
+ {
+ i= i * 10 + (*src - '0');
+ src++;
+ }
+ }
+ else
+ rc= ER_DYNCOL_TRUNCATED;
+ if (len)
+ rc= ER_DYNCOL_TRUNCATED;
+ *ll= i * sign;
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ if (decimal2longlong(&val->x.decimal.value, ll) != E_DEC_OK)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ *ll= (val->x.time_value.year * 10000000000ull +
+ val->x.time_value.month * 100000000L +
+ val->x.time_value.day * 1000000 +
+ val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DATE:
+ *ll= (val->x.time_value.year * 10000 +
+ val->x.time_value.month * 100 +
+ val->x.time_value.day) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_TIME:
+ *ll= (val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(rc);
+}
+
+
+enum enum_dyncol_func_result
+mariadb_dyncol_val_double(double *dbl, DYNAMIC_COLUMN_VALUE *val)
+{
+ enum enum_dyncol_func_result rc= ER_DYNCOL_OK;
+ *dbl= 0;
+ switch (val->type) {
+ case DYN_COL_INT:
+ *dbl= (double)val->x.long_value;
+ if (((longlong) *dbl) != val->x.long_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_UINT:
+ *dbl= (double)val->x.ulong_value;
+ if (((ulonglong) *dbl) != val->x.ulong_value)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ case DYN_COL_DOUBLE:
+ *dbl= val->x.double_value;
+ break;
+ case DYN_COL_STRING:
+ {
+ char *str, *end;
+ if (!(str= malloc(val->x.string.value.length + 1)))
+ return ER_DYNCOL_RESOURCE;
+ memcpy(str, val->x.string.value.str, val->x.string.value.length);
+ str[val->x.string.value.length]= '\0';
+ *dbl= strtod(str, &end);
+ if (*end != '\0')
+ rc= ER_DYNCOL_TRUNCATED;
+ free(str);
+ break;
+ }
+#ifndef LIBMARIADB
+ case DYN_COL_DECIMAL:
+ if (decimal2double(&val->x.decimal.value, dbl) != E_DEC_OK)
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+#endif
+ case DYN_COL_DATETIME:
+ *dbl= (double)(val->x.time_value.year * 10000000000ull +
+ val->x.time_value.month * 100000000L +
+ val->x.time_value.day * 1000000 +
+ val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DATE:
+ *dbl= (double)(val->x.time_value.year * 10000 +
+ val->x.time_value.month * 100 +
+ val->x.time_value.day) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_TIME:
+ *dbl= (double)(val->x.time_value.hour * 10000 +
+ val->x.time_value.minute * 100 +
+ val->x.time_value.second) *
+ (val->x.time_value.neg ? -1 : 1);
+ break;
+ case DYN_COL_DYNCOL:
+ case DYN_COL_NULL:
+ rc= ER_DYNCOL_TRUNCATED;
+ break;
+ default:
+ return(ER_DYNCOL_FORMAT);
+ }
+ return(rc);
+}
+
+
+/**
+ Convert to JSON
+
+ @param str The packed string
+ @param json Where to put json result
+
+ @return ER_DYNCOL_* return code
+*/
+
+#define JSON_STACK_PROTECTION 10
+
+static enum enum_dyncol_func_result
+mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json,
+ uint lvl)
+{
+ DYN_HEADER header;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ if (lvl >= JSON_STACK_PROTECTION)
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ goto err;
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+
+ rc= ER_DYNCOL_RESOURCE;
+
+ if (ma_dynstr_append_mem(json, "{", 1))
+ goto err;
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ DYNAMIC_COLUMN_VALUE val;
+ if (i != 0 && ma_dynstr_append_mem(json, ",", 1))
+ goto err;
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ /*
+ Check that the found data is within the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (header.length == DYNCOL_OFFSET_ERROR ||
+ header.length > INT_MAX || header.offset > header.data_size)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if ((rc= dynamic_column_get_value(&header, &val)) < 0)
+ goto err;
+ if (header.format == dyncol_fmt_num)
+ {
+ uint nm= uint2korr(header.entry);
+ if (ma_dynstr_realloc(json, DYNCOL_NUM_CHAR + 3))
+ goto err;
+ json->str[json->length++]= '"';
+ json->length+= snprintf(json->str + json->length,
+ DYNCOL_NUM_CHAR, "%u", nm);
+ }
+ else
+ {
+ LEX_STRING name;
+ if (read_name(&header, header.entry, &name))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if (ma_dynstr_realloc(json, name.length + 3))
+ goto err;
+ json->str[json->length++]= '"';
+ memcpy(json->str + json->length, name.str, name.length);
+ json->length+= name.length;
+ }
+ json->str[json->length++]= '"';
+ json->str[json->length++]= ':';
+ if (val.type == DYN_COL_DYNCOL)
+ {
+ /* here we use it only for read so can cheat a bit */
+ DYNAMIC_COLUMN dc;
+ memset(&dc, 0, sizeof(dc));
+ dc.str= val.x.string.value.str;
+ dc.length= val.x.string.value.length;
+ if (mariadb_dyncol_json_internal(&dc, json, lvl + 1) < 0)
+ {
+ dc.str= NULL; dc.length= 0;
+ goto err;
+ }
+ dc.str= NULL; dc.length= 0;
+ }
+ else
+ {
+ if ((rc= mariadb_dyncol_val_str(json, &val,
+ ma_charset_utf8_general_ci, '"')) < 0)
+ goto err;
+ }
+ }
+ if (ma_dynstr_append_mem(json, "}", 1))
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+ return ER_DYNCOL_OK;
+
+err:
+ json->length= 0;
+ return rc;
+}
+
+enum enum_dyncol_func_result
+mariadb_dyncol_json(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json)
+{
+
+ if (ma_init_dynamic_string(json, NULL, str->length * 2, 100))
+ return ER_DYNCOL_RESOURCE;
+
+ return mariadb_dyncol_json_internal(str, json, 1);
+}
+
+/**
+ Convert to DYNAMIC_COLUMN_VALUE values and names (LEX_STING) dynamic array
+
+ @param str The packed string
+ @param count number of elements in the arrays
+ @param names Where to put names (should be free by user)
+ @param vals Where to put values (should be free by user)
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_unpack(DYNAMIC_COLUMN *str,
+ uint *count,
+ LEX_STRING **names, DYNAMIC_COLUMN_VALUE **vals)
+{
+ DYN_HEADER header;
+ char *nm;
+ uint i;
+ enum enum_dyncol_func_result rc;
+
+ *count= 0; *names= 0; *vals= 0;
+
+ if (str->length == 0)
+ return ER_DYNCOL_OK; /* no columns */
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+
+
+ if (header.entry_size * header.column_count + FIXED_HEADER_SIZE >
+ str->length)
+ return ER_DYNCOL_FORMAT;
+
+ *vals= (DYNAMIC_COLUMN_VALUE *)malloc(sizeof(DYNAMIC_COLUMN_VALUE)* header.column_count);
+ if (header.format == dyncol_fmt_num)
+ {
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count +
+ DYNCOL_NUM_CHAR * header.column_count);
+ nm= (char *)((*names) + header.column_count);
+ }
+ else
+ {
+ *names= (LEX_STRING *)malloc(sizeof(LEX_STRING) * header.column_count);
+ nm= 0;
+ }
+ if (!(*vals) || !(*names))
+ {
+ rc= ER_DYNCOL_RESOURCE;
+ goto err;
+ }
+
+ for (i= 0, header.entry= header.header;
+ i < header.column_count;
+ i++, header.entry+= header.entry_size)
+ {
+ header.length=
+ hdr_interval_length(&header, header.entry + header.entry_size);
+ header.data= header.dtpool + header.offset;
+ /*
+ Check that the found data is within the ranges. This can happen if
+ we get data with wrong offsets.
+ */
+ if (header.length == DYNCOL_OFFSET_ERROR ||
+ header.length > INT_MAX || header.offset > header.data_size)
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ if ((rc= dynamic_column_get_value(&header, (*vals) + i)) < 0)
+ goto err;
+
+ if (header.format == dyncol_fmt_num)
+ {
+ uint num= uint2korr(header.entry);
+ (*names)[i].str= nm;
+ (*names)[i].length= snprintf(nm, DYNCOL_NUM_CHAR, "%u", num);
+ nm+= (*names)[i].length + 1;
+ }
+ else
+ {
+ if (read_name(&header, header.entry, (*names) + i))
+ {
+ rc= ER_DYNCOL_FORMAT;
+ goto err;
+ }
+ }
+ }
+
+ *count= header.column_count;
+ return ER_DYNCOL_OK;
+
+err:
+ if (*vals)
+ {
+ free(*vals);
+ *vals= 0;
+ }
+ if (*names)
+ {
+ free(*names);
+ *names= 0;
+ }
+ return rc;
+}
+
+
+/**
+ Get not NULL column count
+
+ @param str The packed string
+ @param column_count Where to put column count
+
+ @return ER_DYNCOL_* return code
+*/
+
+enum enum_dyncol_func_result
+mariadb_dyncol_column_count(DYNAMIC_COLUMN *str, uint *column_count)
+{
+ DYN_HEADER header;
+ enum enum_dyncol_func_result rc;
+
+ (*column_count)= 0;
+ if (str->length == 0)
+ return ER_DYNCOL_OK;
+
+ if ((rc= init_read_hdr(&header, str)) < 0)
+ return rc;
+ *column_count= header.column_count;
+ return rc;
+}
+
+/**
+ Release dynamic column memory
+
+ @param str dynamic column
+ @return void
+*/
+void mariadb_dyncol_free(DYNAMIC_COLUMN *str)
+{
+ ma_dynstr_free(str);
+}
diff --git a/libmariadb/libmariadb/mariadb_lib.c b/libmariadb/libmariadb/mariadb_lib.c
new file mode 100644
index 00000000..e8db51a0
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_lib.c
@@ -0,0 +1,4979 @@
+/************************************************************************************
+ Copyright (C) 2000, 2012 MySQL AB & MySQL Finland AB & TCX DataKonsult AB,
+ Monty Program AB
+ 2013, 2022 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 <http://www.gnu.org/licenses>
+ 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
+*************************************************************************************/
+
+#include <ma_global.h>
+
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include <ma_common.h>
+#include "ma_priv.h"
+#include "ma_context.h"
+#include "mysql.h"
+#include "mariadb_version.h"
+#include "ma_server_error.h"
+#include <mariadb/ma_io.h>
+#include "errmsg.h"
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <mariadb_dyncol.h>
+#include <mariadb_rpl.h>
+
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+# include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+#include <ma_sha1.h>
+#ifndef _WIN32
+#include <poll.h>
+#endif
+#include <ma_pvio.h>
+#ifdef HAVE_TLS
+#include <ma_tls.h>
+#endif
+#include <mysql/client_plugin.h>
+#ifdef _WIN32
+#include "shlwapi.h"
+#include "ws2tcpip.h"
+#define strncasecmp _strnicmp
+#endif
+
+#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15)
+#define MA_RPL_VERSION_HACK "5.5.5-"
+
+#define CHARSET_NAME_LEN 64
+
+#undef max_allowed_packet
+#undef net_buffer_length
+extern ulong max_allowed_packet; /* net.c */
+extern ulong net_buffer_length; /* net.c */
+
+static MYSQL_PARAMETERS mariadb_internal_parameters= {&max_allowed_packet, &net_buffer_length, 0};
+static my_bool mysql_client_init=0;
+static void mysql_close_options(MYSQL *mysql);
+static void ma_clear_session_state(MYSQL *mysql);
+static int parse_connection_string(MYSQL *mysql, const char *unused, const char *conn_str, ssize_t len);
+extern void release_configuration_dirs();
+extern char **get_default_configuration_dirs();
+extern my_bool ma_init_done;
+extern my_bool mysql_ps_subsystem_initialized;
+extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename, my_bool can_local_infile);
+extern const MARIADB_CHARSET_INFO * mysql_find_charset_nr(uint charsetnr);
+extern const MARIADB_CHARSET_INFO * mysql_find_charset_name(const char * const name);
+extern my_bool set_default_charset_by_name(const char *cs_name, myf flags __attribute__((unused)));
+extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+ const char *data_plugin, const char *db);
+extern int net_add_multi_command(NET *net, uchar command, const uchar *packet,
+ size_t length);
+extern unsigned char* ma_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *request_len, my_bool internal);
+
+extern LIST *pvio_callback;
+
+/* prepare statement methods from my_stmt.c */
+extern my_bool mthd_supported_buffer_type(enum enum_field_types type);
+extern my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt);
+extern my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt);
+extern my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt);
+extern int mthd_stmt_read_execute_response(MYSQL_STMT *stmt);
+extern int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row);
+extern int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row);
+extern int mthd_stmt_read_all_rows(MYSQL_STMT *stmt);
+extern void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt);
+extern my_bool _mariadb_read_options(MYSQL *mysql, const char *dir, const char *config_file, const char *group, unsigned int recursion);
+extern unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong length);
+
+extern void
+my_context_install_suspend_resume_hook(struct mysql_async_context *b,
+ void (*hook)(my_bool, void *),
+ void *user_data);
+
+uint mysql_port=0;
+my_string mysql_unix_port=0;
+
+#define CONNECT_TIMEOUT 0
+
+struct st_mariadb_methods MARIADB_DEFAULT_METHODS;
+
+#if defined(_WIN32)
+// socket_errno is defined in ma_global.h for all platforms
+#define perror(A)
+#else
+#include <errno.h>
+#define SOCKET_ERROR -1
+#endif /* _WIN32 */
+
+#include <mysql/client_plugin.h>
+
+#define IS_CONNHDLR_ACTIVE(mysql)\
+ ((mysql)->extension && (mysql)->extension->conn_hdlr)
+
+static void end_server(MYSQL *mysql);
+static void mysql_close_memory(MYSQL *mysql);
+void read_user_name(char *name);
+my_bool STDCALL mariadb_reconnect(MYSQL *mysql);
+static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length);
+
+extern int mysql_client_plugin_init();
+extern void mysql_client_plugin_deinit();
+
+/* net_get_error */
+void net_get_error(char *buf, size_t buf_len,
+ char *error, size_t error_len,
+ unsigned int *error_no,
+ char *sqlstate)
+{
+ char *p= buf;
+ size_t error_msg_len= 0;
+
+ if (buf_len > 2)
+ {
+ *error_no= uint2korr(p);
+ p+= 2;
+
+ /* since 4.1 sqlstate is following */
+ if (*p == '#')
+ {
+ memcpy(sqlstate, ++p, SQLSTATE_LENGTH);
+ p+= SQLSTATE_LENGTH;
+ }
+ error_msg_len= buf_len - (p - buf);
+ error_msg_len= MIN(error_msg_len, error_len - 1);
+ memcpy(error, p, error_msg_len);
+ }
+ else
+ {
+ *error_no= CR_UNKNOWN_ERROR;
+ memcpy(sqlstate, SQLSTATE_UNKNOWN, SQLSTATE_LENGTH);
+ }
+}
+
+/*****************************************************************************
+** read a packet from server. Give error message if socket was down
+** or packet is an error message
+*****************************************************************************/
+
+ulong
+ma_net_safe_read(MYSQL *mysql)
+{
+ NET *net= &mysql->net;
+ ulong len=0;
+
+restart:
+ if (net->pvio != 0)
+ len=ma_net_read(net);
+
+ if (len == packet_error || len == 0)
+ {
+ end_server(mysql);
+#ifdef HAVE_TLS
+ /* don't overwrite possible tls protocol errors */
+ if (net->last_errno != CR_SSL_CONNECTION_ERROR)
+#endif
+ {
+ my_set_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ?
+ CR_NET_PACKET_TOO_LARGE:
+ CR_SERVER_LOST,
+ SQLSTATE_UNKNOWN, 0, errno);
+ }
+ return(packet_error);
+ }
+ if (net->read_pos[0] == 255)
+ {
+ if (len > 3)
+ {
+ char *pos=(char*) net->read_pos+1;
+ uint last_errno=uint2korr(pos);
+ pos+=2;
+ len-=2;
+
+ if (last_errno== 65535 &&
+ ((mariadb_connection(mysql) && (mysql->server_capabilities & CLIENT_PROGRESS)) ||
+ (!(mysql->extension->mariadb_server_capabilities & MARIADB_CLIENT_PROGRESS << 32))))
+ {
+ if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1)))
+ {
+ /* Wrong packet */
+ my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
+ return (packet_error);
+ }
+ goto restart;
+ }
+ net->last_errno= last_errno;
+ if (pos[0]== '#')
+ {
+ ma_strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH);
+ pos+= SQLSTATE_LENGTH + 1;
+ }
+ else
+ {
+ strncpy(net->sqlstate, SQLSTATE_UNKNOWN, SQLSTATE_LENGTH);
+ }
+ ma_strmake(net->last_error,(char*) pos,
+ min(len,sizeof(net->last_error)-1));
+ }
+ else
+ {
+ my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
+ }
+
+ mysql->server_status&= ~SERVER_MORE_RESULTS_EXIST;
+
+ return(packet_error);
+ }
+ return len;
+}
+
+/*
+ Report progress to the client
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length)
+{
+ uint stage, max_stage, proc_length;
+ double progress;
+ uchar *start= packet;
+
+ if (length < 5)
+ return 1; /* Wrong packet */
+
+ if (!(mysql->options.extension && mysql->options.extension->report_progress))
+ return 0; /* No callback, ignore packet */
+
+ packet++; /* Ignore number of strings */
+ stage= (uint) *packet++;
+ max_stage= (uint) *packet++;
+ progress= uint3korr(packet)/1000.0;
+ packet+= 3;
+ proc_length= net_field_length(&packet);
+ if (packet + proc_length > start + length)
+ return 1; /* Wrong packet */
+ (*mysql->options.extension->report_progress)(mysql, stage, max_stage,
+ progress, (char*) packet,
+ proc_length);
+ return 0;
+}
+
+/* Get the length of next field. Change parameter to point at fieldstart */
+ulong
+net_field_length(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (ulong) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (ulong) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (ulong) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+ return (ulong) uint4korr(pos+1);
+}
+
+/* Same as above, but returns ulonglong values */
+
+static unsigned long long
+net_field_length_ll(uchar **packet)
+{
+ reg1 uchar *pos= *packet;
+ if (*pos < 251)
+ {
+ (*packet)++;
+ return (unsigned long long) *pos;
+ }
+ if (*pos == 251)
+ {
+ (*packet)++;
+ return (unsigned long long) NULL_LENGTH;
+ }
+ if (*pos == 252)
+ {
+ (*packet)+=3;
+ return (unsigned long long) uint2korr(pos+1);
+ }
+ if (*pos == 253)
+ {
+ (*packet)+=4;
+ return (unsigned long long) uint3korr(pos+1);
+ }
+ (*packet)+=9; /* Must be 254 when here */
+#ifdef NO_CLIENT_LONGLONG
+ return (unsigned long long) uint4korr(pos+1);
+#else
+ return (unsigned long long) uint8korr(pos+1);
+#endif
+}
+
+
+void free_rows(MYSQL_DATA *cur)
+{
+ if (cur)
+ {
+ ma_free_root(&cur->alloc,MYF(0));
+ free(cur);
+ }
+}
+
+int
+mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ size_t length, my_bool skip_check, void *opt_arg)
+{
+ NET *net= &mysql->net;
+ int result= -1;
+ if (mysql->net.pvio == 0)
+ {
+ /* Do reconnect if possible */
+ if (mariadb_reconnect(mysql))
+ return(1);
+ }
+ if (mysql->status != MYSQL_STATUS_READY ||
+ mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ result= mysql->extension->conn_hdlr->plugin->set_connection(mysql, command, arg, length, skip_check, opt_arg);
+ if (result== -1)
+ return(result);
+ }
+
+ CLEAR_CLIENT_ERROR(mysql);
+
+ if (command == COM_QUERY ||
+ command == COM_STMT_PREPARE)
+ {
+ if ((mysql->options.client_flag & CLIENT_LOCAL_FILES) &&
+ mysql->options.extension && mysql->extension->auto_local_infile == WAIT_FOR_QUERY &&
+ arg && (*arg == 'l' || *arg == 'L'))
+ {
+ if (strncasecmp(arg, "load", 4) == 0)
+ mysql->extension->auto_local_infile= ACCEPT_FILE_REQUEST;
+ }
+ }
+
+ mysql->info=0;
+ mysql->affected_rows= ~(unsigned long long) 0;
+ ma_net_clear(net); /* Clear receive buffer */
+ if (!arg)
+ arg="";
+
+ if (net->extension->multi_status== COM_MULTI_ENABLED)
+ {
+ return net_add_multi_command(net, command, (const uchar *)arg, length);
+ }
+
+ if (ma_net_write_command(net,(uchar) command,arg,
+ length ? length : (ulong) strlen(arg), 0))
+ {
+ if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
+ {
+ my_set_error(mysql, CR_NET_PACKET_TOO_LARGE, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ end_server(mysql);
+ if (mariadb_reconnect(mysql))
+ goto end;
+ if (ma_net_write_command(net,(uchar) command,arg,
+ length ? length : (ulong) strlen(arg), 0))
+ {
+ my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ result=0;
+
+ if (net->extension->multi_status > COM_MULTI_OFF)
+ skip_check= 1;
+
+ if (!skip_check)
+ {
+ result= ((mysql->packet_length=ma_net_safe_read(mysql)) == packet_error ?
+ 1 : 0);
+ }
+ end:
+ return(result);
+}
+
+int
+ma_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ size_t length, my_bool skip_check, void *opt_arg)
+{
+ return mysql->methods->db_command(mysql, command, arg, length, skip_check, opt_arg);
+}
+
+int ma_multi_command(MYSQL *mysql, enum enum_multi_status status)
+{
+ NET *net= &mysql->net;
+
+ switch (status) {
+ case COM_MULTI_OFF:
+ ma_net_clear(net);
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_ENABLED:
+ if (net->extension->multi_status > COM_MULTI_DISABLED)
+ return 1;
+ ma_net_clear(net);
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_DISABLED:
+ /* Opposite to COM_MULTI_OFF we don't clear net buffer,
+ next command or com_nulti_end will flush entire buffer */
+ net->extension->multi_status= status;
+ return 0;
+ case COM_MULTI_END:
+ {
+ size_t len= net->write_pos - net->buff - NET_HEADER_SIZE;
+
+ if (len < NET_HEADER_SIZE) /* don't send empty request */
+ {
+ ma_net_clear(net);
+ return 1;
+ }
+ net->extension->multi_status= COM_MULTI_OFF;
+ return ma_net_flush(net);
+ }
+ case COM_MULTI_CANCEL:
+ ma_net_clear(net);
+ net->extension->multi_status= COM_MULTI_OFF;
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static void free_old_query(MYSQL *mysql)
+{
+ if (mysql->fields)
+ ma_free_root(&mysql->field_alloc,MYF(0));
+ ma_init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */
+ mysql->fields=0;
+ mysql->field_count=0; /* For API */
+ mysql->info= 0;
+ return;
+}
+
+#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL)
+struct passwd *getpwuid(uid_t);
+char* getlogin(void);
+#endif
+
+#if !defined(_WIN32)
+void read_user_name(char *name)
+{
+ if (geteuid() == 0)
+ strcpy(name,"root"); /* allow use of surun */
+ else
+ {
+#ifdef HAVE_GETPWUID
+ struct passwd *skr;
+ const char *str;
+ if ((skr=getpwuid(geteuid())) != NULL)
+ {
+ str=skr->pw_name;
+ } else if ((str=getlogin()) == NULL)
+ {
+ if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) &&
+ !(str=getenv("LOGIN")))
+ str="UNKNOWN_USER";
+ }
+ ma_strmake(name,str,USERNAME_LENGTH);
+#elif defined(HAVE_CUSERID)
+ (void) cuserid(name);
+#else
+ ma_strmake(name,"UNKNOWN_USER", USERNAME_LENGTH);
+#endif
+ }
+ return;
+}
+
+#else /* WIN32 */
+
+void read_user_name(char *name)
+{
+ char *str=getenv("USERNAME"); /* ODBC will send user variable */
+ ma_strmake(name,str ? str : "ODBC", USERNAME_LENGTH);
+}
+
+#endif
+
+
+/**************************************************************************
+** Shut down connection
+**************************************************************************/
+
+static void
+end_server(MYSQL *mysql)
+{
+ /* if net->error 2 and reconnect is activated, we need to inform
+ connection handler */
+ if (mysql->net.pvio != 0)
+ {
+ ma_pvio_close(mysql->net.pvio);
+ mysql->net.pvio= 0; /* Marker */
+ }
+ ma_net_end(&mysql->net);
+ free_old_query(mysql);
+ return;
+}
+
+void mthd_my_skip_result(MYSQL *mysql)
+{
+ ulong pkt_len;
+
+ do {
+ pkt_len= ma_net_safe_read(mysql);
+ if (pkt_len == packet_error)
+ break;
+ } while (pkt_len > 8 || mysql->net.read_pos[0] != 254);
+ return;
+}
+
+void STDCALL
+mysql_free_result(MYSQL_RES *result)
+{
+ if (result)
+ {
+ if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT)
+ {
+ result->handle->methods->db_skip_result(result->handle);
+ result->handle->status=MYSQL_STATUS_READY;
+ }
+ free_rows(result->data);
+ if (result->fields)
+ ma_free_root(&result->field_alloc,MYF(0));
+ if (result->row)
+ free(result->row);
+ free(result);
+ }
+ return;
+}
+
+
+/****************************************************************************
+** Get options from my.cnf
+****************************************************************************/
+enum enum_option_type {
+ MARIADB_OPTION_NONE,
+ MARIADB_OPTION_BOOL,
+ MARIADB_OPTION_INT,
+ MARIADB_OPTION_SIZET,
+ MARIADB_OPTION_STR,
+ MARIADB_OPTION_FUNC,
+};
+
+struct st_default_options {
+ union {
+ enum mysql_option option;
+ int (*option_func)(MYSQL *mysql, const char *key, const char *value, ssize_t len);
+ } u;
+ enum enum_option_type type;
+ const char *conf_key;
+};
+
+struct st_default_options mariadb_defaults[] =
+{
+ {{MARIADB_OPT_PORT}, MARIADB_OPTION_INT,"port"},
+ {{MARIADB_OPT_UNIXSOCKET}, MARIADB_OPTION_STR, "socket"},
+ {{MYSQL_OPT_COMPRESS}, MARIADB_OPTION_BOOL, "compress"},
+ {{MARIADB_OPT_PASSWORD}, MARIADB_OPTION_STR, "password"},
+ {{MYSQL_OPT_NAMED_PIPE}, MARIADB_OPTION_BOOL, "pipe"},
+ {{MYSQL_OPT_CONNECT_TIMEOUT}, MARIADB_OPTION_INT, "timeout"},
+ {{MARIADB_OPT_USER}, MARIADB_OPTION_STR, "user"},
+ {{MYSQL_INIT_COMMAND}, MARIADB_OPTION_STR, "init-command"},
+ {{MARIADB_OPT_HOST}, MARIADB_OPTION_STR, "host"},
+ {{MARIADB_OPT_SCHEMA}, MARIADB_OPTION_STR, "database"},
+ {{MARIADB_OPT_DEBUG}, MARIADB_OPTION_STR, "debug"},
+ {{MARIADB_OPT_FOUND_ROWS}, MARIADB_OPTION_NONE, "return-found-rows"},
+ {{MYSQL_OPT_SSL_KEY}, MARIADB_OPTION_STR, "ssl-key"},
+ {{MYSQL_OPT_SSL_CERT}, MARIADB_OPTION_STR,"ssl-cert"},
+ {{MYSQL_OPT_SSL_CA}, MARIADB_OPTION_STR,"ssl-ca"},
+ {{MYSQL_OPT_SSL_CAPATH}, MARIADB_OPTION_STR,"ssl-capath"},
+ {{MYSQL_OPT_SSL_CRL}, MARIADB_OPTION_STR,"ssl-crl"},
+ {{MYSQL_OPT_SSL_CRLPATH}, MARIADB_OPTION_STR,"ssl-crlpath"},
+ {{MYSQL_OPT_SSL_VERIFY_SERVER_CERT}, MARIADB_OPTION_BOOL,"ssl-verify-server-cert"},
+ {{MYSQL_SET_CHARSET_DIR}, MARIADB_OPTION_STR, "character-sets-dir"},
+ {{MYSQL_SET_CHARSET_NAME}, MARIADB_OPTION_STR, "default-character-set"},
+ {{MARIADB_OPT_INTERACTIVE}, MARIADB_OPTION_NONE, "interactive-timeout"},
+ {{MYSQL_OPT_CONNECT_TIMEOUT}, MARIADB_OPTION_INT, "connect-timeout"},
+ {{MYSQL_OPT_LOCAL_INFILE}, MARIADB_OPTION_BOOL, "local-infile"},
+ {{0}, 0 ,"disable-local-infile",},
+ {{MYSQL_OPT_SSL_CIPHER}, MARIADB_OPTION_STR, "ssl-cipher"},
+ {{MYSQL_OPT_MAX_ALLOWED_PACKET}, MARIADB_OPTION_SIZET, "max-allowed-packet"},
+ {{MYSQL_OPT_NET_BUFFER_LENGTH}, MARIADB_OPTION_SIZET, "net-buffer-length"},
+ {{MYSQL_OPT_PROTOCOL}, MARIADB_OPTION_INT, "protocol"},
+ {{MYSQL_SHARED_MEMORY_BASE_NAME}, MARIADB_OPTION_STR,"shared-memory-base-name"},
+ {{MARIADB_OPT_MULTI_RESULTS}, MARIADB_OPTION_BOOL, "multi-results"},
+ {{MARIADB_OPT_MULTI_STATEMENTS}, MARIADB_OPTION_BOOL, "multi-statements"},
+ {{MARIADB_OPT_MULTI_STATEMENTS}, MARIADB_OPTION_BOOL, "multi-queries"},
+ {{MYSQL_SECURE_AUTH}, MARIADB_OPTION_BOOL, "secure-auth"},
+ {{MYSQL_REPORT_DATA_TRUNCATION}, MARIADB_OPTION_BOOL, "report-data-truncation"},
+ {{MYSQL_OPT_RECONNECT}, MARIADB_OPTION_BOOL, "reconnect"},
+ {{MYSQL_PLUGIN_DIR}, MARIADB_OPTION_STR, "plugin-dir"},
+ {{MYSQL_DEFAULT_AUTH}, MARIADB_OPTION_STR, "default-auth"},
+ {{MARIADB_OPT_SSL_FP}, MARIADB_OPTION_STR, "ssl-fp"},
+ {{MARIADB_OPT_SSL_FP_LIST}, MARIADB_OPTION_STR, "ssl-fp-list"},
+ {{MARIADB_OPT_SSL_FP_LIST}, MARIADB_OPTION_STR, "ssl-fplist"},
+ {{MARIADB_OPT_TLS_PASSPHRASE}, MARIADB_OPTION_STR, "ssl-passphrase"},
+ {{MARIADB_OPT_TLS_VERSION}, MARIADB_OPTION_STR, "tls-version"},
+ {{MYSQL_SERVER_PUBLIC_KEY}, MARIADB_OPTION_STR, "server-public-key"},
+ {{MYSQL_OPT_BIND}, MARIADB_OPTION_STR, "bind-address"},
+ {{MYSQL_OPT_SSL_ENFORCE}, MARIADB_OPTION_BOOL, "ssl-enforce"},
+ {{MARIADB_OPT_RESTRICTED_AUTH}, MARIADB_OPTION_STR, "restricted-auth"},
+ {{.option_func=parse_connection_string}, MARIADB_OPTION_FUNC, "connection"},
+ /* Aliases */
+ {{MARIADB_OPT_SCHEMA}, MARIADB_OPTION_STR, "db"},
+ {{MARIADB_OPT_UNIXSOCKET}, MARIADB_OPTION_STR, "unix_socket"},
+ {{MARIADB_OPT_HOST}, MARIADB_OPTION_STR, "servername"},
+ {{MARIADB_OPT_PASSWORD}, MARIADB_OPTION_STR, "passwd"},
+ {{MARIADB_OPT_SSL_FP}, MARIADB_OPTION_STR, "tls-fp"},
+ {{MARIADB_OPT_SSL_FP_LIST}, MARIADB_OPTION_STR, "tls-fplist"},
+ {{MYSQL_OPT_SSL_KEY}, MARIADB_OPTION_STR, "tls-key"},
+ {{MYSQL_OPT_SSL_CERT}, MARIADB_OPTION_STR,"tls-cert"},
+ {{MYSQL_OPT_SSL_CA}, MARIADB_OPTION_STR,"tls-ca"},
+ {{MYSQL_OPT_SSL_CAPATH}, MARIADB_OPTION_STR,"tls-capath"},
+ {{MYSQL_OPT_SSL_CRL}, MARIADB_OPTION_STR,"tls-crl"},
+ {{MYSQL_OPT_SSL_CRLPATH}, MARIADB_OPTION_STR,"tls-crlpath"},
+ {{MYSQL_OPT_SSL_CIPHER}, MARIADB_OPTION_STR, "tls-cipher"},
+ {{MARIADB_OPT_TLS_PASSPHRASE}, MARIADB_OPTION_STR, "tls-passphrase"},
+ {{MYSQL_OPT_SSL_ENFORCE}, MARIADB_OPTION_BOOL, "tls-enforce"},
+ {{MYSQL_OPT_SSL_VERIFY_SERVER_CERT}, MARIADB_OPTION_BOOL,"tls-verify-peer"},
+ {{0}, 0, NULL}
+};
+
+#define CHECK_OPT_EXTENSION_SET(OPTS)\
+ if (!(OPTS)->extension) \
+ (OPTS)->extension= (struct st_mysql_options_extension *) \
+ calloc(1, sizeof(struct st_mysql_options_extension));
+
+#define OPT_SET_EXTENDED_VALUE_BIN(OPTS, KEY, KEY_LEN, VAL, LEN)\
+ CHECK_OPT_EXTENSION_SET(OPTS) \
+ free((gptr)(OPTS)->extension->KEY); \
+ if((VAL) && (LEN)) { \
+ if (((OPTS)->extension->KEY= malloc((LEN)))) { \
+ memcpy((OPTS)->extension->KEY, (VAL), (LEN)); \
+ (OPTS)->extension->KEY_LEN= (LEN); \
+ } \
+ } \
+ else \
+ (OPTS)->extension->KEY= NULL
+
+#define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \
+ CHECK_OPT_EXTENSION_SET(OPTS) \
+ free((gptr)(OPTS)->extension->KEY); \
+ if((VAL)) \
+ (OPTS)->extension->KEY= strdup((char *)(VAL)); \
+ else \
+ (OPTS)->extension->KEY= NULL
+
+#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL) \
+ CHECK_OPT_EXTENSION_SET(OPTS) \
+ (OPTS)->extension->KEY= (VAL)
+
+#define OPT_SET_EXTENDED_VALUE_INT(A,B,C) OPT_SET_EXTENDED_VALUE(A,B,C)
+
+#define OPT_SET_VALUE_STR(OPTS, KEY, VAL) \
+ free((OPTS)->KEY); \
+ if((VAL)) \
+ (OPTS)->KEY= strdup((char *)(VAL)); \
+ else \
+ (OPTS)->KEY= NULL
+
+#define OPT_SET_VALUE_INT(OPTS, KEY, VAL) \
+ (OPTS)->KEY= (VAL)
+
+static void options_add_initcommand(struct st_mysql_options *options,
+ const char *init_cmd)
+{
+ char *insert= strdup(init_cmd);
+ if (!options->init_command)
+ {
+ options->init_command= (DYNAMIC_ARRAY*)malloc(sizeof(DYNAMIC_ARRAY));
+ ma_init_dynamic_array(options->init_command, sizeof(char*), 5, 5);
+ }
+
+ if (ma_insert_dynamic(options->init_command, (gptr)&insert))
+ free(insert);
+}
+my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value)
+{
+ if (config_option)
+ {
+ int i;
+ char *c;
+
+ /* CONC-395: replace underscore "_" by dash "-" */
+ while ((c= strchr(config_option, '_')))
+ *c= '-';
+
+ for (i=0; mariadb_defaults[i].conf_key; i++)
+ {
+ if (!strcmp(mariadb_defaults[i].conf_key, config_option))
+ {
+ my_bool val_bool;
+ int val_int;
+ size_t val_sizet;
+ int rc;
+ void *option_val= NULL;
+ switch (mariadb_defaults[i].type) {
+ case MARIADB_OPTION_FUNC:
+ return mariadb_defaults[i].u.option_func(mysql, config_option, config_value, -1);
+ break;
+ case MARIADB_OPTION_BOOL:
+ val_bool= 0;
+ if (config_value)
+ val_bool= atoi(config_value);
+ option_val= &val_bool;
+ break;
+ case MARIADB_OPTION_INT:
+ val_int= 0;
+ if (config_value)
+ val_int= atoi(config_value);
+ option_val= &val_int;
+ break;
+ case MARIADB_OPTION_SIZET:
+ val_sizet= 0;
+ if (config_value)
+ val_sizet= strtol(config_value, NULL, 10);
+ option_val= &val_sizet;
+ break;
+ case MARIADB_OPTION_STR:
+ option_val= (void*)config_value;
+ break;
+ case MARIADB_OPTION_NONE:
+ break;
+ }
+ rc= mysql_optionsv(mysql, mariadb_defaults[i].u.option, option_val);
+ return(test(rc));
+ }
+ }
+ }
+ /* unknown key */
+ return 1;
+}
+
+/**
+ * @brief: simple connection string parser
+ *
+ * key/value pairs (or key only) are separated by semicolons.
+ * If a semicolon is part of a value, it must be enclosed in
+ * curly braces.
+ *
+ * Unknown keys will be ignored.
+ */
+static int parse_connection_string(MYSQL *mysql, const char *unused __attribute__((unused)),
+ const char *conn_str, ssize_t len)
+{
+ char *conn_save,
+ *end, *pos,
+ *key= NULL, *val= NULL;
+ my_bool in_curly_brace= 0;
+
+ if (len == -1)
+ len= strlen(conn_str);
+
+ /* don't modify original dsn */
+ conn_save= (char *)malloc(len + 1);
+ memcpy(conn_save, conn_str, len);
+ conn_save[len]= 0;
+
+ /* start and end */
+ pos= conn_save;
+ end= conn_save + len;
+
+ while (pos <= end)
+ {
+ /* ignore white space, unless it is between curly braces */
+ if (isspace(*pos) && !in_curly_brace)
+ {
+ pos++;
+ continue;
+ }
+
+ switch (*pos) {
+ case '{':
+ if (!key)
+ goto error;
+ if (!in_curly_brace)
+ {
+ in_curly_brace= 1;
+ if (pos < end)
+ {
+ pos++;
+ val= pos;
+ continue;
+ }
+ }
+ break;
+ case '}':
+ if (in_curly_brace)
+ {
+ if (!key)
+ goto error;
+ if (pos < end && *(pos + 1) == '}')
+ {
+ memmove(pos, pos + 1, end - pos - 1);
+ end--;
+ pos += 2;
+ continue;
+ }
+ if (in_curly_brace)
+ in_curly_brace= 0;
+ else
+ goto error;
+ *pos++= 0;
+ continue;
+ }
+ break;
+ case '=':
+ if (in_curly_brace)
+ {
+ pos++;
+ continue;
+ }
+ if (!key)
+ goto error;
+ *pos++= 0;
+ if (pos < end)
+ val= pos;
+ continue;
+ break;
+ case ';':
+ if (in_curly_brace)
+ {
+ pos++;
+ continue;
+ }
+ if (!key)
+ goto error;
+ *pos++= 0;
+ if (key && strcasecmp(key, "connection") != 0)
+ _mariadb_set_conf_option(mysql, key, val);
+ key= val= NULL;
+ continue;
+ break;
+ }
+ if (!key && *pos)
+ key= pos;
+ pos++;
+ }
+ if (key && strcasecmp(key, "connection") != 0)
+ _mariadb_set_conf_option(mysql, key, val);
+ free(conn_save);
+ return 0;
+
+error:
+ my_set_error(mysql, CR_CONNSTR_PARSE_ERROR, SQLSTATE_UNKNOWN, 0, pos - conn_save);
+ free(conn_save);
+ return 1;
+}
+
+
+static MARIADB_CONST_STRING null_const_string= {0,0};
+
+/***************************************************************************
+** Allocate a string copy on memroot
+***************************************************************************/
+static MARIADB_CONST_STRING ma_const_string_copy_root(MA_MEM_ROOT *memroot,
+ const char *str,
+ size_t length)
+{
+ MARIADB_CONST_STRING res;
+ if (!str || !(res.str= ma_memdup_root(memroot, str, length)))
+ return null_const_string;
+ res.length= length;
+ return res;
+}
+
+
+/***************************************************************************
+** Allocate and initialize MA_FIELD_EXTENSION
+***************************************************************************/
+MA_FIELD_EXTENSION *new_ma_field_extension(MA_MEM_ROOT *memroot)
+{
+ MA_FIELD_EXTENSION *ext= ma_alloc_root(memroot, sizeof(MA_FIELD_EXTENSION));
+ if (ext)
+ memset((void *) ext, 0, sizeof(*ext));
+ return ext;
+}
+
+
+/***************************************************************************
+** Populate field extension from a type info packet
+***************************************************************************/
+
+static void ma_field_extension_init_type_info(MA_MEM_ROOT *memroot,
+ MA_FIELD_EXTENSION *ext,
+ const char *ptr, size_t length)
+{
+ const char *end= ptr + length;
+ for ( ; ptr < end; )
+ {
+ uint itype= (uchar) *ptr++;
+ uint len= (uchar) *ptr++;
+ if (ptr + len > end || len > 127)
+ break; /*Badly encoded data*/
+ if (itype <= 127 && itype <= MARIADB_FIELD_ATTR_LAST)
+ ext->metadata[itype]= ma_const_string_copy_root(memroot, ptr, len);
+ ptr+= len;
+ }
+}
+
+
+/***************************************************************************
+** Allocate a field extension deep copy
+***************************************************************************/
+
+MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot,
+ const MA_FIELD_EXTENSION *from)
+{
+ MA_FIELD_EXTENSION *ext= new_ma_field_extension(memroot);
+ uint i;
+ if (!ext)
+ return NULL;
+ for (i= 0; i < MARIADB_FIELD_ATTR_LAST; i++)
+ {
+ if (from->metadata[i].str)
+ ext->metadata[i]= ma_const_string_copy_root(memroot,
+ from->metadata[i].str,
+ from->metadata[i].length);
+ }
+ return ext;
+}
+
+/***************************************************************************
+** Change field rows to field structs
+***************************************************************************/
+
+static size_t rset_field_offsets[]= {
+ OFFSET(MYSQL_FIELD, catalog),
+ OFFSET(MYSQL_FIELD, catalog_length),
+ OFFSET(MYSQL_FIELD, db),
+ OFFSET(MYSQL_FIELD, db_length),
+ OFFSET(MYSQL_FIELD, table),
+ OFFSET(MYSQL_FIELD, table_length),
+ OFFSET(MYSQL_FIELD, org_table),
+ OFFSET(MYSQL_FIELD, org_table_length),
+ OFFSET(MYSQL_FIELD, name),
+ OFFSET(MYSQL_FIELD, name_length),
+ OFFSET(MYSQL_FIELD, org_name),
+ OFFSET(MYSQL_FIELD, org_name_length)
+};
+
+MYSQL_FIELD *
+unpack_fields(const MYSQL *mysql,
+ MYSQL_DATA *data, MA_MEM_ROOT *alloc, uint fields,
+ my_bool default_value)
+{
+ MYSQL_ROWS *row;
+ MYSQL_FIELD *field,*result;
+ char *p;
+ unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2;
+
+ field=result=(MYSQL_FIELD*) ma_alloc_root(alloc,sizeof(MYSQL_FIELD)*fields);
+ if (!result)
+ return(0);
+
+ for (row=data->data; row ; row = row->next,field++)
+ {
+ if (field >= result + fields)
+ goto error;
+
+ for (i=0; i < field_count; i++)
+ {
+ uint length= (uint)(row->data[i+1] - row->data[i] - 1);
+ if (!row->data[i] || row->data[i][length])
+ goto error;
+
+ *(char **)(((char *)field) + rset_field_offsets[i*2])=
+ ma_strdup_root(alloc, (char *)row->data[i]);
+ *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= length;
+ }
+
+ field->extension= NULL;
+ if (ma_has_extended_type_info(mysql))
+ {
+ if (row->data[i+1] - row->data[i] > 1)
+ {
+ size_t len= row->data[i+1] - row->data[i] - 1;
+ MA_FIELD_EXTENSION *ext= new_ma_field_extension(alloc);
+ if ((field->extension= ext))
+ ma_field_extension_init_type_info(alloc, ext, row->data[i], len);
+ }
+ i++;
+ }
+
+ p= (char *)row->data[i];
+ /* filler */
+ field->charsetnr= uint2korr(p);
+ p+= 2;
+ field->length= (uint) uint4korr(p);
+ p+= 4;
+ field->type= (enum enum_field_types)uint1korr(p);
+ p++;
+ field->flags= uint2korr(p);
+ p+= 2;
+ field->decimals= (uint) p[0];
+ p++;
+
+ /* filler */
+ p+= 2;
+
+ if (INTERNAL_NUM_FIELD(field))
+ field->flags|= NUM_FLAG;
+
+ i++;
+ /* This is used by deprecated function mysql_list_fields only,
+ however the reported length is not correct, so we always zero it */
+ if (default_value && row->data[i])
+ field->def=ma_strdup_root(alloc,(char*) row->data[i]);
+ else
+ field->def=0;
+ field->def_length= 0;
+
+ field->max_length= 0;
+ }
+ if (field < result + fields)
+ goto error;
+ free_rows(data); /* Free old data */
+ return(result);
+error:
+ free_rows(data);
+ ma_free_root(alloc, MYF(0));
+ return(0);
+}
+
+
+/* Read all rows (fields or data) from server */
+
+MYSQL_DATA *mthd_my_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ uint fields)
+{
+ uint field;
+ ulong pkt_len;
+ ulong len;
+ uchar *cp;
+ char *to, *end_to;
+ MYSQL_DATA *result;
+ MYSQL_ROWS **prev_ptr,*cur;
+ NET *net = &mysql->net;
+
+ if ((pkt_len= ma_net_safe_read(mysql)) == packet_error)
+ return(0);
+ if (!(result=(MYSQL_DATA*) calloc(1, sizeof(MYSQL_DATA))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ ma_init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */
+ result->alloc.min_malloc=sizeof(MYSQL_ROWS);
+ prev_ptr= &result->data;
+ result->rows=0;
+ result->fields=fields;
+
+ while (*(cp=net->read_pos) != 254 || pkt_len >= 8)
+ {
+ result->rows++;
+ if (!(cur= (MYSQL_ROWS*) ma_alloc_root(&result->alloc,
+ sizeof(MYSQL_ROWS))) ||
+ !(cur->data= ((MYSQL_ROW)
+ ma_alloc_root(&result->alloc,
+ (fields+1)*sizeof(char *)+fields+pkt_len))))
+ {
+ free_rows(result);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ *prev_ptr=cur;
+ prev_ptr= &cur->next;
+ to= (char*) (cur->data+fields+1);
+ end_to=to+fields+pkt_len-1;
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH)
+ { /* null field */
+ cur->data[field] = 0;
+ }
+ else
+ {
+ cur->data[field] = to;
+ if (len > (ulong)(end_to - to) || to > end_to)
+ {
+ free_rows(result);
+ SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ memcpy(to,(char*) cp,len); to[len]=0;
+ to+=len+1;
+ cp+=len;
+ if (mysql_fields)
+ {
+ if (mysql_fields[field].max_length < len)
+ mysql_fields[field].max_length=len;
+ }
+ }
+ }
+ cur->data[field]=to; /* End of last field */
+ if ((pkt_len=ma_net_safe_read(mysql)) == packet_error)
+ {
+ free_rows(result);
+ return(0);
+ }
+ }
+ *prev_ptr=0; /* last pointer is null */
+ /* save status */
+ if (pkt_len > 1)
+ {
+ unsigned int last_status= mysql->server_status;
+ cp++;
+ mysql->warning_count= uint2korr(cp);
+ cp+= 2;
+ mysql->server_status= uint2korr(cp);
+ ma_status_callback(mysql, last_status)
+ }
+ return(result);
+}
+
+
+/*
+** Read one row. Uses packet buffer as storage for fields.
+** When next packet is read, the previous field values are destroyed
+*/
+
+
+int mthd_my_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
+{
+ uint field;
+ ulong pkt_len,len;
+ uchar *pos,*prev_pos, *end_pos;
+
+ if ((pkt_len=(uint) ma_net_safe_read(mysql)) == packet_error)
+ return -1;
+
+ if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
+ {
+ unsigned int last_status= mysql->server_status;
+ mysql->warning_count= uint2korr(mysql->net.read_pos + 1);
+ mysql->server_status= uint2korr(mysql->net.read_pos + 3);
+ ma_status_callback(mysql, last_status);
+ return 1; /* End of data */
+ }
+ prev_pos= 0; /* allowed to write at packet[-1] */
+ pos=mysql->net.read_pos;
+ end_pos=pos+pkt_len;
+ for (field=0 ; field < fields ; field++)
+ {
+ if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH)
+ { /* null field */
+ row[field] = 0;
+ *lengths++=0;
+ }
+ else
+ {
+ if (len > (ulong) (end_pos - pos) || pos > end_pos)
+ {
+ mysql->net.last_errno=CR_UNKNOWN_ERROR;
+ strncpy(mysql->net.last_error,ER(mysql->net.last_errno),
+ MYSQL_ERRMSG_SIZE - 1);
+ return -1;
+ }
+ row[field] = (char*) pos;
+ pos+=len;
+ *lengths++=len;
+ }
+ if (prev_pos)
+ *prev_pos=0; /* Terminate prev field */
+ prev_pos=pos;
+ }
+ row[field]=(char*) prev_pos+1; /* End of last field */
+ *prev_pos=0; /* Terminate last field */
+ return 0;
+}
+
+/****************************************************************************
+** Init MySQL structure or allocate one
+****************************************************************************/
+
+MYSQL * STDCALL
+mysql_init(MYSQL *mysql)
+{
+ if (mysql_server_init(0, NULL, NULL))
+ return NULL;
+ if (!mysql)
+ {
+ if (!(mysql=(MYSQL*) calloc(1, sizeof(MYSQL))))
+ return 0;
+ mysql->free_me=1;
+ mysql->net.pvio= 0;
+ mysql->net.extension= 0;
+ }
+ else
+ {
+ memset((char*) (mysql), 0, sizeof(*(mysql)));
+ mysql->net.pvio= 0;
+ mysql->free_me= 0;
+ mysql->net.extension= 0;
+ }
+
+ if (!(mysql->net.extension= (struct st_mariadb_net_extension *)
+ calloc(1, sizeof(struct st_mariadb_net_extension))) ||
+ !(mysql->extension= (struct st_mariadb_extension *)
+ calloc(1, sizeof(struct st_mariadb_extension))))
+ goto error;
+ mysql->options.report_data_truncation= 1;
+ mysql->options.connect_timeout=CONNECT_TIMEOUT;
+ mysql->charset= mysql_find_charset_name(MARIADB_DEFAULT_CHARSET);
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+ strcpy(mysql->net.sqlstate, "00000");
+ mysql->net.last_error[0]= mysql->net.last_errno= mysql->net.extension->extended_errno= 0;
+
+ if (ENABLED_LOCAL_INFILE != LOCAL_INFILE_MODE_OFF)
+ mysql->options.client_flag|= CLIENT_LOCAL_FILES;
+ mysql->extension->auto_local_infile= ENABLED_LOCAL_INFILE == LOCAL_INFILE_MODE_AUTO
+ ? WAIT_FOR_QUERY : ALWAYS_ACCEPT;
+ mysql->options.reconnect= 0;
+ return mysql;
+error:
+ if (mysql->free_me)
+ free(mysql);
+ return 0;
+}
+
+int STDCALL
+mysql_ssl_set(MYSQL *mysql __attribute__((unused)),
+ const char *key __attribute__((unused)),
+ const char *cert __attribute__((unused)),
+ const char *ca __attribute__((unused)),
+ const char *capath __attribute__((unused)),
+ const char *cipher __attribute__((unused)))
+{
+#ifdef HAVE_TLS
+ char enable= 1;
+ return (mysql_optionsv(mysql, MYSQL_OPT_SSL_ENFORCE, &enable) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_KEY, key) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CERT, cert) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CA, ca) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CAPATH, capath) |
+ mysql_optionsv(mysql, MYSQL_OPT_SSL_CIPHER, cipher)) ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+/**************************************************************************
+**************************************************************************/
+
+const char * STDCALL
+mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused)))
+{
+#ifdef HAVE_TLS
+ if (mysql->net.pvio && mysql->net.pvio->ctls)
+ {
+ return ma_pvio_tls_cipher(mysql->net.pvio->ctls);
+ }
+#endif
+ return(NULL);
+}
+
+/**************************************************************************
+** Free strings in the SSL structure and clear 'use_ssl' flag.
+** NB! Errors are not reported until you do mysql_real_connect.
+**************************************************************************/
+
+char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer)
+{
+ if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS)
+ {
+ buffer= (unsigned char *)mysql_net_store_length((unsigned char *)buffer, (mysql->options.extension) ?
+ mysql->options.extension->connect_attrs_len : 0);
+ if (mysql->options.extension &&
+ ma_hashtbl_inited(&mysql->options.extension->connect_attrs))
+ {
+ uint i;
+ for (i=0; i < mysql->options.extension->connect_attrs.records; i++)
+ {
+ size_t len;
+ uchar *p= ma_hashtbl_element(&mysql->options.extension->connect_attrs, i);
+
+ len= strlen((char *)p);
+ buffer= mysql_net_store_length(buffer, len);
+ memcpy(buffer, p, len);
+ buffer+= (len);
+ p+= (len + 1);
+ len= strlen((char *)p);
+ buffer= mysql_net_store_length(buffer, len);
+ memcpy(buffer, p, len);
+ buffer+= len;
+ }
+ }
+ }
+ return (char *)buffer;
+}
+
+/** set some default attributes */
+static my_bool
+ma_set_connect_attrs(MYSQL *mysql, const char *host)
+{
+ char buffer[255];
+ int rc= 0;
+
+ rc= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name") +
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version") +
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os") +
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_server_host") +
+#ifdef _WIN32
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread") +
+#endif
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid") +
+ mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform");
+
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "libmariadb")
+ + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_version", MARIADB_PACKAGE_VERSION)
+ + mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_os", MARIADB_SYSTEM_TYPE);
+
+ if (host && *host)
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", host);
+
+#ifdef _WIN32
+ snprintf(buffer, 255, "%lu", (ulong) GetCurrentThreadId());
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buffer);
+ snprintf(buffer, 255, "%lu", (ulong) GetCurrentProcessId());
+#else
+ snprintf(buffer, 255, "%lu", (ulong) getpid());
+#endif
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buffer);
+
+ rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_platform", MARIADB_MACHINE_TYPE);
+ return(test(rc>0));
+}
+
+/*
+** Note that the mysql argument must be initialized with mysql_init()
+** before calling mysql_real_connect !
+*/
+
+MYSQL * STDCALL
+mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
+ const char *passwd, const char *db,
+ uint port, const char *unix_socket,unsigned long client_flag)
+{
+ char *end= NULL;
+ char *connection_handler= (mysql->options.extension) ?
+ mysql->options.extension->connection_handler : 0;
+
+ if (!mysql->methods)
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+
+ /* set default */
+ if (!mysql->options.extension || !mysql->options.extension->status_callback)
+ mysql_optionsv(mysql, MARIADB_OPT_STATUS_CALLBACK, NULL, NULL);
+
+ /* if host contains a semicolon, we need to parse connection string */
+ if (host && strchr(host, ';'))
+ {
+ if (parse_connection_string(mysql, NULL, host, strlen(host)))
+ return NULL;
+ /* if host was passed in connection string, it is now
+ stored in mysql->options.host */
+ host= NULL;
+ } else if (connection_handler ||
+ (host && (end= strstr(host, "://"))))
+ {
+ MARIADB_CONNECTION_PLUGIN *plugin;
+ char plugin_name[64];
+
+ if (!connection_handler || !connection_handler[0])
+ {
+ memset(plugin_name, 0, 64);
+ ma_strmake(plugin_name, host, MIN(end - host, 63));
+ end+= 3;
+ }
+ else
+ ma_strmake(plugin_name, connection_handler, MIN(63, strlen(connection_handler)));
+
+ if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN)))
+ return NULL;
+
+ if (!(mysql->extension->conn_hdlr= (MA_CONNECTION_HANDLER *)calloc(1, sizeof(MA_CONNECTION_HANDLER))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return NULL;
+ }
+
+ /* save URL for reconnect */
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, url, host);
+
+ mysql->extension->conn_hdlr->plugin= plugin;
+
+ if (plugin && plugin->connect)
+ {
+ MYSQL *my= plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag);
+ if (!my)
+ {
+ free(mysql->extension->conn_hdlr);
+ mysql->extension->conn_hdlr= NULL;
+ }
+ return my;
+ }
+ }
+#ifndef HAVE_SCHANNEL
+ return mysql->methods->db_connect(mysql, host, user, passwd,
+ db, port, unix_socket, client_flag);
+#else
+/*
+ With older windows versions (prior Win 10) TLS connections periodically
+ fail with SEC_E_INVALID_TOKEN, SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED
+ error (see MDEV-13492). If the connect attempt returns on of these error codes
+ in mysql->net.extended_errno we will try to connect again (max. 3 times)
+*/
+#define MAX_SCHANNEL_CONNECT_ATTEMPTS 3
+ {
+ int ssl_retry= (mysql->options.use_ssl) ? MAX_SCHANNEL_CONNECT_ATTEMPTS : 1;
+ MYSQL *my= NULL;
+ while (ssl_retry)
+ {
+ if ((my= mysql->methods->db_connect(mysql, host, user, passwd,
+ db, port, unix_socket, client_flag | CLIENT_REMEMBER_OPTIONS)))
+ return my;
+
+ switch (mysql->net.extension->extended_errno) {
+ case SEC_E_INVALID_TOKEN:
+ case SEC_E_BUFFER_TOO_SMALL:
+ case SEC_E_MESSAGE_ALTERED:
+ ssl_retry--;
+ break;
+ default:
+ ssl_retry= 0;
+ break;
+ }
+ }
+ if (!my && !(client_flag & CLIENT_REMEMBER_OPTIONS))
+ mysql_close_options(mysql);
+ return my;
+ }
+#endif
+}
+
+struct st_host {
+ char *host;
+ int port;
+};
+
+static void ma_get_host_list(char *host_list, struct st_host *host_info, int default_port)
+{
+ char *token, *start, *save;
+ int host_nr= 0;
+
+ start= host_list;
+ while ((token= strtok_r(start, ",", &save)))
+ {
+ char *p;
+
+ /* ipv6 hostname */
+ if ((p= strchr(token, ']')))
+ {
+ host_info[host_nr].host= token + 1;
+ *p++= 0;
+ token= p;
+ }
+ else
+ host_info[host_nr].host= token;
+ /* check if port was specified */
+ if ((p= strchr(token, ':')))
+ {
+ *p++= 0;
+ host_info[host_nr].port= atoi(p);
+ }
+ else
+ host_info[host_nr].port= default_port;
+ host_nr++;
+ start= NULL;
+ }
+ return;
+}
+
+MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
+ const char *passwd, const char *db,
+ uint port, const char *unix_socket, unsigned long client_flag)
+{
+ char buff[NAME_LEN+USERNAME_LENGTH+100];
+ char *end, *end_pkt, *host_info;
+ MA_PVIO_CINFO cinfo= {NULL, NULL, 0, -1, NULL};
+ MARIADB_PVIO *pvio= NULL;
+ char *scramble_data;
+ const char *scramble_plugin;
+ uint pkt_length, scramble_len, pkt_scramble_len= 0;
+ NET *net= &mysql->net;
+ my_bool is_multi= 0;
+ char *host_copy= NULL;
+ struct st_host *host_list= NULL;
+ int connect_attempts= 0;
+
+ if (!mysql->methods)
+ mysql->methods= &MARIADB_DEFAULT_METHODS;
+
+ if (net->pvio) /* check if we are already connected */
+ {
+ SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ /* use default options */
+ if (mysql->options.my_cnf_file || mysql->options.my_cnf_group)
+ {
+ _mariadb_read_options(mysql, NULL,
+ (mysql->options.my_cnf_file ?
+ mysql->options.my_cnf_file : NULL),
+ mysql->options.my_cnf_group, 0);
+ free(mysql->options.my_cnf_file);
+ free(mysql->options.my_cnf_group);
+ mysql->options.my_cnf_file=mysql->options.my_cnf_group=0;
+ }
+
+ if (!port)
+ port=mysql->options.port;
+ if (!host || !host[0])
+ host= mysql->options.host;
+
+ /* check if we have multi hosts */
+ if (host && host[0] && strchr(host, ','))
+ {
+ char *p;
+ size_t host_count= 1;
+
+ is_multi= 1;
+
+ /* don't change original entry, so let's make a copy */
+ if (!(host_copy= strdup(host)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+
+ p= host_copy;
+
+ while ((p = strchr(p, ',')))
+ {
+ host_count++;
+ p++;
+ }
+
+ if (!(host_list= (struct st_host *)calloc(host_count + 1, sizeof(struct st_host))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+
+ ma_get_host_list(host_copy, host_list, port);
+ }
+
+restart:
+ /* check if we reached end of list */
+ if (is_multi)
+ {
+ if (!host_list[connect_attempts].host)
+ goto error;
+
+ host= host_list[connect_attempts].host;
+ port= host_list[connect_attempts].port;
+ }
+
+ ma_set_connect_attrs(mysql, host);
+
+#ifndef WIN32
+ if (mysql->options.protocol > MYSQL_PROTOCOL_SOCKET)
+ {
+ SET_CLIENT_ERROR(mysql, CR_CONN_UNKNOWN_PROTOCOL, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+#endif
+
+ /* Some empty-string-tests are done because of ODBC */
+ if (!user || !user[0])
+ user=mysql->options.user;
+ if (!passwd)
+ {
+ passwd=mysql->options.password;
+#ifndef DONT_USE_MYSQL_PWD
+ if (!passwd)
+ passwd=getenv("MYSQL_PWD"); /* get it from environment (haneke) */
+ if (!passwd)
+ passwd= "";
+#endif
+ }
+ if (!db || !db[0])
+ db=mysql->options.db;
+ if (!unix_socket)
+ unix_socket=mysql->options.unix_socket;
+
+ mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
+
+ /* try to connect via pvio_init */
+ cinfo.host= host;
+ cinfo.unix_socket= unix_socket;
+ cinfo.port= port;
+ cinfo.mysql= mysql;
+
+ /*
+ ** Grab a socket and connect it to the server
+ */
+#ifndef _WIN32
+#if defined(HAVE_SYS_UN_H)
+ if ((!host || strcmp(host,LOCAL_HOST) == 0) &&
+ mysql->options.protocol != MYSQL_PROTOCOL_TCP &&
+ (unix_socket || mysql_unix_port))
+ {
+ cinfo.host= LOCAL_HOST;
+ cinfo.unix_socket= (unix_socket) ? unix_socket : mysql_unix_port;
+ cinfo.type= PVIO_TYPE_UNIXSOCKET;
+ sprintf(host_info=buff,ER(CR_LOCALHOST_CONNECTION),cinfo.host);
+ }
+ else
+#endif
+#else
+ if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY)
+ {
+ cinfo.host= mysql->options.shared_memory_base_name;
+ cinfo.type= PVIO_TYPE_SHAREDMEM;
+ sprintf(host_info=buff,ER(CR_SHARED_MEMORY_CONNECTION), cinfo.host ? cinfo.host : SHM_DEFAULT_NAME);
+ }
+ /* named pipe */
+ else if (mysql->options.protocol == MYSQL_PROTOCOL_PIPE ||
+ (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0))
+ {
+ cinfo.type= PVIO_TYPE_NAMEDPIPE;
+ sprintf(host_info=buff,ER(CR_NAMEDPIPE_CONNECTION),cinfo.host);
+ }
+ else
+#endif
+ {
+ cinfo.unix_socket=0; /* This is not used */
+ if (!port)
+ port=mysql_port;
+ if (!host)
+ host=LOCAL_HOST;
+ cinfo.host= host;
+ cinfo.port= port;
+ cinfo.type= PVIO_TYPE_SOCKET;
+ sprintf(host_info=buff,ER(CR_TCP_CONNECTION), cinfo.host);
+ }
+ /* Initialize and load pvio plugin */
+ if (!(pvio= ma_pvio_init(&cinfo)))
+ goto error;
+
+ /* try to connect */
+ if (ma_pvio_connect(pvio, &cinfo) != 0)
+ {
+ ma_pvio_close(pvio);
+ if (mysql->options.extension && mysql->options.extension->async_context &&
+ mysql->options.extension->async_context->pvio)
+ {
+ /* pvio delegated to mysql->net.pvio by ma_net_init().
+ * invalidate the pvio pointer in the async context */
+ mysql->options.extension->async_context->pvio = NULL;
+ }
+
+ if (is_multi)
+ {
+ connect_attempts++;
+ goto restart;
+ }
+ goto error;
+ }
+
+ if (mysql->options.extension && mysql->options.extension->proxy_header)
+ {
+ char *hdr = mysql->options.extension->proxy_header;
+ size_t len = mysql->options.extension->proxy_header_len;
+ if (ma_pvio_write(pvio, (unsigned char *)hdr, len) <= 0)
+ {
+ ma_pvio_close(pvio);
+ goto error;
+ }
+ }
+
+ if (ma_net_init(net, pvio))
+ {
+ ma_pvio_close(pvio);
+ goto error;
+ }
+
+ if (mysql->options.extension && mysql->options.extension->async_context && mysql->options.extension->async_context->pvio)
+ {
+ /* pvio delegated to mysql->net.pvio by ma_net_init().
+ * invalidate the pvio pointer in the async context */
+ mysql->options.extension->async_context->pvio = NULL;
+ }
+
+
+ if (mysql->options.max_allowed_packet)
+ net->max_packet_size= mysql->options.max_allowed_packet;
+
+ ma_pvio_keepalive(net->pvio);
+ strcpy(mysql->net.sqlstate, "00000");
+
+ /* Get version info */
+ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
+/*
+ if (ma_pvio_wait_io_or_timeout(net->pvio, FALSE, 0) < 1)
+ {
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "handshake: waiting for initial communication packet",
+ errno);
+ goto error;
+ }
+ */
+ if ((pkt_length=ma_net_safe_read(mysql)) == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "handshake: reading initial communication packet",
+ errno);
+
+ goto error;
+ }
+ end= (char *)net->read_pos;
+ end_pkt= (char *)net->read_pos + pkt_length;
+
+ /* Check if version of protocol matches current one */
+
+ mysql->protocol_version= end[0];
+ end++;
+
+ /* Check if server sends an error */
+ if (mysql->protocol_version == 0XFF)
+ {
+ net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error),
+ &net->last_errno, net->sqlstate);
+ /* fix for bug #26426 */
+ if (net->last_errno == 1040)
+ memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
+ goto error;
+ }
+
+ if (mysql->protocol_version < PROTOCOL_VERSION)
+ {
+ net->last_errno= CR_VERSION_ERROR;
+ sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version,
+ PROTOCOL_VERSION);
+ goto error;
+ }
+ /* Save connection information */
+ if (!user) user="";
+
+ if (!(mysql->host_info= strdup(host_info)) ||
+ !(mysql->host= strdup(cinfo.host ? cinfo.host : "")) ||
+ !(mysql->user=strdup(user)) ||
+ !(mysql->passwd=strdup(passwd)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ if (cinfo.unix_socket)
+ mysql->unix_socket= strdup(cinfo.unix_socket);
+ else
+ mysql->unix_socket=0;
+ mysql->port=port;
+ client_flag|=mysql->options.client_flag;
+
+ if (strncmp(end, MA_RPL_VERSION_HACK, sizeof(MA_RPL_VERSION_HACK) - 1) == 0)
+ {
+ mysql->server_version= strdup(end + sizeof(MA_RPL_VERSION_HACK) - 1);
+ }
+ else
+ {
+ if (!(mysql->server_version= strdup(end)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ }
+ end+= strlen(end) + 1;
+
+ mysql->thread_id=uint4korr(end);
+ end+=4;
+
+ /* This is the first part of scramble packet. In 4.1 and later
+ a second package will follow later */
+ scramble_data= end;
+ scramble_len= SCRAMBLE_LENGTH_323 + 1;
+ scramble_plugin= old_password_plugin_name;
+ end+= SCRAMBLE_LENGTH_323;
+
+ /* 1st pad */
+ end++;
+
+ if (end + 1<= end_pkt)
+ {
+ mysql->server_capabilities=uint2korr(end);
+ }
+
+ /* mysql 5.5 protocol */
+ if (end + 18 <= end_pkt)
+ {
+ mysql->server_language= uint1korr(end + 2);
+ mysql->server_status= uint2korr(end + 3);
+ mysql->server_capabilities|= (unsigned int)(uint2korr(end + 5)) << 16;
+ pkt_scramble_len= uint1korr(end + 7);
+
+ /* check if MariaD2B specific capabilities are available */
+ if (mariadb_connection(mysql) && !(mysql->server_capabilities & CLIENT_MYSQL))
+ {
+ mysql->extension->mariadb_server_capabilities= (ulonglong) uint4korr(end + 14);
+ }
+ }
+
+ /* pad 2 */
+ end+= 18;
+
+ /* second scramble package */
+ if (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 <= end_pkt)
+ {
+ memcpy(end - SCRAMBLE_LENGTH_323, scramble_data, SCRAMBLE_LENGTH_323);
+ scramble_data= end - SCRAMBLE_LENGTH_323;
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ scramble_len= pkt_scramble_len;
+ scramble_plugin= scramble_data + scramble_len;
+ if (scramble_data + scramble_len > end_pkt)
+ {
+ SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ } else
+ {
+ scramble_len= (uint)(end_pkt - scramble_data);
+ scramble_plugin= native_password_plugin_name;
+ }
+ } else
+ {
+ mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
+ if (mysql->options.secure_auth)
+ {
+ SET_CLIENT_ERROR(mysql, CR_SECURE_AUTH, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ }
+
+ /* Set character set */
+ if (mysql->options.charset_name)
+ mysql->charset= mysql_find_charset_name(mysql->options.charset_name);
+ else
+ mysql->charset=mysql_find_charset_name(MARIADB_DEFAULT_CHARSET);
+
+ if (!mysql->charset)
+ {
+ net->last_errno=CR_CANT_READ_CHARSET;
+ sprintf(net->last_error,ER(net->last_errno),
+ mysql->options.charset_name ? mysql->options.charset_name :
+ MARIADB_DEFAULT_CHARSET,
+ "compiled_in");
+ goto error;
+ }
+
+ mysql->client_flag= client_flag;
+
+ if (run_plugin_auth(mysql, scramble_data, scramble_len,
+ scramble_plugin, db))
+ goto error;
+
+ if (mysql->client_flag & CLIENT_COMPRESS ||
+ mysql->client_flag & CLIENT_ZSTD_COMPRESSION)
+ {
+ if (!compression_plugin(net) ||
+ (!(compression_ctx(net) = compression_plugin(net)->init_ctx(COMPRESSION_LEVEL_DEFAULT))))
+ {
+ int alg= (mysql->client_flag & CLIENT_ZSTD_COMPRESSION) ?
+ COMPRESSION_ZSTD : COMPRESSION_ZLIB;
+ compression_plugin(net)= NULL;
+ my_set_error(mysql, CR_ERR_LOAD_PLUGIN, SQLSTATE_UNKNOWN, NULL,
+ _mariadb_compression_algorithm_str(alg));
+ goto error;
+ }
+ net->compress= 1;
+ }
+
+ /* last part: select default db */
+ if (!(mysql->server_capabilities & CLIENT_CONNECT_WITH_DB) &&
+ (db && !mysql->db))
+ {
+ if (mysql_select_db(mysql, db))
+ {
+ my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "Setting intital database",
+ errno);
+ goto error;
+ }
+ }
+
+ if (mysql->options.init_command)
+ {
+ char **begin= (char **)mysql->options.init_command->buffer;
+ char **end= begin + mysql->options.init_command->elements;
+
+ /* Avoid reconnect in mysql_real_connect */
+ my_bool save_reconnect= mysql->options.reconnect;
+ mysql->options.reconnect= 0;
+
+ for (;begin < end; begin++)
+ {
+ if (mysql_real_query(mysql, *begin, (unsigned long)strlen(*begin)))
+ goto error;
+
+ /* check if query produced a result set */
+ do {
+ MYSQL_RES *res;
+ if ((res= mysql_use_result(mysql)))
+ mysql_free_result(res);
+ } while (!mysql_next_result(mysql));
+ }
+ mysql->options.reconnect= save_reconnect;
+ }
+
+ strcpy(mysql->net.sqlstate, "00000");
+
+ /* connection established, apply timeouts */
+ ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout);
+ ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout);
+
+ free(host_list);
+ free(host_copy);
+
+ return(mysql);
+
+error:
+ /* Free allocated memory */
+ free(host_list);
+ free(host_copy);
+ end_server(mysql);
+ /* only free the allocated memory, user needs to call mysql_close */
+ mysql_close_memory(mysql);
+ if (!(client_flag & CLIENT_REMEMBER_OPTIONS) &&
+ !(IS_MYSQL_ASYNC(mysql)))
+ mysql_close_options(mysql);
+ return(0);
+}
+
+struct my_hook_data {
+ MYSQL *orig_mysql;
+ MYSQL *new_mysql;
+ /* This is always NULL currently, but restoring does not hurt just in case. */
+ MARIADB_PVIO *orig_pvio;
+};
+/*
+ Callback hook to make the new VIO accessible via the old MYSQL to calling
+ application when suspending a non-blocking call during automatic reconnect.
+*/
+static void
+my_suspend_hook(my_bool suspend, void *data)
+{
+ struct my_hook_data *hook_data= (struct my_hook_data *)data;
+ if (suspend)
+ {
+ hook_data->orig_pvio= hook_data->orig_mysql->net.pvio;
+ hook_data->orig_mysql->net.pvio= hook_data->new_mysql->net.pvio;
+ }
+ else
+ hook_data->orig_mysql->net.pvio= hook_data->orig_pvio;
+}
+
+my_bool STDCALL mariadb_reconnect(MYSQL *mysql)
+{
+ MYSQL tmp_mysql;
+ struct my_hook_data hook_data;
+ struct mysql_async_context *ctxt= NULL;
+ LIST *li_stmt= mysql->stmts;
+
+ /* check if connection handler is active */
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reconnect)
+ return(mysql->extension->conn_hdlr->plugin->reconnect(mysql));
+ }
+
+ if (!mysql->options.reconnect ||
+ (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info)
+ {
+ /* Allow reconnect next time */
+ mysql->server_status&= ~SERVER_STATUS_IN_TRANS;
+ my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ mysql_init(&tmp_mysql);
+ tmp_mysql.free_me= 0;
+ tmp_mysql.options=mysql->options;
+ if (mysql->extension->conn_hdlr)
+ {
+ tmp_mysql.extension->conn_hdlr= mysql->extension->conn_hdlr;
+ mysql->extension->conn_hdlr= 0;
+ }
+
+ /* don't reread options from configuration files */
+ tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL;
+ if (IS_MYSQL_ASYNC_ACTIVE(mysql))
+ {
+ ctxt= mysql->options.extension->async_context;
+ hook_data.orig_mysql= mysql;
+ hook_data.new_mysql= &tmp_mysql;
+ hook_data.orig_pvio= mysql->net.pvio;
+ my_context_install_suspend_resume_hook(ctxt, my_suspend_hook, &hook_data);
+ }
+
+ if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd,
+ mysql->db, mysql->port, mysql->unix_socket,
+ mysql->client_flag | CLIENT_REMEMBER_OPTIONS) ||
+ mysql_set_character_set(&tmp_mysql, mysql->charset->csname))
+ {
+ if (ctxt)
+ my_context_install_suspend_resume_hook(ctxt, NULL, NULL);
+ /* don't free options (CONC-118) */
+ memset(&tmp_mysql.options, 0, sizeof(struct st_mysql_options));
+ my_set_error(mysql, tmp_mysql.net.last_errno,
+ tmp_mysql.net.sqlstate,
+ tmp_mysql.net.last_error);
+ mysql_close(&tmp_mysql);
+ return(1);
+ }
+
+ for (;li_stmt;li_stmt= li_stmt->next)
+ {
+ MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
+
+ if (stmt->state != MYSQL_STMT_INITTED)
+ {
+ stmt->state= MYSQL_STMT_INITTED;
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ }
+ }
+
+ tmp_mysql.free_me= mysql->free_me;
+ tmp_mysql.stmts= mysql->stmts;
+ mysql->stmts= NULL;
+
+ if (ctxt)
+ my_context_install_suspend_resume_hook(ctxt, NULL, NULL);
+ /* Don't free options, we moved them to tmp_mysql */
+ memset(&mysql->options, 0, sizeof(mysql->options));
+ mysql->free_me=0;
+ mysql_close(mysql);
+ *mysql=tmp_mysql;
+ mysql->net.pvio->mysql= mysql;
+#ifdef HAVE_TLS
+ /* CONC-604: Set new connection handle */
+ if (mysql_get_ssl_cipher(mysql))
+ ma_pvio_tls_set_connection(mysql);
+#endif
+ ma_net_clear(&mysql->net);
+ mysql->affected_rows= ~(unsigned long long) 0;
+ mysql->info= 0;
+ return(0);
+}
+
+void ma_invalidate_stmts(MYSQL *mysql, const char *function_name)
+{
+ if (mysql->stmts)
+ {
+ LIST *li_stmt= mysql->stmts;
+
+ for (; li_stmt; li_stmt= li_stmt->next)
+ {
+ MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data;
+ stmt->mysql= NULL;
+ stmt_set_error(stmt, CR_STMT_CLOSED, SQLSTATE_UNKNOWN, 0, function_name);
+ }
+ mysql->stmts= NULL;
+ }
+}
+
+/*
+ Legacy support of the MariaDB 5.5 version, where timeouts where only in
+ seconds resolution. Applications that use this will be asked to set a timeout
+ at the nearest higher whole-seconds value.
+*/
+unsigned int STDCALL
+mysql_get_timeout_value(const MYSQL *mysql)
+{
+ unsigned int timeout= 0;
+
+ if (mysql->options.extension && mysql->options.extension->async_context)
+ timeout= mysql->options.extension->async_context->timeout_value;
+ /* Avoid overflow. */
+ if (timeout > UINT_MAX - 999)
+ return (timeout - 1)/1000 + 1;
+ else
+ return (timeout+999)/1000;
+}
+
+
+unsigned int STDCALL
+mysql_get_timeout_value_ms(const MYSQL *mysql)
+{
+ if (mysql->options.extension && mysql->options.extension->async_context)
+ return mysql->options.extension->async_context->timeout_value;
+ return 0;
+}
+
+/**************************************************************************
+** Change user and database
+**************************************************************************/
+
+my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
+ const char *passwd, const char *db)
+{
+ const MARIADB_CHARSET_INFO *s_cs= mysql->charset;
+ char *s_user= mysql->user,
+ *s_passwd= mysql->passwd,
+ *s_db= mysql->db;
+ int rc;
+
+ if (mysql->options.charset_name)
+ mysql->charset= mysql_find_charset_name(mysql->options.charset_name);
+ else
+ mysql->charset=mysql_find_charset_name(MARIADB_DEFAULT_CHARSET);
+
+ mysql->user= strdup(user ? user : "");
+ mysql->passwd= strdup(passwd ? passwd : "");
+
+ /* db will be set in run_plugin_auth */
+ mysql->db= 0;
+ rc= run_plugin_auth(mysql, 0, 0, 0, db);
+
+ /* COM_CHANGE_USER always releases prepared statements, so we need to invalidate them */
+ ma_invalidate_stmts(mysql, "mysql_change_user()");
+
+ if (rc==0)
+ {
+ free(s_user);
+ free(s_passwd);
+ free(s_db);
+
+ if (!mysql->db && db && !(mysql->db= strdup(db)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ rc= 1;
+ }
+ } else
+ {
+ free(mysql->user);
+ free(mysql->passwd);
+ free(mysql->db);
+
+ mysql->user= s_user;
+ mysql->passwd= s_passwd;
+ mysql->db= s_db;
+ mysql->charset= s_cs;
+ }
+ return(rc);
+}
+
+
+/**************************************************************************
+** Set current database
+**************************************************************************/
+
+int STDCALL
+mysql_select_db(MYSQL *mysql, const char *db)
+{
+ int error;
+
+ if (!db)
+ return 1;
+
+ if ((error=ma_simple_command(mysql, COM_INIT_DB, db,
+ (uint) strlen(db),0,0)))
+ return(error);
+ free(mysql->db);
+ mysql->db=strdup(db);
+ return(0);
+}
+
+
+/*************************************************************************
+** Send a QUIT to the server and close the connection
+** If handle is allocated by mysql connect free it.
+*************************************************************************/
+
+static void mysql_close_options(MYSQL *mysql)
+{
+ if (mysql->options.init_command)
+ {
+ char **begin= (char **)mysql->options.init_command->buffer;
+ char **end= begin + mysql->options.init_command->elements;
+
+ for (;begin < end; begin++)
+ free(*begin);
+ ma_delete_dynamic(mysql->options.init_command);
+ free(mysql->options.init_command);
+ }
+ free(mysql->options.user);
+ free(mysql->options.host);
+ free(mysql->options.password);
+ free(mysql->options.unix_socket);
+ free(mysql->options.db);
+ free(mysql->options.my_cnf_file);
+ free(mysql->options.my_cnf_group);
+ free(mysql->options.charset_dir);
+ free(mysql->options.charset_name);
+ free(mysql->options.bind_address);
+ free(mysql->options.ssl_key);
+ free(mysql->options.ssl_cert);
+ free(mysql->options.ssl_ca);
+ free(mysql->options.ssl_capath);
+ free(mysql->options.ssl_cipher);
+
+ if (mysql->options.extension)
+ {
+ struct mysql_async_context *ctxt;
+ if ((ctxt = mysql->options.extension->async_context))
+ {
+ my_context_destroy(&ctxt->async_context);
+ free(ctxt);
+ mysql->options.extension->async_context= 0;
+ }
+ free(mysql->options.extension->plugin_dir);
+ free(mysql->options.extension->default_auth);
+ free(mysql->options.extension->db_driver);
+ free(mysql->options.extension->ssl_crl);
+ free(mysql->options.extension->ssl_crlpath);
+ free(mysql->options.extension->tls_fp);
+ free(mysql->options.extension->tls_fp_list);
+ free(mysql->options.extension->tls_pw);
+ free(mysql->options.extension->tls_version);
+ free(mysql->options.extension->url);
+ free(mysql->options.extension->connection_handler);
+ free(mysql->options.extension->proxy_header);
+ if(ma_hashtbl_inited(&mysql->options.extension->connect_attrs))
+ ma_hashtbl_free(&mysql->options.extension->connect_attrs);
+ if (ma_hashtbl_inited(&mysql->options.extension->userdata))
+ ma_hashtbl_free(&mysql->options.extension->userdata);
+ free(mysql->options.extension->restricted_auth);
+ free(mysql->options.extension->rpl_host);
+
+ }
+ free(mysql->options.extension);
+ /* clear all pointer */
+ memset(&mysql->options, 0, sizeof(mysql->options));
+}
+
+static void mysql_close_memory(MYSQL *mysql)
+{
+ ma_clear_session_state(mysql);
+ free(mysql->host_info);
+ free(mysql->host);
+ free(mysql->user);
+ free(mysql->passwd);
+ free(mysql->db);
+ free(mysql->unix_socket);
+ free(mysql->server_version);
+ mysql->host_info= mysql->host= mysql->unix_socket=
+ mysql->server_version=mysql->user=mysql->passwd=mysql->db=0;
+}
+
+void my_set_error(MYSQL *mysql,
+ unsigned int error_nr,
+ const char *sqlstate,
+ const char *format,
+ ...)
+{
+ va_list ap;
+
+ const char *errmsg= format;
+
+ mysql->net.last_errno= error_nr;
+ ma_strmake(mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH);
+
+ if (!format)
+ {
+ if (IS_MYSQL_ERROR(error_nr) || IS_MARIADB_ERROR(error_nr))
+ errmsg= ER(error_nr);
+ else {
+ snprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE - 1,
+ ER_UNKNOWN_ERROR_CODE, error_nr);
+ return;
+ }
+ }
+
+ va_start(ap, format);
+ vsnprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE - 1, errmsg, ap);
+ va_end(ap);
+ return;
+}
+
+void mysql_close_slow_part(MYSQL *mysql)
+{
+ if (mysql->net.pvio)
+ {
+ free_old_query(mysql);
+ mysql->status=MYSQL_STATUS_READY; /* Force command */
+ mysql->options.reconnect=0;
+ if (mysql->net.pvio && mysql->net.buff)
+ ma_simple_command(mysql, COM_QUIT,NullS,0,1,0);
+ end_server(mysql);
+ }
+ /* there is an ongoing async operation */
+ else if (mysql->options.extension && mysql->options.extension->async_context)
+ {
+ if (mysql->options.extension->async_context->pending_gai_res)
+ {
+ freeaddrinfo(mysql->options.extension->async_context->pending_gai_res);
+ mysql->options.extension->async_context->pending_gai_res = 0;
+ }
+ if (mysql->options.extension->async_context->pvio)
+ {
+ ma_pvio_close(mysql->options.extension->async_context->pvio);
+ mysql->options.extension->async_context->pvio = 0;
+ }
+ }
+}
+
+static void ma_clear_session_state(MYSQL *mysql)
+{
+ uint i;
+
+ if (!mysql || !mysql->extension)
+ return;
+
+ for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
+ {
+ list_free(mysql->extension->session_state[i].list, 0);
+ }
+ memset(mysql->extension->session_state, 0, sizeof(struct st_mariadb_session_state) * SESSION_TRACK_TYPES);
+}
+
+void STDCALL
+mysql_close(MYSQL *mysql)
+{
+ if (mysql) /* Some simple safety */
+ {
+ if (mysql->extension && mysql->extension->conn_hdlr)
+ {
+ MA_CONNECTION_HANDLER *p= mysql->extension->conn_hdlr;
+ if (p->plugin->close)
+ p->plugin->close(mysql);
+ free(p);
+ /* Fix for CONC-294: Since we already called plugin->close function
+ we need to prevent that mysql_close_slow_part (which sends COM_QUIT
+ to the server) will be handled by plugin again. */
+ mysql->extension->conn_hdlr= NULL;
+ }
+
+ if (mysql->methods)
+ mysql->methods->db_close(mysql);
+
+ /* reset the connection in all active statements */
+ ma_invalidate_stmts(mysql, "mysql_close()");
+
+ mysql_close_memory(mysql);
+ mysql_close_options(mysql);
+ ma_clear_session_state(mysql);
+
+ if (mysql->net.extension)
+ {
+ if (compression_plugin(&mysql->net))
+ compression_plugin(&mysql->net)->free_ctx(compression_ctx(&mysql->net));
+ free(mysql->net.extension);
+ }
+
+ mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
+
+ /* Clear pointers for better safety */
+ memset((char*) &mysql->options, 0, sizeof(mysql->options));
+
+ if (mysql->extension)
+ free(mysql->extension);
+
+ /* Clear pointers for better safety */
+ mysql->net.extension = NULL;
+ mysql->extension = NULL;
+
+ mysql->net.pvio= 0;
+ if (mysql->free_me)
+ free(mysql);
+ }
+ return;
+}
+
+
+/**************************************************************************
+** Do a query. If query returned rows, free old rows.
+** Read data by mysql_store_result or by repeating calls to mysql_fetch_row
+**************************************************************************/
+
+int STDCALL
+mysql_query(MYSQL *mysql, const char *query)
+{
+ return mysql_real_query(mysql,query, (unsigned long) strlen(query));
+}
+
+/*
+ Send the query and return so we can do something else.
+ Needs to be followed by mysql_read_query_result() when we want to
+ finish processing it.
+*/
+
+
+
+int STDCALL
+mysql_send_query(MYSQL* mysql, const char* query, unsigned long length)
+{
+ return ma_simple_command(mysql, COM_QUERY, query, length, 1,0);
+}
+
+void ma_save_session_track_info(void *ptr, enum enum_mariadb_status_info type, ...)
+{
+ MYSQL *mysql= (MYSQL *)ptr;
+ enum enum_session_state_type track_type;
+ va_list ap;
+
+ DBUG_ASSERT(mysql != NULL);
+
+ /* We only handle SESSION_TRACK_TYPE here */
+ if (type != SESSION_TRACK_TYPE)
+ return;
+
+ va_start(ap, type);
+
+ track_type= va_arg(ap, enum enum_session_state_type);
+
+ switch (track_type) {
+ case SESSION_TRACK_SCHEMA:
+ case SESSION_TRACK_STATE_CHANGE:
+ case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
+ case SESSION_TRACK_TRANSACTION_STATE:
+ case SESSION_TRACK_GTIDS:
+ case SESSION_TRACK_SYSTEM_VARIABLES:
+ {
+ LIST *session_item;
+ MYSQL_LEX_STRING *str;
+ char *tmp;
+ MARIADB_CONST_STRING *data1= va_arg(ap, MARIADB_CONST_STRING *);
+
+ if (!(session_item= ma_multi_malloc(0,
+ &session_item, sizeof(LIST),
+ &str, sizeof(MYSQL_LEX_STRING),
+ &tmp, data1->length,
+ NULL)))
+ goto mem_error;
+
+ str->str= tmp;
+ memcpy(str->str, data1->str, data1->length);
+ str->length= data1->length;
+ session_item->data= str;
+ mysql->extension->session_state[track_type].list= list_add(mysql->extension->session_state[track_type].list,
+ session_item);
+ if (track_type == SESSION_TRACK_SYSTEM_VARIABLES)
+ {
+ MARIADB_CONST_STRING *data2= va_arg(ap, MARIADB_CONST_STRING *);
+ if (!(session_item= ma_multi_malloc(0,
+ &session_item, sizeof(LIST),
+ &str, sizeof(MYSQL_LEX_STRING),
+ &tmp, data2->length,
+ NULL)))
+ goto mem_error;
+
+ str->str= tmp;
+ memcpy(str->str, data2->str, data2->length);
+ str->length= data2->length;
+ session_item->data= str;
+ mysql->extension->session_state[track_type].list= list_add(mysql->extension->session_state[track_type].list,
+ session_item);
+ }
+ }
+ break;
+ }
+ return;
+
+mem_error:
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return;
+}
+
+int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length)
+{
+ uchar *end= mysql->net.read_pos+length;
+ size_t item_len;
+ unsigned int last_status= mysql->server_status;
+ mysql->affected_rows= net_field_length_ll(&pos);
+ mysql->insert_id= net_field_length_ll(&pos);
+ mysql->server_status=uint2korr(pos);
+
+ /* clear error */
+ mysql->net.last_error[0]= mysql->net.last_errno= mysql->net.extension->extended_errno= 0;
+
+ /* callback */
+ ma_status_callback(mysql, last_status);
+ pos+=2;
+ mysql->warning_count=uint2korr(pos);
+ pos+=2;
+ if (pos > end)
+ goto corrupted;
+ if (pos < end)
+ {
+ if ((item_len= net_field_length(&pos)))
+ mysql->info=(char*) pos;
+ if (pos + item_len > end)
+ goto corrupted;
+
+ /* check if server supports session tracking */
+ if (mysql->server_capabilities & CLIENT_SESSION_TRACKING)
+ {
+ ma_clear_session_state(mysql);
+ pos+= item_len;
+
+ if (mysql->server_status & SERVER_SESSION_STATE_CHANGED)
+ {
+ int i;
+ if (pos < end)
+ {
+ enum enum_session_state_type si_type;
+ uchar *old_pos= pos;
+
+ item_len= net_field_length(&pos); /* length for all items */
+ if (pos + item_len > end)
+ goto corrupted;
+ end= pos + item_len;
+
+ /* length was already set, so make sure that info will be zero terminated */
+ if (mysql->info)
+ *old_pos= 0;
+
+ while (pos < end)
+ {
+ size_t plen;
+ MARIADB_CONST_STRING data1, data2;
+ si_type= (enum enum_session_state_type)net_field_length(&pos);
+
+ switch(si_type) {
+ case SESSION_TRACK_SCHEMA:
+ case SESSION_TRACK_STATE_CHANGE:
+ case SESSION_TRACK_TRANSACTION_CHARACTERISTICS:
+ case SESSION_TRACK_SYSTEM_VARIABLES:
+ case SESSION_TRACK_TRANSACTION_STATE:
+ case SESSION_TRACK_GTIDS:
+ if (si_type != SESSION_TRACK_STATE_CHANGE)
+ {
+ net_field_length(&pos); /* ignore total length, item length will follow next */
+ }
+ if (si_type == SESSION_TRACK_GTIDS)
+ {
+ /* skip encoding */
+ net_field_length(&pos);
+ }
+ plen= net_field_length(&pos);
+ if (pos + plen > end)
+ goto corrupted;
+
+ data1.str= (char *)pos;
+ data1.length= plen;
+ if (si_type != SESSION_TRACK_SYSTEM_VARIABLES)
+ {
+ mysql->options.extension->status_callback(mysql->options.extension->status_data,
+ SESSION_TRACK_TYPE, si_type,
+ &data1);
+ if (mysql->net.last_errno)
+ goto oom;
+ }
+ pos+= plen;
+ /* in case schema has changed, we have to update mysql->db */
+ if (si_type == SESSION_TRACK_SCHEMA)
+ {
+ free(mysql->db);
+ mysql->db= malloc(plen + 1);
+ memcpy(mysql->db, data1.str, data1.length);
+ mysql->db[data1.length]= 0;
+ }
+ else if (si_type == SESSION_TRACK_SYSTEM_VARIABLES)
+ {
+ my_bool set_charset= 0;
+ /* make sure that we update charset in case it has changed */
+ if (!strncmp(data1.str, "character_set_client", plen))
+ set_charset= 1;
+ plen= net_field_length(&pos);
+ if (pos + plen > end)
+ goto corrupted;
+ data2.str= (char *)pos;
+ data2.length= plen;
+
+ mysql->options.extension->status_callback(mysql->options.extension->status_data,
+ SESSION_TRACK_TYPE, si_type,
+ &data1, &data2);
+ if (mysql->net.last_errno)
+ goto oom;
+
+ pos+= plen;
+ if (set_charset && plen < CHARSET_NAME_LEN &&
+ strncmp(mysql->charset->csname, data2.str, data2.length) != 0)
+ {
+ char cs_name[CHARSET_NAME_LEN];
+ const MARIADB_CHARSET_INFO *cs_info;
+ memcpy(cs_name, data2.str, data2.length);
+ cs_name[plen]= 0;
+ if ((cs_info = mysql_find_charset_name(cs_name)))
+ mysql->charset= cs_info;
+ }
+ }
+ break;
+ default:
+ /* not supported yet */
+ plen= net_field_length(&pos);
+ if (pos + plen > end)
+ goto corrupted;
+ pos+= plen;
+ break;
+ }
+ }
+ }
+ for (i= SESSION_TRACK_BEGIN; i <= SESSION_TRACK_END; i++)
+ {
+ mysql->extension->session_state[i].list= list_reverse(mysql->extension->session_state[i].list);
+ mysql->extension->session_state[i].current= mysql->extension->session_state[i].list;
+ }
+ }
+ }
+ }
+ /* CONC-351: clear session state information */
+ else if (mysql->server_capabilities & CLIENT_SESSION_TRACKING)
+ ma_clear_session_state(mysql);
+ return(0);
+
+oom:
+ ma_clear_session_state(mysql);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return -1;
+
+corrupted:
+ ma_clear_session_state(mysql);
+ SET_CLIENT_ERROR(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
+ return -1;
+}
+
+
+static int ma_deep_copy_field(const MYSQL_FIELD *src, MYSQL_FIELD *dst,
+ MA_MEM_ROOT *r)
+{
+#define MA_STRDUP(f) \
+ do \
+ { \
+ if (src->f) \
+ { \
+ if ((dst->f= ma_strdup_root(r, src->f)) == NULL) \
+ return -1; \
+ } \
+ else \
+ { \
+ dst->f= NULL; \
+ } \
+ } \
+ while (0)
+
+
+ MA_STRDUP(catalog);
+ MA_STRDUP(db);
+ MA_STRDUP(def);
+ MA_STRDUP(name);
+ MA_STRDUP(org_name);
+ MA_STRDUP(org_table);
+ MA_STRDUP(table);
+#undef MA_STRDUP
+
+ dst->catalog_length= src->catalog_length;
+ dst->charsetnr= src->charsetnr;
+ dst->db_length= src->db_length;
+ dst->decimals= src->decimals;
+ dst->def_length= src->def_length;
+ dst->extension=
+ src->extension
+ ? ma_field_extension_deep_dup(r,
+ src->extension)
+ : NULL;
+ dst->flags= src->flags;
+ dst->length= src->length;
+ dst->max_length = src->max_length;
+ dst->name_length= src->name_length;
+ dst->org_name_length= src->org_name_length;
+ dst->org_table_length= src->org_table_length;
+ dst->table_length= src->table_length;
+ dst->type= src->type;
+ return 0;
+}
+
+
+MYSQL_FIELD *ma_duplicate_resultset_metadata(MYSQL_FIELD *fields, size_t count,
+ MA_MEM_ROOT *memroot)
+{
+ size_t i;
+ MYSQL_FIELD *result=
+ (MYSQL_FIELD *) ma_alloc_root(memroot, sizeof(MYSQL_FIELD) * count);
+ if (!result)
+ return NULL;
+
+ for (i= 0; i < count; i++)
+ {
+ if (ma_deep_copy_field(&fields[i], &result[i], memroot))
+ return NULL;
+ }
+ return result;
+}
+
+
+int mthd_my_read_query_result(MYSQL *mysql)
+{
+ uchar *pos;
+ ulong field_count;
+ MYSQL_DATA *fields;
+ ulong length;
+ const uchar *end;
+ uchar has_metadata;
+
+ my_bool can_local_infile= (mysql->options.extension) && (mysql->extension->auto_local_infile != WAIT_FOR_QUERY);
+
+ if (mysql->options.extension && mysql->extension->auto_local_infile == ACCEPT_FILE_REQUEST)
+ mysql->extension->auto_local_infile= WAIT_FOR_QUERY;
+
+ if ((length = ma_net_safe_read(mysql)) == packet_error)
+ {
+ return(1);
+ }
+ free_old_query(mysql); /* Free old result */
+get_info:
+ pos=(uchar*) mysql->net.read_pos;
+ end= pos + length;
+ if ((field_count= net_field_length(&pos)) == 0)
+ return ma_read_ok_packet(mysql, pos, length);
+ if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */
+ {
+ int error=mysql_handle_local_infile(mysql, (char *)pos, can_local_infile);
+
+ if ((length=ma_net_safe_read(mysql)) == packet_error || error)
+ return(-1);
+ goto get_info; /* Get info packet */
+ }
+
+ has_metadata= 1;
+ if (ma_supports_cache_metadata(mysql))
+ {
+ assert(mysql->fields == NULL);
+ if (pos < end)
+ {
+ has_metadata= *pos;
+ pos++;
+ }
+ }
+
+ if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
+ mysql->server_status|= SERVER_STATUS_IN_TRANS;
+
+ if (has_metadata)
+ {
+ if (!(fields= mysql->methods->db_read_rows(mysql, (MYSQL_FIELD *) 0,
+ ma_result_set_rows(mysql))))
+ return (-1);
+ if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc,
+ (uint) field_count, 1)))
+ return (-1);
+ }
+ else
+ {
+ unsigned int last_status= mysql->server_status;
+ /* Read EOF, to get the status and warning count. */
+ if ((length= ma_net_safe_read(mysql)) == packet_error)
+ {
+ return -1;
+ }
+ pos= (uchar *) mysql->net.read_pos;
+ if (length != 5 || pos[0] != 0xfe)
+ {
+ return -1;
+ }
+ mysql->warning_count= uint2korr(pos + 1);
+ mysql->server_status= uint2korr(pos + 3);
+ ma_status_callback(mysql, last_status);
+ }
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ mysql->field_count=field_count;
+ return(0);
+}
+
+int STDCALL mysql_session_track_get_next(MYSQL *mysql, enum enum_session_state_type type,
+ const char **data, size_t *length)
+{
+ MYSQL_LEX_STRING *str;
+ if (!mysql->extension->session_state[type].current)
+ return 1;
+
+ str= (MYSQL_LEX_STRING *)mysql->extension->session_state[type].current->data;
+ mysql->extension->session_state[type].current= mysql->extension->session_state[type].current->next;
+
+ *data= str->str ? str->str : NULL;
+ *length= str->str ? str->length : 0;
+ return 0;
+}
+
+int STDCALL mysql_session_track_get_first(MYSQL *mysql, enum enum_session_state_type type,
+ const char **data, size_t *length)
+{
+ mysql->extension->session_state[type].current= mysql->extension->session_state[type].list;
+ return mysql_session_track_get_next(mysql, type, data, length);
+}
+
+my_bool STDCALL
+mysql_read_query_result(MYSQL *mysql)
+{
+ return test(mysql->methods->db_read_query_result(mysql)) ? 1 : 0;
+}
+
+int STDCALL
+mysql_real_query(MYSQL *mysql, const char *query, unsigned long length)
+{
+ my_bool skip_result= OPT_EXT_VAL(mysql, multi_command);
+
+ if (length == (unsigned long)-1)
+ length= (unsigned long)strlen(query);
+
+ free_old_query(mysql);
+
+ if (ma_simple_command(mysql, COM_QUERY,query,length,1,0))
+ return(-1);
+ if (!skip_result && !mysql->options.extension->skip_read_response)
+ return(mysql->methods->db_read_query_result(mysql));
+ return(0);
+}
+
+/**************************************************************************
+** Alloc result struct for buffered results. All rows are read to buffer.
+** mysql_data_seek may be used.
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_store_result(MYSQL *mysql)
+{
+ MYSQL_RES *result;
+
+ if (!mysql->fields)
+ return(0);
+ if (mysql->status != MYSQL_STATUS_GET_RESULT)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ mysql->status=MYSQL_STATUS_READY; /* server is ready */
+ if (!(result=(MYSQL_RES*) calloc(1, sizeof(MYSQL_RES)+
+ sizeof(ulong)*mysql->field_count)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ result->eof=1; /* Marker for buffered */
+ result->lengths=(ulong*) (result+1);
+ if (!(result->data=mysql->methods->db_read_rows(mysql,mysql->fields,mysql->field_count)))
+ {
+ free(result);
+ return(0);
+ }
+ mysql->affected_rows= result->row_count= result->data->rows;
+ result->data_cursor= result->data->data;
+ result->fields= mysql->fields;
+ result->field_alloc= mysql->field_alloc;
+ result->field_count= mysql->field_count;
+ result->current_field=0;
+ result->current_row=0; /* Must do a fetch first */
+ mysql->fields=0; /* fields is now in result */
+ return(result); /* Data fetched */
+}
+
+
+/**************************************************************************
+** Alloc struct for use with unbuffered reads. Data is fetched by domand
+** when calling to mysql_fetch_row.
+** mysql_data_seek is a noop.
+**
+** No other queries may be specified with the same MYSQL handle.
+** There shouldn't be much processing per row because mysql server shouldn't
+** have to wait for the client (and will not wait more than 30 sec/packet).
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_use_result(MYSQL *mysql)
+{
+ MYSQL_RES *result;
+
+ if (!mysql->fields)
+ return(0);
+ if (mysql->status != MYSQL_STATUS_GET_RESULT)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ if (!(result=(MYSQL_RES*) calloc(1, sizeof(*result)+
+ sizeof(ulong)*mysql->field_count)))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ result->lengths=(ulong*) (result+1);
+ if (!(result->row=(MYSQL_ROW)
+ malloc(sizeof(result->row[0])*(mysql->field_count+1))))
+ { /* Ptrs: to one row */
+ free(result);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(0);
+ }
+ result->fields= mysql->fields;
+ result->field_alloc= mysql->field_alloc;
+ result->field_count= mysql->field_count;
+ result->current_field=0;
+ result->handle= mysql;
+ result->current_row= 0;
+ mysql->fields=0; /* fields is now in result */
+ mysql->status=MYSQL_STATUS_USE_RESULT;
+ return(result); /* Data is read to be fetched */
+}
+
+/**************************************************************************
+** Return next field of the query results
+**************************************************************************/
+MYSQL_FIELD * STDCALL
+mysql_fetch_field(MYSQL_RES *result)
+{
+ if (result->current_field >= result->field_count)
+ return(NULL);
+ return &result->fields[result->current_field++];
+}
+
+
+/**************************************************************************
+** Return mysql field metadata
+**************************************************************************/
+int STDCALL
+mariadb_field_attr(MARIADB_CONST_STRING *attr,
+ const MYSQL_FIELD *field,
+ enum mariadb_field_attr_t type)
+{
+ MA_FIELD_EXTENSION *ext= (MA_FIELD_EXTENSION*) field->extension;
+ if (!ext || type > MARIADB_FIELD_ATTR_LAST)
+ {
+ *attr= null_const_string;
+ return 1;
+ }
+ *attr= ext->metadata[type];
+ return 0;
+}
+
+
+/**************************************************************************
+** Return next row of the query results
+**************************************************************************/
+MYSQL_ROW STDCALL
+mysql_fetch_row(MYSQL_RES *res)
+{
+ if (!res)
+ return 0;
+ if (res->handle)
+ {
+ if (res->handle->status != MYSQL_STATUS_USE_RESULT &&
+ res->handle->status != MYSQL_STATUS_GET_RESULT)
+ return 0;
+ }
+ if (!res->data)
+ { /* Unbufferred fetch */
+ if (!res->eof && res->handle)
+ {
+ if (!(res->handle->methods->db_read_one_row(res->handle,res->field_count,res->row, res->lengths)))
+ {
+ res->row_count++;
+ return(res->current_row=res->row);
+ }
+ res->eof=1;
+ res->handle->status=MYSQL_STATUS_READY;
+ /* Don't clear handle in mysql_free_results */
+ res->handle=0;
+ }
+ return((MYSQL_ROW) NULL);
+ }
+ {
+ MYSQL_ROW tmp;
+ if (!res->data_cursor)
+ {
+ return(res->current_row=(MYSQL_ROW) NULL);
+ }
+ tmp = res->data_cursor->data;
+ res->data_cursor = res->data_cursor->next;
+ return(res->current_row=tmp);
+ }
+}
+
+/**************************************************************************
+** Get column lengths of the current row
+** If one uses mysql_use_result, res->lengths contains the length information,
+** else the lengths are calculated from the offset between pointers.
+**************************************************************************/
+
+ulong * STDCALL
+mysql_fetch_lengths(MYSQL_RES *res)
+{
+ ulong *lengths,*prev_length;
+ char *start;
+ MYSQL_ROW column,end;
+
+ if (!(column=res->current_row))
+ return 0; /* Something is wrong */
+ if (res->data)
+ {
+ start=0;
+ prev_length=0; /* Keep gcc happy */
+ lengths=res->lengths;
+ for (end=column+res->field_count+1 ; column != end ; column++,lengths++)
+ {
+ if (!*column)
+ {
+ *lengths=0; /* Null */
+ continue;
+ }
+ if (start) /* Found end of prev string */
+ *prev_length= (uint) (*column-start-1);
+ start= *column;
+ prev_length=lengths;
+ }
+ }
+ return res->lengths;
+}
+
+/**************************************************************************
+** Move to a specific row and column
+**************************************************************************/
+
+void STDCALL
+mysql_data_seek(MYSQL_RES *result, unsigned long long row)
+{
+ MYSQL_ROWS *tmp=0;
+ if (result->data)
+ for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ;
+ result->current_row=0;
+ result->data_cursor = tmp;
+}
+
+/*************************************************************************
+** put the row or field cursor one a position one got from mysql_row_tell()
+** This doesn't restore any data. The next mysql_fetch_row or
+** mysql_fetch_field will return the next row or field after the last used
+*************************************************************************/
+
+MYSQL_ROW_OFFSET STDCALL
+mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row)
+{
+ MYSQL_ROW_OFFSET return_value=result->data_cursor;
+ result->current_row= 0;
+ result->data_cursor= row;
+ return return_value;
+}
+
+
+MYSQL_FIELD_OFFSET STDCALL
+mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset)
+{
+ MYSQL_FIELD_OFFSET return_value=result->current_field;
+ result->current_field=field_offset;
+ return return_value;
+}
+
+/********************************************************
+ Warning: mysql_list_dbs is deprecated and will be
+ removed. Use SQL statement "SHOW DATABASES"
+ instead
+ ********************************************************/
+
+/*****************************************************************************
+** List all databases
+*****************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_dbs(MYSQL *mysql, const char *wild)
+{
+ char buff[255];
+ snprintf(buff, 255, "SHOW DATABASES LIKE '%s'", wild ? wild : "%");
+ if (mysql_query(mysql,buff))
+ return(0);
+ return (mysql_store_result(mysql));
+}
+
+
+/********************************************************
+ Warning: mysql_list_tables is deprecated and will be
+ removed. Use SQL statement "SHOW TABLES"
+ instead
+ ********************************************************/
+/*****************************************************************************
+** List all tables in a database
+** If wild is given then only the tables matching wild are returned
+*****************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_tables(MYSQL *mysql, const char *wild)
+{
+ char buff[255];
+
+ snprintf(buff, 255, "SHOW TABLES LIKE '%s'", wild ? wild : "%");
+ if (mysql_query(mysql,buff))
+ return(0);
+ return (mysql_store_result(mysql));
+}
+
+
+/**************************************************************************
+** List all fields in a table
+** If wild is given then only the fields matching wild are returned
+** Instead of this use query:
+** show fields in 'table' like "wild"
+**************************************************************************/
+
+MYSQL_RES * STDCALL
+mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
+{
+ MYSQL_RES *result;
+ MYSQL_DATA *query;
+ char buff[255];
+ int length= 0;
+
+ LINT_INIT(query);
+
+ length= snprintf(buff, 128, "%s%c%s", table, '\0', wild ? wild : "");
+
+ if (ma_simple_command(mysql, COM_FIELD_LIST,buff,length,1,0) ||
+ !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,
+ ma_result_set_rows(mysql))))
+ return(NULL);
+
+ free_old_query(mysql);
+ if (!(result = (MYSQL_RES *) calloc(1, sizeof(MYSQL_RES))))
+ {
+ free_rows(query);
+ return(NULL);
+ }
+ result->field_alloc=mysql->field_alloc;
+ mysql->fields=0;
+ result->eof=1;
+ result->field_count = (uint) query->rows;
+ result->fields= unpack_fields(mysql, query, &result->field_alloc,
+ result->field_count, 1);
+ if (result->fields)
+ return(result);
+
+ free(result);
+ return(NULL);
+}
+
+/********************************************************
+ Warning: mysql_list_processes is deprecated and will be
+ removed. Use SQL statement "SHOW PROCESSLIST"
+ instead
+ ********************************************************/
+
+/* List all running processes (threads) in server */
+
+MYSQL_RES * STDCALL
+mysql_list_processes(MYSQL *mysql)
+{
+ MYSQL_DATA *fields;
+ uint field_count;
+ uchar *pos;
+
+ LINT_INIT(fields);
+ if (ma_simple_command(mysql, COM_PROCESS_INFO,0,0,0,0))
+ return(NULL);
+ free_old_query(mysql);
+ pos=(uchar*) mysql->net.read_pos;
+ field_count=(uint) net_field_length(&pos);
+ if (!(fields = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,7)))
+ return(NULL);
+ if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc,
+ field_count, 0)))
+ return(NULL);
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ mysql->field_count=field_count;
+ return(mysql_store_result(mysql));
+}
+
+/* In 5.0 this version became an additional parameter shutdown_level */
+int STDCALL
+mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level)
+{
+ uchar s_level[2];
+ s_level[0]= (uchar)shutdown_level;
+ return(ma_simple_command(mysql, COM_SHUTDOWN, (char *)s_level, 1, 0, 0));
+}
+
+int STDCALL
+mysql_refresh(MYSQL *mysql,uint options)
+{
+ uchar bits[1];
+ bits[0]= (uchar) options;
+ return(ma_simple_command(mysql, COM_REFRESH,(char*) bits,1,0,0));
+}
+
+int STDCALL
+mysql_kill(MYSQL *mysql,ulong pid)
+{
+ char buff[12];
+ int4store(buff,pid);
+ /* if we kill our own thread, reading the response packet will fail */
+ return(ma_simple_command(mysql, COM_PROCESS_KILL,buff,4,0,0));
+}
+
+
+int STDCALL
+mysql_dump_debug_info(MYSQL *mysql)
+{
+ return(ma_simple_command(mysql, COM_DEBUG,0,0,0,0));
+}
+
+char * STDCALL
+mysql_stat(MYSQL *mysql)
+{
+ if (ma_simple_command(mysql, COM_STATISTICS,0,0,0,0))
+ return mysql->net.last_error;
+ mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */
+ if (!mysql->net.read_pos[0])
+ {
+ SET_CLIENT_ERROR(mysql, CR_WRONG_HOST_INFO , SQLSTATE_UNKNOWN, 0);
+ return mysql->net.last_error;
+ }
+ return((char*) mysql->net.read_pos);
+}
+
+int STDCALL
+mysql_ping(MYSQL *mysql)
+{
+ int rc;
+ rc= ma_simple_command(mysql, COM_PING, 0, 0, 0, 0);
+ if (rc && mysql->options.reconnect)
+ rc= ma_simple_command(mysql, COM_PING, 0, 0, 0, 0);
+ return rc;
+}
+
+char * STDCALL
+mysql_get_server_info(MYSQL *mysql)
+{
+ return((char*) mysql->server_version);
+}
+
+static size_t mariadb_server_version_id(MYSQL *mysql)
+{
+ size_t major, minor, patch;
+ char *p;
+
+ if (!(p = mysql->server_version)) {
+ return 0;
+ }
+
+ major = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ minor = strtol(p, &p, 10);
+ p += 1; /* consume the dot */
+ patch = strtol(p, &p, 10);
+
+ return (major * 10000L + (unsigned long)(minor * 100L + patch));
+}
+
+unsigned long STDCALL mysql_get_server_version(MYSQL *mysql)
+{
+ return (unsigned long)mariadb_server_version_id(mysql);
+}
+
+char * STDCALL
+mysql_get_host_info(MYSQL *mysql)
+{
+ return(mysql->host_info);
+}
+
+uint STDCALL
+mysql_get_proto_info(MYSQL *mysql)
+{
+ return (mysql->protocol_version);
+}
+
+const char * STDCALL
+mysql_get_client_info(void)
+{
+ return (char*) MARIADB_PACKAGE_VERSION;
+}
+
+static size_t get_store_length(size_t length)
+{
+ #define MAX_STORE_SIZE 9
+ unsigned char buffer[MAX_STORE_SIZE], *p;
+
+ /* We just store the length and subtract offset of our buffer
+ to determine the length */
+ p= mysql_net_store_length(buffer, length);
+ return p - buffer;
+}
+
+uchar *ma_get_hash_keyval(const uchar *hash_entry,
+ unsigned int *length,
+ my_bool not_used __attribute__((unused)))
+{
+ /* Hash entry has the following format:
+ Offset: 0 key (\0 terminated)
+ key_length + 1 value (\0 terminated)
+ */
+ uchar *p= (uchar *)hash_entry;
+ size_t len= strlen((char *)p);
+ *length= (unsigned int)len;
+ return p;
+}
+
+void ma_int_hash_free(void *p)
+{
+ free(p);
+}
+
+int
+mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
+{
+ va_list ap;
+ void *arg1;
+ size_t stacksize;
+ struct mysql_async_context *ctxt;
+
+ va_start(ap, option);
+
+ arg1= va_arg(ap, void *);
+
+ switch (option) {
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ mysql->options.connect_timeout= *(uint*) arg1;
+ break;
+ case MYSQL_OPT_COMPRESS:
+ mysql->options.compress= 1; /* Remember for connect */
+ mysql->options.client_flag|= CLIENT_COMPRESS;
+ break;
+ case MYSQL_OPT_NAMED_PIPE:
+ mysql->options.named_pipe=1; /* Force named pipe */
+ break;
+ case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
+ if (!arg1 || test(*(unsigned int*) arg1))
+ mysql->options.client_flag|= CLIENT_LOCAL_FILES;
+ else
+ mysql->options.client_flag&= ~CLIENT_LOCAL_FILES;
+ if (arg1) {
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ mysql->extension->auto_local_infile= *(uint*)arg1 == LOCAL_INFILE_MODE_AUTO
+ ? WAIT_FOR_QUERY : ALWAYS_ACCEPT;
+ }
+ break;
+ case MYSQL_INIT_COMMAND:
+ options_add_initcommand(&mysql->options, (char *)arg1);
+ break;
+ case MYSQL_READ_DEFAULT_FILE:
+ OPT_SET_VALUE_STR(&mysql->options, my_cnf_file, (char *)arg1);
+ break;
+ case MYSQL_READ_DEFAULT_GROUP:
+ OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, arg1 ? (char *)arg1 : "");
+ break;
+ case MYSQL_SET_CHARSET_DIR:
+ OPT_SET_VALUE_STR(&mysql->options, charset_dir, arg1);
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ OPT_SET_VALUE_STR(&mysql->options, charset_name, arg1);
+ break;
+ case MYSQL_OPT_RECONNECT:
+ mysql->options.reconnect= *(my_bool *)arg1;
+ break;
+ case MYSQL_OPT_PROTOCOL:
+ mysql->options.protocol= *((uint *)arg1);
+ break;
+#ifdef _WIN32
+ case MYSQL_SHARED_MEMORY_BASE_NAME:
+ OPT_SET_VALUE_STR(&mysql->options, shared_memory_base_name, arg1);
+ break;
+#endif
+ case MYSQL_OPT_READ_TIMEOUT:
+ mysql->options.read_timeout= *(uint *)arg1;
+ break;
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ mysql->options.write_timeout= *(uint *)arg1;
+ break;
+ case MYSQL_REPORT_DATA_TRUNCATION:
+ mysql->options.report_data_truncation= *(my_bool *)arg1;
+ break;
+ case MYSQL_PROGRESS_CALLBACK:
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (mysql->options.extension)
+ mysql->options.extension->report_progress=
+ (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, server_public_key, (char *)arg1);
+ break;
+ case MYSQL_PLUGIN_DIR:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, plugin_dir, (char *)arg1);
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, default_auth, (char *)arg1);
+ break;
+ case MYSQL_OPT_NONBLOCK:
+ if (mysql->options.extension &&
+ (ctxt = mysql->options.extension->async_context) != 0)
+ {
+ /*
+ We must not allow changing the stack size while a non-blocking call is
+ suspended (as the stack is then in use).
+ */
+ if (ctxt->suspended)
+ goto end;
+ my_context_destroy(&ctxt->async_context);
+ free(ctxt);
+ }
+ if (!(ctxt= (struct mysql_async_context *)
+ calloc(1, sizeof(*ctxt))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ stacksize= 0;
+ if (arg1)
+ stacksize= *(const size_t *)arg1;
+ if (!stacksize)
+ stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE;
+ if (my_context_init(&ctxt->async_context, stacksize))
+ {
+ free(ctxt);
+ goto end;
+ }
+ if (!mysql->options.extension)
+ if(!(mysql->options.extension= (struct st_mysql_options_extension *)
+ calloc(1, sizeof(struct st_mysql_options_extension))))
+ {
+ free(ctxt);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ mysql->options.extension->async_context= ctxt;
+ break;
+ case MYSQL_OPT_MAX_ALLOWED_PACKET:
+ if (mysql)
+ mysql->options.max_allowed_packet= (unsigned long)(*(size_t *)arg1);
+ else
+ max_allowed_packet= (unsigned long)(*(size_t *)arg1);
+ break;
+ case MYSQL_OPT_NET_BUFFER_LENGTH:
+ net_buffer_length= (unsigned long)(*(size_t *)arg1);
+ break;
+ case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
+ if (*(my_bool *)arg1)
+ mysql->options.client_flag |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
+ else
+ mysql->options.client_flag &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
+ break;
+ case MYSQL_OPT_SSL_ENFORCE:
+ mysql->options.use_ssl= (*(my_bool *)arg1);
+ break;
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ OPT_SET_EXTENDED_VALUE(&mysql->options, tls_verify_server_cert, *(my_bool *)arg1);
+ break;
+ case MYSQL_OPT_SSL_KEY:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_key, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CERT:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_cert, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CA:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_ca, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CAPATH:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_capath, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CIPHER:
+ OPT_SET_VALUE_STR(&mysql->options, ssl_cipher, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CRL:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1);
+ break;
+ case MYSQL_OPT_SSL_CRLPATH:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crlpath, (char *)arg1);
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_DELETE:
+ {
+ uchar *h;
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (ma_hashtbl_inited(&mysql->options.extension->connect_attrs) &&
+ (h= (uchar *)ma_hashtbl_search(&mysql->options.extension->connect_attrs, (uchar *)arg1,
+ arg1 ? (uint)strlen((char *)arg1) : 0)))
+ {
+ uchar *p= h;
+ size_t key_len= strlen((char *)p);
+ mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len);
+ p+= key_len + 1;
+ key_len= strlen((char *)p);
+ mysql->options.extension->connect_attrs_len-= key_len + get_store_length(key_len);
+ ma_hashtbl_delete(&mysql->options.extension->connect_attrs, h);
+ }
+
+ }
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_RESET:
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (ma_hashtbl_inited(&mysql->options.extension->connect_attrs))
+ {
+ ma_hashtbl_free(&mysql->options.extension->connect_attrs);
+ mysql->options.extension->connect_attrs_len= 0;
+ }
+ break;
+ case MARIADB_OPT_CONNECTION_HANDLER:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, connection_handler, (char *)arg1);
+ break;
+ case MARIADB_OPT_PORT:
+ OPT_SET_VALUE_INT(&mysql->options, port, *((uint *)arg1));
+ break;
+ case MARIADB_OPT_UNIXSOCKET:
+ OPT_SET_VALUE_STR(&mysql->options, unix_socket, arg1);
+ break;
+ case MARIADB_OPT_USER:
+ OPT_SET_VALUE_STR(&mysql->options, user, arg1);
+ break;
+ case MARIADB_OPT_HOST:
+ OPT_SET_VALUE_STR(&mysql->options, host, arg1);
+ break;
+ case MARIADB_OPT_SCHEMA:
+ OPT_SET_VALUE_STR(&mysql->options, db, arg1);
+ break;
+ case MARIADB_OPT_DEBUG:
+ break;
+ case MARIADB_OPT_FOUND_ROWS:
+ mysql->options.client_flag|= CLIENT_FOUND_ROWS;
+ break;
+ case MARIADB_OPT_INTERACTIVE:
+ mysql->options.client_flag|= CLIENT_INTERACTIVE;
+ break;
+ case MARIADB_OPT_MULTI_RESULTS:
+ mysql->options.client_flag|= CLIENT_MULTI_RESULTS;
+ break;
+ case MARIADB_OPT_MULTI_STATEMENTS:
+ mysql->options.client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
+ break;
+ case MARIADB_OPT_PASSWORD:
+ OPT_SET_VALUE_STR(&mysql->options, password, arg1);
+ break;
+ case MARIADB_OPT_USERDATA:
+ {
+ void *data= va_arg(ap, void *);
+ uchar *buffer, *p;
+ char *key= (char *)arg1;
+
+ if (!key || !data)
+ {
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (!ma_hashtbl_inited(&mysql->options.extension->userdata))
+ {
+ if (_ma_hashtbl_init(&mysql->options.extension->userdata,
+ 0, 0, 0, ma_get_hash_keyval, ma_int_hash_free, 0))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ /* check if key is already in buffer */
+ p= (uchar *)ma_hashtbl_search(&mysql->options.extension->userdata,
+ (uchar *)key,
+ (uint)strlen(key));
+ if (p)
+ {
+ p+= strlen(key) + 1;
+ memcpy(p, &data, sizeof(void *));
+ break;
+ }
+
+ if (!(buffer= (uchar *)malloc(strlen(key) + 1 + sizeof(void *))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ p= buffer;
+ strcpy((char *)p, key);
+ p+= strlen(key) + 1;
+ memcpy(p, &data, sizeof(void *));
+
+ if (ma_hashtbl_insert(&mysql->options.extension->userdata, buffer))
+ {
+ free(buffer);
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ break;
+ case MYSQL_OPT_CONNECT_ATTR_ADD:
+ {
+ uchar *buffer;
+ void *arg2= va_arg(ap, void *);
+ size_t storage_len, key_len= arg1 ? strlen((char *)arg1) : 0,
+ value_len= arg2 ? strlen((char *)arg2) : 0;
+ if (!key_len || !value_len)
+ {
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ storage_len= key_len + value_len +
+ get_store_length(key_len) +
+ get_store_length(value_len);
+
+ /* since we store terminating zero character in hash, we need
+ * to increase lengths */
+ key_len++;
+ value_len++;
+
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ if (!key_len ||
+ storage_len + mysql->options.extension->connect_attrs_len > 0xFFFF)
+ {
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+
+ if (!ma_hashtbl_inited(&mysql->options.extension->connect_attrs))
+ {
+ if (_ma_hashtbl_init(&mysql->options.extension->connect_attrs,
+ 0, 0, 0, ma_get_hash_keyval, ma_int_hash_free, 0))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ if ((buffer= (uchar *)malloc(key_len + value_len)))
+ {
+ uchar *p= buffer;
+ strcpy((char *)p, arg1);
+ p+= (strlen(arg1) + 1);
+ if (arg2)
+ strcpy((char *)p, arg2);
+
+ if (ma_hashtbl_insert(&mysql->options.extension->connect_attrs, buffer))
+ {
+ free(buffer);
+ SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ mysql->options.extension->connect_attrs_len+= storage_len;
+ }
+ else
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto end;
+ }
+ }
+ break;
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
+ break;
+ case MYSQL_SECURE_AUTH:
+ mysql->options.secure_auth= *(my_bool *)arg1;
+ break;
+ case MYSQL_OPT_BIND:
+ OPT_SET_VALUE_STR(&mysql->options, bind_address, arg1);
+ break;
+ case MARIADB_OPT_TLS_CIPHER_STRENGTH:
+ OPT_SET_EXTENDED_VALUE_INT(&mysql->options, tls_cipher_strength, *((unsigned int *)arg1));
+ break;
+ case MARIADB_OPT_SSL_FP:
+ case MARIADB_OPT_TLS_PEER_FP:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp, (char *)arg1);
+ mysql->options.use_ssl= 1;
+ break;
+ case MARIADB_OPT_SSL_FP_LIST:
+ case MARIADB_OPT_TLS_PEER_FP_LIST:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_fp_list, (char *)arg1);
+ mysql->options.use_ssl= 1;
+ break;
+ case MARIADB_OPT_TLS_PASSPHRASE:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_pw, (char *)arg1);
+ break;
+ case MARIADB_OPT_CONNECTION_READ_ONLY:
+ OPT_SET_EXTENDED_VALUE_INT(&mysql->options, read_only, *(my_bool *)arg1);
+ break;
+ case MARIADB_OPT_PROXY_HEADER:
+ {
+ size_t arg2 = va_arg(ap, size_t);
+ OPT_SET_EXTENDED_VALUE_BIN(&mysql->options, proxy_header, proxy_header_len, (char *)arg1, arg2);
+ }
+ break;
+ case MARIADB_OPT_TLS_VERSION:
+ case MYSQL_OPT_TLS_VERSION:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_version, (char *)arg1);
+ break;
+ case MARIADB_OPT_IO_WAIT:
+ CHECK_OPT_EXTENSION_SET(&mysql->options);
+ mysql->options.extension->io_wait = (int(*)(my_socket, my_bool, int))arg1;
+ break;
+ case MARIADB_OPT_SKIP_READ_RESPONSE:
+ OPT_SET_EXTENDED_VALUE_INT(&mysql->options, skip_read_response, *(my_bool *)arg1);
+ break;
+ case MARIADB_OPT_RESTRICTED_AUTH:
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, restricted_auth, (char *)arg1);
+ break;
+ case MARIADB_OPT_RPL_REGISTER_REPLICA:
+ {
+ unsigned int arg2 = va_arg(ap, unsigned int);
+ OPT_SET_EXTENDED_VALUE_STR(&mysql->options, rpl_host,(char *)arg1);
+ OPT_SET_EXTENDED_VALUE(&mysql->options, rpl_port, (ushort)arg2);
+ }
+ break;
+ case MARIADB_OPT_STATUS_CALLBACK:
+ {
+ void *arg2= va_arg(ap, void *);
+ if (arg1 || arg2)
+ {
+ if (arg1) {
+ OPT_SET_EXTENDED_VALUE(&mysql->options, status_callback, arg1);
+ }
+ if (arg2) {
+ OPT_SET_EXTENDED_VALUE(&mysql->options, status_data, arg2);
+ }
+ } else {
+ OPT_SET_EXTENDED_VALUE(&mysql->options, status_callback, ma_save_session_track_info);
+ OPT_SET_EXTENDED_VALUE(&mysql->options, status_data, mysql);
+ }
+ }
+ break;
+ default:
+ va_end(ap);
+ SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ va_end(ap);
+ return(0);
+end:
+ va_end(ap);
+ return(1);
+}
+
+int
+mysql_get_optionv(MYSQL *mysql, enum mysql_option option, void *arg, ...)
+{
+ va_list ap;
+
+ va_start(ap, arg);
+
+ switch(option) {
+ case MYSQL_OPT_CONNECT_TIMEOUT:
+ *((uint *)arg)= mysql->options.connect_timeout;
+ break;
+ case MYSQL_OPT_COMPRESS:
+ *((my_bool *)arg)= mysql->options.compress;
+ break;
+ case MYSQL_OPT_NAMED_PIPE:
+ *((my_bool *)arg)= mysql->options.named_pipe;
+ break;
+ case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/
+ *((uint *)arg)= test(mysql->options.client_flag & CLIENT_LOCAL_FILES);
+ break;
+ case MYSQL_INIT_COMMAND:
+ /* mysql_get_optionsv(mysql, MYSQL_INIT_COMMAND, commands, elements) */
+ {
+ unsigned int *elements;
+ if (arg)
+ *((char **)arg)= mysql->options.init_command ? mysql->options.init_command->buffer : NULL;
+ if ((elements= va_arg(ap, unsigned int *)))
+ *elements= mysql->options.init_command ? mysql->options.init_command->elements : 0;
+ }
+ break;
+ case MYSQL_READ_DEFAULT_FILE:
+ *((char **)arg)= mysql->options.my_cnf_file;
+ break;
+ case MYSQL_READ_DEFAULT_GROUP:
+ *((char **)arg)= mysql->options.my_cnf_group;
+ break;
+ case MYSQL_SET_CHARSET_DIR:
+ /* not supported in this version. Since all character sets
+ are internally available, we don't throw an error */
+ *((char **)arg)= NULL;
+ break;
+ case MYSQL_SET_CHARSET_NAME:
+ if (mysql->charset)
+ *((const char **)arg)= mysql->charset->csname;
+ else
+ *((char **)arg)= mysql->options.charset_name;
+ break;
+ case MYSQL_OPT_RECONNECT:
+ *((my_bool *)arg)= mysql->options.reconnect;
+ break;
+ case MYSQL_OPT_PROTOCOL:
+ *((uint *)arg)= mysql->options.protocol;
+ break;
+ case MYSQL_OPT_READ_TIMEOUT:
+ *((uint *)arg)= mysql->options.read_timeout;
+ break;
+ case MYSQL_OPT_WRITE_TIMEOUT:
+ *((uint *)arg)= mysql->options.write_timeout;
+ break;
+ case MYSQL_REPORT_DATA_TRUNCATION:
+ *((my_bool *)arg)= mysql->options.report_data_truncation;
+ break;
+ case MYSQL_PROGRESS_CALLBACK:
+ *((void (**)(const MYSQL *, uint, uint, double, const char *, uint))arg)=
+ mysql->options.extension ? mysql->options.extension->report_progress : NULL;
+ break;
+ case MYSQL_SERVER_PUBLIC_KEY:
+ *((char **)arg)= mysql->options.extension ?
+ mysql->options.extension->server_public_key : NULL;
+ break;
+ case MYSQL_PLUGIN_DIR:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->plugin_dir : NULL;
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->default_auth : NULL;
+ break;
+ case MYSQL_OPT_NONBLOCK:
+ *((my_bool *)arg)= test(mysql->options.extension && mysql->options.extension->async_context);
+ break;
+ case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
+ *((my_bool *)arg)= test(mysql->options.client_flag & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS);
+ break;
+ case MYSQL_OPT_SSL_ENFORCE:
+ *((my_bool *)arg)= mysql->options.use_ssl;
+ break;
+ case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
+ *((my_bool*)arg) = mysql->options.extension ? mysql->options.extension->tls_verify_server_cert : 0;
+ break;
+ case MYSQL_OPT_SSL_KEY:
+ *((char **)arg)= mysql->options.ssl_key;
+ break;
+ case MYSQL_OPT_SSL_CERT:
+ *((char **)arg)= mysql->options.ssl_cert;
+ break;
+ case MYSQL_OPT_SSL_CA:
+ *((char **)arg)= mysql->options.ssl_ca;
+ break;
+ case MYSQL_OPT_SSL_CAPATH:
+ *((char **)arg)= mysql->options.ssl_capath;
+ break;
+ case MYSQL_OPT_SSL_CIPHER:
+ *((char **)arg)= mysql->options.ssl_cipher;
+ break;
+ case MYSQL_OPT_SSL_CRL:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
+ break;
+ case MYSQL_OPT_SSL_CRLPATH:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
+ break;
+ case MARIADB_OPT_TLS_VERSION:
+ case MYSQL_OPT_TLS_VERSION:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_version : NULL;
+ break;
+ case MYSQL_OPT_CONNECT_ATTRS:
+ /* mysql_get_optionsv(mysql, MYSQL_OPT_CONNECT_ATTRS, keys, vals, elements) */
+ {
+ unsigned int i, *elements;
+ char **key= NULL;
+ void *arg1;
+ char **val= NULL;
+
+ if (arg)
+ key= *(char ***)arg;
+
+ arg1= va_arg(ap, char **);
+ if (arg1)
+ val= *(char ***)arg1;
+
+ if (!(elements= va_arg(ap, unsigned int *)))
+ goto error;
+
+ *elements= 0;
+
+ if (!mysql->options.extension ||
+ !ma_hashtbl_inited(&mysql->options.extension->connect_attrs))
+ break;
+
+ *elements= mysql->options.extension->connect_attrs.records;
+
+ if (val || key)
+ {
+ for (i=0; i < *elements; i++)
+ {
+ uchar *p= ma_hashtbl_element(&mysql->options.extension->connect_attrs, i);
+ if (key)
+ key[i]= (char *)p;
+ p+= strlen((char *)p) + 1;
+ if (val)
+ val[i]= (char *)p;
+ }
+ }
+ }
+ break;
+ case MYSQL_OPT_MAX_ALLOWED_PACKET:
+ *((unsigned long *)arg)= (mysql) ? mysql->options.max_allowed_packet :
+ max_allowed_packet;
+ break;
+ case MYSQL_OPT_NET_BUFFER_LENGTH:
+ *((unsigned long *)arg)= net_buffer_length;
+ break;
+ case MYSQL_SECURE_AUTH:
+ *((my_bool *)arg)= mysql->options.secure_auth;
+ break;
+ case MYSQL_OPT_BIND:
+ *((char **)arg)= mysql->options.bind_address;
+ break;
+ case MARIADB_OPT_TLS_CIPHER_STRENGTH:
+ *((unsigned int *)arg) = mysql->options.extension ? mysql->options.extension->tls_cipher_strength : 0;
+ break;
+ case MARIADB_OPT_SSL_FP:
+ case MARIADB_OPT_TLS_PEER_FP:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp : NULL;
+ break;
+ case MARIADB_OPT_SSL_FP_LIST:
+ case MARIADB_OPT_TLS_PEER_FP_LIST:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_fp_list : NULL;
+ break;
+ case MARIADB_OPT_TLS_PASSPHRASE:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->tls_pw : NULL;
+ break;
+ case MARIADB_OPT_CONNECTION_READ_ONLY:
+ *((my_bool *)arg)= mysql->options.extension ? mysql->options.extension->read_only : 0;
+ break;
+ case MARIADB_OPT_USERDATA:
+ /* nysql_get_optionv(mysql, MARIADB_OPT_USERDATA, key, value) */
+ {
+ uchar *p;
+ void *data= va_arg(ap, void *);
+ char *key= (char *)arg;
+ if (key && data && mysql->options.extension && ma_hashtbl_inited(&mysql->options.extension->userdata) &&
+ (p= (uchar *)ma_hashtbl_search(&mysql->options.extension->userdata, (uchar *)key,
+ (uint)strlen((char *)key))))
+ {
+ p+= strlen(key) + 1;
+ *((void **)data)= *((void **)p);
+ break;
+ }
+ if (data)
+ *((void **)data)= NULL;
+ }
+ break;
+ case MARIADB_OPT_CONNECTION_HANDLER:
+ *((char **)arg)= mysql->options.extension ? mysql->options.extension->connection_handler : NULL;
+ break;
+ case MARIADB_OPT_IO_WAIT:
+ *((int(**)(my_socket, my_bool, int))arg) = mysql->options.extension ? mysql->options.extension->io_wait : NULL;
+ break;
+ case MARIADB_OPT_SKIP_READ_RESPONSE:
+ *((my_bool*)arg)= mysql->options.extension ? mysql->options.extension->skip_read_response : 0;
+ break;
+ default:
+ va_end(ap);
+ SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ va_end(ap);
+ return(0);
+error:
+ va_end(ap);
+ return(1);
+}
+
+int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option, void *arg)
+{
+ return mysql_get_optionv(mysql, option, arg);
+}
+
+int STDCALL
+mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
+{
+ return mysql_optionsv(mysql, option, arg);
+}
+
+int STDCALL
+mysql_options4(MYSQL *mysql,enum mysql_option option, const void *arg1, const void *arg2)
+{
+ return mysql_optionsv(mysql, option, arg1, arg2);
+}
+/****************************************************************************
+** Functions to get information from the MySQL structure
+** These are functions to make shared libraries more usable.
+****************************************************************************/
+
+/* MYSQL_RES */
+my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res)
+{
+ return res->row_count;
+}
+
+unsigned int STDCALL mysql_num_fields(MYSQL_RES *res)
+{
+ return res->field_count;
+}
+
+/* deprecated */
+my_bool STDCALL mysql_eof(MYSQL_RES *res)
+{
+ return res->eof;
+}
+
+MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr)
+{
+ return &(res)->fields[fieldnr];
+}
+
+MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res)
+{
+ return (res)->fields;
+}
+
+MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res)
+{
+ return res->data_cursor;
+}
+
+uint STDCALL mysql_field_tell(MYSQL_RES *res)
+{
+ return (res)->current_field;
+}
+
+/* MYSQL */
+
+unsigned int STDCALL mysql_field_count(MYSQL *mysql)
+{
+ return mysql->field_count;
+}
+
+my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql)
+{
+ return (mysql)->affected_rows;
+}
+
+my_bool STDCALL mysql_autocommit(MYSQL *mysql, my_bool mode)
+{
+ return((my_bool) mysql_real_query(mysql, (mode) ? "SET autocommit=1" :
+ "SET autocommit=0", 16));
+}
+
+my_bool STDCALL mysql_commit(MYSQL *mysql)
+{
+ return((my_bool)mysql_real_query(mysql, "COMMIT", (unsigned long)strlen("COMMIT")));
+}
+
+my_bool STDCALL mysql_rollback(MYSQL *mysql)
+{
+ return((my_bool)mysql_real_query(mysql, "ROLLBACK", (unsigned long)strlen("ROLLBACK")));
+}
+
+my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql)
+{
+ return (mysql)->insert_id;
+}
+
+uint STDCALL mysql_errno(MYSQL *mysql)
+{
+ return mysql ? mysql->net.last_errno : 0;
+}
+
+const char * STDCALL mysql_error(MYSQL *mysql)
+{
+ return mysql ? (mysql)->net.last_error : (char *)"";
+}
+
+const char *STDCALL mysql_info(MYSQL *mysql)
+{
+ return (mysql)->info;
+}
+
+my_bool STDCALL mysql_more_results(MYSQL *mysql)
+{
+ return(test(mysql->server_status & SERVER_MORE_RESULTS_EXIST));
+}
+
+int STDCALL mysql_next_result(MYSQL *mysql)
+{
+
+ /* make sure communication is not blocking */
+ if (mysql->status != MYSQL_STATUS_READY)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* clear error, and mysql status variables */
+ CLEAR_CLIENT_ERROR(mysql);
+ mysql->affected_rows = (ulonglong) ~0;
+
+ if (mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ return(mysql->methods->db_read_query_result(mysql));
+ }
+
+ return(-1);
+}
+
+ulong STDCALL mysql_thread_id(MYSQL *mysql)
+{
+ return (mysql)->thread_id;
+}
+
+const char * STDCALL mysql_character_set_name(MYSQL *mysql)
+{
+ return mysql->charset->csname;
+}
+
+
+uint STDCALL mysql_thread_safe(void)
+{
+#ifdef THREAD
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+/****************************************************************************
+** Some support functions
+****************************************************************************/
+
+/*
+** Add escape characters to a string (blob?) to make it suitable for a insert
+** to should at least have place for length*2+1 chars
+** Returns the length of the to string
+*/
+
+ulong STDCALL
+mysql_escape_string(char *to,const char *from, ulong length)
+{
+ return (ulong)mysql_cset_escape_slashes(ma_default_charset_info, to, from, length);
+}
+
+ulong STDCALL
+mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
+ ulong length)
+{
+ if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
+ return (ulong)mysql_cset_escape_quotes(mysql->charset, to, from, length);
+ else
+ return (ulong)mysql_cset_escape_slashes(mysql->charset, to, from, length);
+}
+
+static void mariadb_get_charset_info(MYSQL *mysql, MY_CHARSET_INFO *cs)
+{
+ if (!cs)
+ return;
+
+ cs->number= mysql->charset->nr;
+ cs->csname= mysql->charset->csname;
+ cs->name= mysql->charset->name;
+ cs->state= 0;
+ cs->comment= NULL;
+ cs->dir= NULL;
+ cs->mbminlen= mysql->charset->char_minlen;
+ cs->mbmaxlen= mysql->charset->char_maxlen;
+
+ return;
+}
+
+void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs)
+{
+ mariadb_get_charset_info(mysql, cs);
+}
+
+int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname)
+{
+ const MARIADB_CHARSET_INFO *cs;
+
+ if (!csname)
+ goto error;
+
+ if ((cs= mysql_find_charset_name(csname)))
+ {
+ char buff[64];
+
+ snprintf(buff, 63, "SET NAMES %s", cs->csname);
+ if (!mysql_real_query(mysql, buff, (unsigned long)strlen(buff)))
+ {
+ mysql->charset= cs;
+ return(0);
+ }
+ return(mysql->net.last_errno);
+ }
+
+error:
+ my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN,
+ 0, csname, "compiled_in");
+ return(mysql->net.last_errno);
+}
+
+unsigned int STDCALL mysql_warning_count(MYSQL *mysql)
+{
+ return mysql->warning_count;
+}
+
+const char * STDCALL mysql_sqlstate(MYSQL *mysql)
+{
+ return mysql->net.sqlstate;
+}
+
+#ifndef _WIN32
+#include <signal.h>
+static void ignore_sigpipe()
+{
+ signal(SIGPIPE, SIG_IGN);
+}
+#else
+#define ignore_sigpipe()
+#endif
+
+#ifdef _WIN32
+static int mysql_once_init()
+#else
+static void mysql_once_init()
+#endif
+{
+ ma_init(); /* Will init threads */
+ init_client_errs();
+ get_default_configuration_dirs();
+ set_default_charset_by_name(MARIADB_DEFAULT_CHARSET, 0);
+ if (mysql_client_plugin_init())
+ {
+#ifdef _WIN32
+ return 1;
+#else
+ return;
+#endif
+ }
+ if (!mysql_port)
+ {
+#if !__has_feature(memory_sanitizer) /* work around MSAN deficiency */
+ struct servent *serv_ptr;
+#endif
+ char *env;
+
+ mysql_port = MARIADB_PORT;
+#if !__has_feature(memory_sanitizer) /* work around MSAN deficiency */
+ if ((serv_ptr = getservbyname("mysql", "tcp")))
+ mysql_port = (uint)ntohs((ushort)serv_ptr->s_port);
+#endif
+ if ((env = getenv("MYSQL_TCP_PORT")))
+ mysql_port =(uint)atoi(env);
+ }
+ if (!mysql_unix_port)
+ {
+ char *env;
+#ifdef _WIN32
+ mysql_unix_port = (char*)MARIADB_NAMEDPIPE;
+#else
+ mysql_unix_port = (char*)MARIADB_UNIX_ADDR;
+#endif
+ if ((env = getenv("MYSQL_UNIX_PORT")) ||
+ (env = getenv("MARIADB_UNIX_PORT")))
+ mysql_unix_port = env;
+ }
+ if (!mysql_ps_subsystem_initialized)
+ mysql_init_ps_subsystem();
+#ifdef HAVE_TLS
+ ma_tls_start(0, 0);
+#endif
+ ignore_sigpipe();
+ mysql_client_init = 1;
+#ifdef _WIN32
+ return 0;
+#endif
+}
+
+#ifdef _WIN32
+static INIT_ONCE init_once= INIT_ONCE_STATIC_INIT;
+BOOL CALLBACK win_init_once(
+ PINIT_ONCE InitOnce,
+ PVOID Parameter,
+ PVOID *lpContext)
+{
+ return !mysql_once_init();
+ return TRUE;
+}
+#else
+static pthread_once_t init_once = PTHREAD_ONCE_INIT;
+#endif
+
+int STDCALL mysql_server_init(int argc __attribute__((unused)),
+ char **argv __attribute__((unused)),
+ char **groups __attribute__((unused)))
+{
+#ifdef _WIN32
+ BOOL ret = InitOnceExecuteOnce(&init_once, win_init_once, NULL, NULL);
+ return ret? 0: 1;
+#else
+ return pthread_once(&init_once, mysql_once_init);
+#endif
+}
+
+void STDCALL mysql_server_end(void)
+{
+ if (!mysql_client_init)
+ return;
+
+ release_configuration_dirs();
+ mysql_client_plugin_deinit();
+
+ list_free(pvio_callback, 0);
+ if (ma_init_done)
+ ma_end(0);
+#ifdef HAVE_TLS
+ ma_pvio_tls_end();
+#endif
+ mysql_client_init= 0;
+ ma_init_done= 0;
+#ifdef WIN32
+ init_once = (INIT_ONCE)INIT_ONCE_STATIC_INIT;
+#else
+ init_once = (pthread_once_t)PTHREAD_ONCE_INIT;
+#endif
+}
+
+my_bool STDCALL mysql_thread_init(void)
+{
+ return 0;
+}
+
+void STDCALL mysql_thread_end(void)
+{
+}
+
+int STDCALL mysql_set_server_option(MYSQL *mysql,
+ enum enum_mysql_set_option option)
+{
+ char buffer[2];
+ int2store(buffer, (uint)option);
+ return(ma_simple_command(mysql, COM_SET_OPTION, buffer, sizeof(buffer), 0, 0));
+}
+
+ulong STDCALL mysql_get_client_version(void)
+{
+ return MARIADB_PACKAGE_VERSION_ID;
+}
+
+ulong STDCALL mysql_hex_string(char *to, const char *from, unsigned long len)
+{
+ char *start= to;
+ char hexdigits[]= "0123456789ABCDEF";
+
+ while (len--)
+ {
+ *to++= hexdigits[((unsigned char)*from) >> 4];
+ *to++= hexdigits[((unsigned char)*from) & 0x0F];
+ from++;
+ }
+ *to= 0;
+ return (ulong)(to - start);
+}
+
+my_bool STDCALL mariadb_connection(MYSQL *mysql)
+{
+ return (strstr(mysql->server_version, "MariaDB") ||
+ strstr(mysql->server_version, "-maria-"));
+}
+
+const char * STDCALL
+mysql_get_server_name(MYSQL *mysql)
+{
+ if (mysql->options.extension &&
+ mysql->options.extension->db_driver != NULL)
+ return mysql->options.extension->db_driver->name;
+ return mariadb_connection(mysql) ? "MariaDB" : "MySQL";
+}
+
+static my_socket mariadb_get_socket(MYSQL *mysql)
+{
+ my_socket sock= INVALID_SOCKET;
+ if (mysql->net.pvio)
+ {
+ ma_pvio_get_handle(mysql->net.pvio, &sock);
+
+ }
+ /* if an asynchronous connect is in progress, we need to obtain
+ pvio handle from async_context until the connection was
+ successfully established.
+ */
+ else if (mysql->options.extension && mysql->options.extension->async_context &&
+ mysql->options.extension->async_context->pvio)
+ {
+ ma_pvio_get_handle(mysql->options.extension->async_context->pvio, &sock);
+ }
+ return sock;
+}
+
+my_socket STDCALL
+mysql_get_socket(MYSQL *mysql)
+{
+ return mariadb_get_socket(mysql);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_name(const char *csname)
+{
+ return (MARIADB_CHARSET_INFO *)mysql_find_charset_name(csname);
+}
+
+MARIADB_CHARSET_INFO * STDCALL mariadb_get_charset_by_nr(unsigned int csnr)
+{
+ return (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(csnr);
+}
+
+my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ...)
+{
+ va_list ap;
+
+ va_start(ap, arg);
+
+ switch(value) {
+ case MARIADB_MAX_ALLOWED_PACKET:
+ *((size_t *)arg)= (size_t)max_allowed_packet;
+ break;
+ case MARIADB_NET_BUFFER_LENGTH:
+ *((size_t *)arg)= (size_t)net_buffer_length;
+ break;
+ case MARIADB_CONNECTION_ERROR_ID:
+ if (!mysql)
+ goto error;
+ *((unsigned int *)arg)= mysql->net.last_errno;
+ break;
+ case MARIADB_CONNECTION_ERROR:
+ if (!mysql)
+ goto error;
+ *((char **)arg)= mysql->net.last_error;
+ break;
+ case MARIADB_CONNECTION_SQLSTATE:
+ if (!mysql)
+ goto error;
+ *((char **)arg)= mysql->net.sqlstate;
+ break;
+ case MARIADB_CONNECTION_TLS_VERSION:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((char **)arg)= (char *)ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls);
+ else
+ #endif
+ goto error;
+ break;
+ case MARIADB_CONNECTION_TLS_VERSION_ID:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((unsigned int *)arg)= ma_pvio_tls_get_protocol_version_id(mysql->net.pvio->ctls);
+ else
+ #endif
+ goto error;
+ break;
+ case MARIADB_TLS_LIBRARY:
+#ifdef HAVE_TLS
+ *((const char **)arg)= tls_library_version;
+#else
+ *((const char **)arg)= "Off";
+#endif
+ break;
+ case MARIADB_CLIENT_VERSION:
+ *((const char **)arg)= MARIADB_CLIENT_VERSION_STR;
+ break;
+ case MARIADB_CLIENT_VERSION_ID:
+ *((size_t *)arg)= MARIADB_VERSION_ID;
+ break;
+ case MARIADB_CONNECTION_SERVER_VERSION:
+ if (mysql)
+ *((char **)arg)= mysql->server_version;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_TYPE:
+ if (mysql)
+ *((const char **)arg)= mariadb_connection(mysql) ? "MariaDB" : "MySQL";
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_VERSION_ID:
+ if (mysql)
+ *((size_t *)arg)= mariadb_server_version_id(mysql);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PROTOCOL_VERSION_ID:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->protocol_version;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_MARIADB_CHARSET_INFO:
+ if (mysql)
+ mariadb_get_charset_info(mysql, (MY_CHARSET_INFO *)arg);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SOCKET:
+ if (mysql)
+ *((my_socket *)arg)= mariadb_get_socket(mysql);
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_TYPE:
+ if (mysql && mysql->net.pvio)
+ *((int *)arg)= (int)mysql->net.pvio->type;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_ASYNC_TIMEOUT_MS:
+ if (mysql && mysql->options.extension && mysql->options.extension->async_context)
+ *((unsigned int *)arg)= mysql->options.extension->async_context->timeout_value;
+ break;
+ case MARIADB_CONNECTION_ASYNC_TIMEOUT:
+ if (mysql && mysql->options.extension && mysql->options.extension->async_context)
+ {
+ unsigned int timeout= mysql->options.extension->async_context->timeout_value;
+ if (timeout > UINT_MAX - 999)
+ *((unsigned int *)arg)= (timeout - 1)/1000 + 1;
+ else
+ *((unsigned int *)arg)= (timeout+999)/1000;
+ }
+ break;
+ case MARIADB_CHARSET_NAME:
+ {
+ char *name;
+ name= va_arg(ap, char *);
+ if (name)
+ *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_name(name);
+ else
+ goto error;
+ }
+ break;
+ case MARIADB_CHARSET_ID:
+ {
+ unsigned int nr;
+ nr= va_arg(ap, unsigned int);
+ *((MARIADB_CHARSET_INFO **)arg)= (MARIADB_CHARSET_INFO *)mysql_find_charset_nr(nr);
+ }
+ break;
+ case MARIADB_CONNECTION_SSL_CIPHER:
+ #ifdef HAVE_TLS
+ if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
+ *((char **)arg)= (char *)ma_pvio_tls_cipher(mysql->net.pvio->ctls);
+ else
+ #endif
+ *((char **)arg)= NULL;
+ break;
+ case MARIADB_CLIENT_ERRORS:
+ *((char ***)arg)= (char **)client_errors;
+ break;
+ case MARIADB_CONNECTION_INFO:
+ if (mysql)
+ *((char **)arg)= (char *)mysql->info;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PVIO_TYPE:
+ if (mysql && mysql->net.pvio)
+ *((unsigned int *)arg)= (unsigned int)mysql->net.pvio->type;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SCHEMA:
+ if (mysql)
+ *((char **)arg)= mysql->db;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_USER:
+ if (mysql)
+ *((char **)arg)= mysql->user;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_PORT:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->port;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_UNIX_SOCKET:
+ if (mysql)
+ *((char **)arg)= mysql->unix_socket;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_HOST:
+ if (mysql)
+ *((char **)arg)= mysql->host;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_STATUS:
+ if (mysql)
+ *((unsigned int *)arg)= mysql->server_status;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_SERVER_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->server_capabilities;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->extension->mariadb_server_capabilities;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_CLIENT_CAPABILITIES:
+ if (mysql)
+ *((unsigned long *)arg)= mysql->client_flag;
+ else
+ goto error;
+ break;
+ case MARIADB_CONNECTION_BYTES_READ:
+ *((size_t *)arg)= mysql->net.pvio->bytes_read;
+ break;
+ case MARIADB_CONNECTION_BYTES_SENT:
+ *((size_t *)arg)= mysql->net.pvio->bytes_sent;
+ break;
+ default:
+ va_end(ap);
+ return(-1);
+ }
+ va_end(ap);
+ return(0);
+error:
+ va_end(ap);
+ return(-1);
+}
+
+my_bool STDCALL mariadb_get_info(MYSQL *mysql, enum mariadb_value value, void *arg)
+{
+ return mariadb_get_infov(mysql, value, arg);
+}
+
+/*
+ Immediately aborts connection, making all subsequent read/write operations fail.
+ Does not invalidate memory used for mysql structure, nor closes any communication
+ channels - mysql_close is still needed.
+ Useful to break long query, in situation sending KILL is not possible.
+*/
+int STDCALL mariadb_cancel(MYSQL *mysql)
+{
+ if (!mysql || !mysql->net.pvio || !mysql->net.pvio->methods || !mysql->net.pvio->methods->shutdown)
+ {
+ return 1;
+ }
+ else
+ {
+ MARIADB_PVIO *pvio = mysql->net.pvio;
+ return pvio->methods->shutdown(pvio);
+ }
+}
+
+/* compatibility functions for MariaDB */
+void STDCALL
+mysql_debug(const char *debug __attribute__((unused)))
+{
+ return;
+}
+
+/********************************************************************
+ mysql_net_ functions - low-level API to MySQL protocol
+*********************************************************************/
+ulong STDCALL mysql_net_read_packet(MYSQL *mysql)
+{
+ return ma_net_safe_read(mysql);
+}
+
+ulong STDCALL mysql_net_field_length(uchar **packet)
+{
+ return net_field_length(packet);
+}
+
+my_bool STDCALL mysql_embedded(void)
+{
+#ifdef EMBEDDED_LIBRARY
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+MYSQL_PARAMETERS *STDCALL
+mysql_get_parameters(void)
+{
+ return &mariadb_internal_parameters;
+}
+
+int STDCALL mysql_reset_connection(MYSQL *mysql)
+{
+ int rc;
+
+ /* check if connection handler is active */
+ if (IS_CONNHDLR_ACTIVE(mysql))
+ {
+ if (mysql->extension->conn_hdlr->plugin && mysql->extension->conn_hdlr->plugin->reset)
+ return(mysql->extension->conn_hdlr->plugin->reset(mysql));
+ }
+
+ /* skip result sets */
+ if (mysql->status == MYSQL_STATUS_USE_RESULT ||
+ mysql->status == MYSQL_STATUS_GET_RESULT)
+ {
+ mthd_my_skip_result(mysql);
+ }
+
+ if (mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ while (mysql_next_result(mysql))
+ {
+ MYSQL_RES *res= mysql_use_result(mysql);
+ mysql_free_result(res);
+ }
+ }
+
+ mysql->status= MYSQL_STATUS_READY;
+
+ rc= ma_simple_command(mysql, COM_RESET_CONNECTION, 0, 0, 0, 0);
+ if (rc && mysql->options.reconnect)
+ {
+ /* There is no big sense in resetting but we need reconnect */
+ rc= ma_simple_command(mysql, COM_RESET_CONNECTION,0,0,0,0);
+ }
+ if (rc)
+ return 1;
+
+ /* reset the connection in all active statements */
+ ma_invalidate_stmts(mysql, "mysql_reset_connection()");
+ free_old_query(mysql);
+ mysql->status= MYSQL_STATUS_READY;
+ mysql->affected_rows= ~(my_ulonglong)0;
+ mysql->insert_id= 0;
+ return 0;
+}
+
+#undef STDCALL
+/* API functions for usage in dynamic plugins */
+struct st_mariadb_api MARIADB_API=
+{
+ mysql_num_rows,
+ mysql_num_fields,
+ mysql_eof,
+ mysql_fetch_field_direct,
+ mysql_fetch_fields,
+ mysql_row_tell,
+ mysql_field_tell,
+ mysql_field_count,
+ mysql_more_results,
+ mysql_next_result,
+ mysql_affected_rows,
+ mysql_autocommit,
+ mysql_commit,
+ mysql_rollback,
+ mysql_insert_id,
+ mysql_errno,
+ mysql_error,
+ mysql_info,
+ mysql_thread_id,
+ mysql_character_set_name,
+ mysql_get_character_set_info,
+ mysql_set_character_set,
+ mariadb_get_infov,
+ mariadb_get_info,
+ mysql_init,
+ mysql_ssl_set,
+ mysql_get_ssl_cipher,
+ mysql_change_user,
+ mysql_real_connect,
+ mysql_close,
+ mysql_select_db,
+ mysql_query,
+ mysql_send_query,
+ mysql_read_query_result,
+ mysql_real_query,
+ mysql_shutdown,
+ mysql_dump_debug_info,
+ mysql_refresh,
+ mysql_kill,
+ mysql_ping,
+ mysql_stat,
+ mysql_get_server_info,
+ mysql_get_server_version,
+ mysql_get_host_info,
+ mysql_get_proto_info,
+ mysql_list_dbs,
+ mysql_list_tables,
+ mysql_list_fields,
+ mysql_list_processes,
+ mysql_store_result,
+ mysql_use_result,
+ mysql_options,
+ mysql_free_result,
+ mysql_data_seek,
+ mysql_row_seek,
+ mysql_field_seek,
+ mysql_fetch_row,
+ mysql_fetch_lengths,
+ mysql_fetch_field,
+ mysql_escape_string,
+ mysql_real_escape_string,
+ mysql_thread_safe,
+ mysql_warning_count,
+ mysql_sqlstate,
+ mysql_server_init,
+ mysql_server_end,
+ mysql_thread_end,
+ mysql_thread_init,
+ mysql_set_server_option,
+ mysql_get_client_info,
+ mysql_get_client_version,
+ mariadb_connection,
+ mysql_get_server_name,
+ mariadb_get_charset_by_name,
+ mariadb_get_charset_by_nr,
+ mariadb_convert_string,
+ mysql_optionsv,
+ mysql_get_optionv,
+ mysql_get_option,
+ mysql_hex_string,
+ mysql_get_socket,
+ mysql_get_timeout_value,
+ mysql_get_timeout_value_ms,
+ mariadb_reconnect,
+ mysql_stmt_init,
+ mysql_stmt_prepare,
+ mysql_stmt_execute,
+ mysql_stmt_fetch,
+ mysql_stmt_fetch_column,
+ mysql_stmt_store_result,
+ mysql_stmt_param_count,
+ mysql_stmt_attr_set,
+ mysql_stmt_attr_get,
+ mysql_stmt_bind_param,
+ mysql_stmt_bind_result,
+ mysql_stmt_close,
+ mysql_stmt_reset,
+ mysql_stmt_free_result,
+ mysql_stmt_send_long_data,
+ mysql_stmt_result_metadata,
+ mysql_stmt_param_metadata,
+ mysql_stmt_errno,
+ mysql_stmt_error,
+ mysql_stmt_sqlstate,
+ mysql_stmt_row_seek,
+ mysql_stmt_row_tell,
+ mysql_stmt_data_seek,
+ mysql_stmt_num_rows,
+ mysql_stmt_affected_rows,
+ mysql_stmt_insert_id,
+ mysql_stmt_field_count,
+ mysql_stmt_next_result,
+ mysql_stmt_more_results,
+ mariadb_stmt_execute_direct,
+ mysql_reset_connection
+};
+
+/*
+ * Default methods for a connection. These methods are
+ * stored in mysql->methods and can be overwritten by
+ * a plugin, e.g. for using another database
+ */
+struct st_mariadb_methods MARIADB_DEFAULT_METHODS = {
+ /* open a connection */
+ mthd_my_real_connect,
+ /* close connection */
+ mysql_close_slow_part,
+ /* send command to server */
+ mthd_my_send_cmd,
+ /* skip result set */
+ mthd_my_skip_result,
+ /* read response packet */
+ mthd_my_read_query_result,
+ /* read all rows from a result set */
+ mthd_my_read_rows,
+ /* read one/next row */
+ mthd_my_read_one_row,
+ /* check if datatype is supported */
+ mthd_supported_buffer_type,
+ /* read response packet from prepare */
+ mthd_stmt_read_prepare_response,
+ /* read response from stmt execute */
+ mthd_my_read_query_result,
+ /* get result set metadata for a prepared statement */
+ mthd_stmt_get_result_metadata,
+ /* get param metadata for a prepared statement */
+ mthd_stmt_get_param_metadata,
+ /* read all rows (buffered) */
+ mthd_stmt_read_all_rows,
+ /* fetch one row (unbuffered) */
+ mthd_stmt_fetch_row,
+ /* store values in bind buffer */
+ mthd_stmt_fetch_to_bind,
+ /* skip unbuffered stmt result */
+ mthd_stmt_flush_unbuffered,
+ /* set error */
+ my_set_error,
+ /* invalidate statements */
+ ma_invalidate_stmts,
+ /* API functions */
+ &MARIADB_API,
+ /* read execute response */
+ mthd_stmt_read_execute_response,
+ /* generate execute request */
+ ma_stmt_execute_generate_request,
+};
diff --git a/libmariadb/libmariadb/mariadb_rpl.c b/libmariadb/libmariadb/mariadb_rpl.c
new file mode 100644
index 00000000..0019f246
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_rpl.c
@@ -0,0 +1,2099 @@
+/************************************************************************************
+ Copyright (C) 2018,2022 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+*************************************************************************************/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <mysql.h>
+#include <errmsg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <zlib.h>
+#include <ma_decimal.h>
+#include <mariadb_rpl.h>
+
+
+#ifdef WIN32
+#include <malloc.h>
+#undef alloca
+#define alloca _malloca
+#endif
+
+#define RPL_EVENT_HEADER_SIZE 19
+#define RPL_ERR_POS(r) (r)->filename_length, (r)->filename, (r)->start_position
+#define RPL_CHECK_NULL_POS(position, end)\
+{\
+ uchar *tmp= (position);\
+ while (*tmp && tmp < (end))\
+ tmp++;\
+ if (tmp > (end))\
+ goto malformed_packet;\
+}
+
+#define RPL_CHECK_POS(position, end, bytes)\
+if ((end)-(position) < (ssize_t)(bytes))\
+ goto malformed_packet;
+
+#define RPL_CHECK_FIELD_LENGTH(position, end)\
+{\
+ RPL_CHECK_POS((position), (end), 1);\
+ RPL_CHECK_POS((position), (end), net_field_size((position)));\
+}
+
+#define RPL_CHECK_POST_HEADER_LEN(position, end, type)\
+if (rpl->post_header_len[(type) - 1])\
+{\
+ RPL_CHECK_POS((position), (end), rpl->post_header_len[(type)-1])\
+}
+
+static inline uint64_t uintNkorr(uint8_t len, u_char *p)
+{
+ switch (len) {
+ case 1:
+ return *p;
+ case 2:
+ return uint2korr(p);
+ case 3:
+ return uint3korr(p);
+ case 4:
+ return uint4korr(p);
+ case 8:
+ return uint8korr(p);
+ default:
+ return 0;
+ }
+}
+
+static inline int net_field_size(uchar *p)
+{
+ if (*p <= 251)
+ return 1;
+ if (*p == 252)
+ return 3;
+ if (*p == 253)
+ return 4;
+ return 9;
+}
+
+static inline int rpl_bit_size(uint32_t x)
+{
+ int bits= 1;
+
+ while (x >>= 1)
+ bits++;
+
+ return bits;
+}
+
+static inline int rpl_byte_size(uint32_t x)
+{
+ int bits= rpl_bit_size(x);
+
+ return (bits + 7) / 8;
+}
+
+void rpl_set_error(MARIADB_RPL *rpl,
+ unsigned int error_nr,
+ const char *format,
+ ...)
+{
+ va_list ap;
+
+ const char *errmsg;
+
+ return;
+ if (!format)
+ {
+ if (error_nr >= CR_MIN_ERROR && error_nr <= CR_MYSQL_LAST_ERROR)
+ errmsg= ER(error_nr);
+ else if (error_nr >= CER_MIN_ERROR && error_nr <= CR_MARIADB_LAST_ERROR)
+ errmsg= CER(error_nr);
+ else
+ errmsg= ER(CR_UNKNOWN_ERROR);
+ }
+
+ rpl->error_no= error_nr;
+ va_start(ap, format);
+ vsnprintf(rpl->error_msg, MYSQL_ERRMSG_SIZE - 1,
+ format ? format : errmsg, ap);
+ va_end(ap);
+
+ /* For backward compatibility we also need to set a connection
+ error, if we read from primary instead of file */
+ if (rpl->mysql)
+ {
+ my_set_error(rpl->mysql, error_nr, SQLSTATE_UNKNOWN, rpl->error_msg);
+ }
+}
+
+const char * STDCALL mariadb_rpl_error(MARIADB_RPL *rpl)
+{
+ return rpl->error_msg;
+}
+
+uint32_t STDCALL mariadb_rpl_errno(MARIADB_RPL *rpl)
+{
+ return rpl->error_no;
+}
+
+uint8_t rpl_parse_opt_metadata(MARIADB_RPL_EVENT *event, const uchar *buffer, size_t length)
+{
+ const uchar *pos= buffer, *end= buffer + length;
+ struct st_mariadb_rpl_table_map_event *tm_event= (struct st_mariadb_rpl_table_map_event *)&event->event;
+
+ if (event->event_type != TABLE_MAP_EVENT)
+ return 1;
+
+ while (pos < end)
+ {
+ uint8_t meta_type= *pos++;
+ uint32_t len;
+
+ RPL_CHECK_FIELD_LENGTH((uchar *)pos, end);
+ len= net_field_length((uchar **)&pos);
+ RPL_CHECK_POS(pos, end,len);
+
+ switch(meta_type)
+ {
+ case SIGNEDNESS:
+ tm_event->signed_indicator= (uchar *)pos;
+ pos+= len;
+ break;
+ case DEFAULT_CHARSET:
+ tm_event->default_charset= *pos;
+ pos+= len;
+ break;
+ case COLUMN_CHARSET:
+ tm_event->column_charsets.data= pos;
+ tm_event->column_charsets.length= len;
+ pos+= len;
+ break;
+ case COLUMN_NAME:
+ tm_event->column_names.data= pos;
+ tm_event->column_names.length= len;
+ pos+= len;
+ break;
+ case SIMPLE_PRIMARY_KEY:
+ tm_event->simple_primary_keys.data= pos;
+ tm_event->simple_primary_keys.length= len;
+ pos+= len;
+ break;
+ case PRIMARY_KEY_WITH_PREFIX:
+ tm_event->prefixed_primary_keys.data= pos;
+ tm_event->prefixed_primary_keys.length= len;
+ pos+= len;
+ break;
+ case GEOMETRY_TYPE:
+ {
+ tm_event->geometry_types.data= pos;
+ tm_event->geometry_types.length= len;
+ pos+= len;
+ break;
+ }
+ /* Default character set used by all columns */
+ case ENUM_AND_SET_DEFAULT_CHARSET:
+ tm_event->enum_set_default_charset= *pos;
+ pos+= len;
+ break;
+ case ENUM_AND_SET_COLUMN_CHARSET:
+ tm_event->enum_set_column_charsets.data= pos;
+ tm_event->enum_set_column_charsets.length= len;
+ pos+= len;
+ break;
+ case SET_STR_VALUE:
+ tm_event->set_values.data= pos;
+ tm_event->set_values.length= len;
+ pos+= len;
+ break;
+ case ENUM_STR_VALUE:
+ tm_event->enum_values.data= pos;
+ tm_event->enum_values.length= len;
+ pos+= len;
+ break;
+ default:
+ rpl_set_error(event->rpl, CR_BINLOG_ERROR, 0, RPL_ERR_POS(event->rpl), "Unknown/unsupported event type");
+ pos+= len;
+ break;
+ }
+ }
+ return 0;
+malformed_packet:
+ return 1;
+}
+
+static void *ma_calloc_root(void *memroot, size_t len)
+{
+ void *p;
+
+ if ((p= ma_alloc_root(memroot, len)))
+ memset(p, 0, len);
+ return p;
+}
+
+static void rpl_set_string_and_len(MARIADB_STRING *s,
+ unsigned char *buffer,
+ size_t len)
+{
+ if (!buffer || !len)
+ {
+ s->length= 0;
+ return;
+ }
+ s->str= (char *)buffer;
+ s->length= len;
+}
+
+static uint8_t rpl_alloc_set_string_and_len(MARIADB_RPL_EVENT *event,
+ MARIADB_STRING *s,
+ void *buffer,
+ size_t len)
+{
+ if (!buffer || !len)
+ {
+ s->length= 0;
+ return 0;
+ }
+
+ if (!(s->str = (char *)ma_alloc_root(&event->memroot, len)))
+ return 1;
+
+ memcpy(s->str, buffer, len);
+ s->length= len;
+ return 0;
+}
+
+static uint8_t rpl_metadata_size(enum enum_field_types field_type)
+{
+ switch (field_type) {
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIMESTAMP2:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ return 1;
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_BIT:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static uint8_t ma_rpl_get_second_part(MYSQL_TIME *tm, uchar *ptr, uchar *metadata)
+{
+ switch(metadata[0])
+ {
+ case 0:
+ tm->second_part= 0;
+ return 0;
+ case 1:
+ case 2:
+ tm->second_part= (uint32_t)ptr[0] * 10000;
+ return 1;
+ case 3:
+ case 4:
+ tm->second_part= myisam_sint2korr(ptr) * 100;
+ return 2;
+ case 5:
+ case 6:
+ tm->second_part= myisam_sint3korr(ptr);
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+MARIADB_RPL_ROW * STDCALL
+mariadb_rpl_extract_rows(MARIADB_RPL *rpl,
+ MARIADB_RPL_EVENT *tm_event,
+ MARIADB_RPL_EVENT *row_event)
+{
+ uchar *start, *pos, *end;
+ MARIADB_RPL_ROW *f_row= NULL, *p_row= NULL, *c_row= NULL;
+ uint32_t column_count;
+
+ if (!rpl || !tm_event || !row_event)
+ return NULL;
+
+ if (tm_event->event_type != TABLE_MAP_EVENT || !(IS_ROW_EVENT(row_event)))
+ {
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, RPL_ERR_POS(rpl), "Event with wrong event type passed.");
+ return NULL;
+ }
+
+ if (row_event->event.rows.table_id != tm_event->event.table_map.table_id)
+ {
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, RPL_ERR_POS(rpl), "table_id in table_map event differs.");
+ return NULL;
+ }
+
+ if (!row_event->event.rows.row_data_size ||
+ !row_event->event.rows.row_data)
+ {
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, "Row event has no data.");
+ return NULL;
+ }
+
+ column_count= tm_event->event.table_map.column_count;
+
+ start= pos = row_event->event.rows.row_data;
+ end= start + row_event->event.rows.row_data_size;
+
+ while (pos < end)
+ {
+ uchar *n_bitmap;
+ uint32_t i;
+
+ uchar *metadata= (uchar *)tm_event->event.table_map.metadata.str;
+
+ if (!(c_row = (MARIADB_RPL_ROW *)ma_calloc_root(&row_event->memroot, sizeof(MARIADB_RPL_ROW))) ||
+ !(c_row->columns= (MARIADB_RPL_VALUE *)ma_calloc_root(&row_event->memroot,
+ sizeof(MARIADB_RPL_VALUE) * column_count)))
+ {
+ rpl_set_error(rpl, CR_OUT_OF_MEMORY, 0);
+ return NULL;
+ }
+
+ if (!f_row)
+ f_row= c_row;
+ if (p_row)
+ p_row->next= c_row;
+
+ c_row->column_count= column_count;
+ n_bitmap= pos;
+ pos+= (column_count + 7) / 8;
+
+ for (i= 0; i < column_count; i++)
+ {
+ MARIADB_RPL_VALUE *column= &c_row->columns[i];
+ column->field_type= (uchar)tm_event->event.table_map.column_types.str[i];
+ /* enum, set and string types are stored as string - first metadata
+ byte contains real_type, second byte contains the length */
+ if (column->field_type == MYSQL_TYPE_STRING)
+ {
+ if (metadata[0] == MYSQL_TYPE_ENUM || metadata[0] == MYSQL_TYPE_SET)
+ column->field_type = metadata[0];
+ }
+
+ if ((n_bitmap[i / 8] >> (i % 8)) & 1)
+ {
+ column->is_null= 1;
+ metadata+= rpl_metadata_size(column->field_type);
+ continue;
+ }
+ if (column->field_type == MYSQL_TYPE_BLOB)
+ {
+ switch(metadata[0])
+ {
+ case 1:
+ column->field_type= MYSQL_TYPE_TINY_BLOB;
+ break;
+ case 3:
+ column->field_type= MYSQL_TYPE_MEDIUM_BLOB;
+ break;
+ case 4:
+ column->field_type= MYSQL_TYPE_LONG_BLOB;
+ break;
+ default:
+ break;
+ }
+ }
+ switch (column->field_type) {
+ case MYSQL_TYPE_TINY:
+ column->val.ll= sint1korr(pos);
+ column->val.ull= uint1korr(pos);
+ pos++;
+ break;
+ case MYSQL_TYPE_YEAR:
+ column->val.ull= uint1korr(pos++) + 1900;
+ break;
+ case MYSQL_TYPE_SHORT:
+ column->val.ll= sint2korr(pos);
+ column->val.ull= uint2korr(pos);
+ pos+= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ column->val.ll= sint3korr(pos);
+ column->val.ull= uint3korr(pos);
+ pos+= 3;
+ break;
+ case MYSQL_TYPE_LONG:
+ column->val.ll= sint4korr(pos);
+ column->val.ull= uint4korr(pos);
+ pos+= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ column->val.ll= sint8korr(pos);
+ column->val.ull= uint8korr(pos);
+ pos+= 8;
+ break;
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ uint8_t precision= *metadata++;
+ uint8_t scale= *metadata++;
+ uint32_t bin_size;
+ decimal dec;
+ char str[200];
+ char buf[100];
+ int s_len= sizeof(str) - 1;
+
+ dec.buf= (void *)buf;
+ dec.len= sizeof(buf) / sizeof(decimal_digit);
+
+ bin_size= decimal_bin_size(precision, scale);
+ bin2decimal((char *)pos, &dec, precision, scale);
+ decimal2string(&dec, str, &s_len);
+ pos+= bin_size;
+
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, str, s_len))
+ goto mem_error;
+
+ break;
+ }
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ {
+ uint8_t flen= *metadata++;
+ if (flen == 4)
+ {
+ float4get(column->val.f, pos);
+ }
+ if (flen == 8)
+ {
+ float8get(column->val.d, pos);
+ }
+ pos+= flen;
+ break;
+ }
+ case MYSQL_TYPE_BIT:
+ {
+ uint8_t num_bits= (metadata[0] & 0xFF) + metadata[1] * 8;
+ uint8_t b_len= (num_bits + 7) / 8;
+ metadata+= 2;
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, pos, b_len))
+ goto mem_error;
+ pos+= b_len;
+ break;
+ }
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ column->val.ull= uint4korr(pos);
+ pos+= 4;
+ break;
+ }
+ case MYSQL_TYPE_TIMESTAMP2:
+ {
+ char tmp[20];
+ uint32_t p1= uint4korr(pos);
+ uint8_t f_len= *metadata++;
+ uint32_t p2;
+ pos+= 4;
+ p2= (uint32_t)uintNkorr(f_len, pos);
+ pos+= f_len;
+ sprintf(tmp, "%d.%d", p1, p2);
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, tmp, strlen(tmp)))
+ goto mem_error;
+ break;
+ }
+ case MYSQL_TYPE_DATE:
+ {
+ MYSQL_TIME *tm= &column->val.tm;
+ uint32_t d_val= uint3korr(pos);
+ pos+= 3;
+ tm->year= (int)(d_val / (16 * 32));
+ tm->month= (int)(d_val / 32 % 16);
+ tm->day= d_val % 32;
+ tm->time_type= MYSQL_TIMESTAMP_DATE;
+ break;
+ }
+ case MYSQL_TYPE_TIME2:
+ {
+ MYSQL_TIME *tm= &column->val.tm;
+ int64_t t_val= myisam_uint3korr(pos) - 0x800000LL;
+
+ if ((tm->neg = t_val < 0))
+ t_val= -t_val;
+
+ pos+= 3;
+ tm->hour= (t_val >> 12) % (1 << 10);
+ tm->minute= (t_val >> 6) % (1 << 6);
+ tm->second= t_val % (1 << 6);
+
+ pos+= ma_rpl_get_second_part(tm, pos, metadata);
+ metadata++;
+ tm->time_type= MYSQL_TIMESTAMP_TIME;
+ column->field_type= MYSQL_TYPE_TIME;
+ break;
+ }
+ case MYSQL_TYPE_DATETIME2:
+ {
+ MYSQL_TIME *tm= &column->val.tm;
+ uint64_t dt_val= mi_uint5korr(pos) - 0x8000000000LL,
+ date_part, time_part;
+ pos+= 5;
+
+ date_part= dt_val >> 17;
+ time_part= dt_val % (1 << 17);
+
+ tm->day= (unsigned int)date_part % (1 << 5);
+ tm->month= (unsigned int)(date_part >> 5) % 13;
+ tm->year= (unsigned int)(date_part >> 5) / 13;
+
+ tm->second= time_part % (1 << 6);
+ tm->minute= (time_part >> 6) % (1 << 6);
+ tm->hour= (uint32_t)(time_part >> 12);
+
+ tm->time_type= MYSQL_TIMESTAMP_DATETIME;
+ column->field_type= MYSQL_TYPE_DATETIME;
+
+ pos+= ma_rpl_get_second_part(tm, pos, metadata);
+ metadata++;
+ break;
+ }
+ case MYSQL_TYPE_STRING:
+ {
+ uint8_t s_len= metadata[2];
+ metadata+= 2;
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, pos, s_len))
+ goto mem_error;
+ pos+= s_len;
+ break;
+ }
+ case MYSQL_TYPE_ENUM:
+ {
+ uint8_t e_len= metadata[2];
+ metadata+= 2;
+ column->val.ull= uintNkorr(e_len, pos);
+ pos+= e_len;
+ break;
+ }
+ case MYSQL_TYPE_SET:
+ {
+ uint8_t e_len= metadata[2];
+ metadata+= 2;
+ column->val.ull= uintNkorr(e_len, pos);
+ pos+= e_len;
+ break;
+ }
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ {
+ uint8_t h_len= *metadata++;
+ uint64_t b_len= uintNkorr(h_len, pos);
+ pos+= h_len;
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, pos, (size_t)b_len))
+ goto mem_error;
+ pos+= b_len;
+ break;
+ }
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ {
+ uint32_t s_len= uint2korr(metadata);
+ uint8_t byte_len= rpl_byte_size(s_len);
+ metadata+= 2;
+ s_len= (uint32_t)uintNkorr(byte_len, pos);
+ pos+= byte_len;
+ if (rpl_alloc_set_string_and_len(row_event, &column->val.str, pos, s_len))
+ goto mem_error;
+ pos+= s_len;
+ break;
+ }
+ case MYSQL_TYPE_TIME:
+ {
+ MYSQL_TIME *tm= &column->val.tm;
+ uint64_t t= uint8korr(pos);
+ pos+= 8;
+ tm->hour= (unsigned int)(t/100)/100;
+ tm->minute= (unsigned int)(t/100) % 100;
+ tm->second= (unsigned int)t % 100;
+ tm->time_type= MYSQL_TIMESTAMP_TIME;
+ break;
+ }
+ case MYSQL_TYPE_DATETIME:
+ {
+ MYSQL_TIME *tm= &column->val.tm;
+ uint64_t t= uint8korr(pos);
+ uint32_t d_val= (uint32_t)t / 1000000,
+ t_val= (uint32_t)t % 1000000;
+ pos+= 8;
+ tm->year= (unsigned int)(d_val / 100) / 100;
+ tm->month= (unsigned int)(d_val / 100) % 100;
+ tm->day= (unsigned int)d_val % 100;
+ tm->hour= (t_val/100)/100;
+ tm->minute= (t_val/100) % 100;
+ tm->second= t_val % 100;
+ tm->time_type= MYSQL_TIMESTAMP_DATETIME;
+ break;
+ }
+
+
+ default:
+ break;
+ }
+ }
+ p_row= c_row;
+ }
+ return f_row;
+
+mem_error:
+ rpl_set_error(rpl, CR_OUT_OF_MEMORY, 0);
+ return NULL;
+}
+
+MARIADB_RPL * STDCALL mariadb_rpl_init_ex(MYSQL *mysql, unsigned int version)
+{
+ MARIADB_RPL *rpl;
+
+ if (version < MARIADB_RPL_REQUIRED_VERSION ||
+ version > MARIADB_RPL_VERSION)
+ {
+ if (mysql)
+ my_set_error(mysql, CR_VERSION_MISMATCH, SQLSTATE_UNKNOWN, 0, version,
+ MARIADB_RPL_VERSION, MARIADB_RPL_REQUIRED_VERSION);
+ return 0;
+ }
+
+ if (!(rpl= (MARIADB_RPL *)calloc(1, sizeof(MARIADB_RPL))))
+ {
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return 0;
+ }
+ rpl->version= version;
+
+ if ((rpl->mysql= mysql))
+ {
+ MYSQL_RES *result;
+ if (!mysql_query(mysql, "select @@binlog_checksum"))
+ {
+ if ((result= mysql_store_result(mysql)))
+ {
+ MYSQL_ROW row= mysql_fetch_row(result);
+ if (!strcmp(row[0], "CRC32"))
+ {
+ rpl->artificial_checksum= 1;
+ }
+ mysql_free_result(result);
+ }
+ }
+ }
+
+ /* recommended way to set replica host and port is via rpl_optionsv(), however if
+ hostname and port was set via mysql_optionsv, we set it here to rpl */
+ if (rpl->mysql && rpl->mysql->options.extension && rpl->mysql->options.extension->rpl_host)
+ {
+ mariadb_rpl_optionsv(rpl, MARIADB_RPL_HOST, rpl->mysql->options.extension->rpl_host);
+ mariadb_rpl_optionsv(rpl, MARIADB_RPL_PORT, rpl->mysql->options.extension->rpl_port);
+ }
+ return rpl;
+}
+
+void STDCALL mariadb_free_rpl_event(MARIADB_RPL_EVENT *event)
+{
+ if (event)
+ {
+ ma_free_root(&event->memroot, MYF(0));
+ free(event);
+ }
+}
+
+int STDCALL mariadb_rpl_open(MARIADB_RPL *rpl)
+{
+ unsigned char *ptr, *buf;
+
+ if (!rpl)
+ return 1;
+
+ /* COM_BINLOG_DUMP:
+ Ofs Len Data
+ 0 1 COM_BINLOG_DUMP
+ 1 4 position
+ 5 2 flags
+ 7 4 server id
+ 11 * filename
+
+ * = filename length
+
+ */
+ rpl_clear_error(rpl);
+
+ /* if replica was specified, we will register replica via
+ COM_REGISTER_SLAVE */
+ if (rpl->host)
+ {
+ /* Protocol:
+ Ofs Len Data
+ 0 1 COM_REGISTER_SLAVE
+ 1 4 server id
+ 5 1 replica host name length
+ 6 <n> replica host name
+ 1 user name length
+ <n> user name
+ 1 password length
+ <n> password
+ 2 replica port
+ 4 replication rank (unused)
+ 4 source server id (unused)
+ */
+ unsigned char *p, buffer[1024];
+ size_t len= MIN(strlen(rpl->host), 255);
+
+ p= buffer;
+ int4store(p, rpl->server_id);
+ p+= 4;
+ *p++= (unsigned char)len;
+ memcpy(p, rpl->host, len);
+ p+= len;
+
+ /* Don't send user, password, rank and server_id */
+ *p++= 0;
+ *p++= 0;
+ int2store(p, rpl->port);
+ p+= 2;
+
+ int4store(p, 0);
+ p+= 4;
+ int4store(p, 0);
+ p+= 4;
+
+ if (ma_simple_command(rpl->mysql, COM_REGISTER_SLAVE, (const char *)buffer, p - buffer, 0, 0))
+ {
+ rpl_set_error(rpl, mysql_errno(rpl->mysql), 0, NULL, 0);
+ return 1;
+ }
+ }
+
+ if (rpl->mysql)
+ {
+ uint32_t replica_id= rpl->server_id;
+ ptr= buf= (unsigned char *)alloca(rpl->filename_length + 11);
+
+ if (!ptr)
+ {
+ rpl_set_error(rpl, CR_OUT_OF_MEMORY, 0);
+ }
+
+ if (rpl->is_semi_sync)
+ {
+ if (mysql_query(rpl->mysql, "SET @rpl_semi_sync_slave=1"))
+ {
+ rpl_set_error(rpl, mysql_errno(rpl->mysql), 0, mysql_error(rpl->mysql));
+ return 1;
+ }
+ }
+ else {
+ MYSQL_RES* result;
+ MYSQL_ROW row;
+ if (mysql_query(rpl->mysql, "SELECT @rpl_semi_sync_slave=1"))
+ {
+ rpl_set_error(rpl, mysql_errno(rpl->mysql), 0, mysql_error(rpl->mysql));
+ return 1;
+ }
+ if ((result = mysql_store_result(rpl->mysql)))
+ {
+ if ((row = mysql_fetch_row(result)))
+ rpl->is_semi_sync = (row[0] != NULL && row[0][0] == '1');
+ mysql_free_result(result);
+ }
+ }
+
+ int4store(ptr, (unsigned int)rpl->start_position);
+ ptr+= 4;
+ int2store(ptr, rpl->flags);
+ ptr+= 2;
+ if ((rpl->flags & MARIADB_RPL_BINLOG_DUMP_NON_BLOCK) && !replica_id)
+ replica_id= 1;
+ int4store(ptr, replica_id);
+ ptr+= 4;
+ memcpy(ptr, rpl->filename, rpl->filename_length);
+ ptr+= rpl->filename_length;
+
+ return (ma_simple_command(rpl->mysql, COM_BINLOG_DUMP, (const char *)buf, ptr - buf, 1, 0));
+ } else
+ {
+ char *buf[RPL_BINLOG_MAGIC_SIZE];
+ MYSQL mysql;
+
+ /* Semi sync doesn't work when processing files */
+ rpl->is_semi_sync = 0;
+
+ if (rpl->fp)
+ ma_close(rpl->fp);
+
+ if (!(rpl->fp= ma_open((const char *)rpl->filename, "r", &mysql)))
+ {
+ rpl_set_error(rpl, CR_FILE_NOT_FOUND, 0, rpl->filename, errno);
+ return errno;
+ }
+
+ if (ma_read(buf, 1, RPL_BINLOG_MAGIC_SIZE, rpl->fp) != 4)
+ {
+ rpl_set_error(rpl, CR_FILE_READ, 0, rpl->filename, errno);
+ return errno;
+ }
+
+ /* check if it is a valid binlog file */
+ if (memcmp(buf, RPL_BINLOG_MAGIC, RPL_BINLOG_MAGIC_SIZE) != 0)
+ {
+ rpl_set_error(rpl, CR_BINLOG_INVALID_FILE, 0, rpl->filename, errno);
+ return errno;
+ }
+
+ return 0;
+ }
+}
+
+static int ma_set_rpl_filename(MARIADB_RPL *rpl, const unsigned char *filename, size_t len)
+{
+ if (!rpl)
+ return 1;
+ free(rpl->filename);
+ if (!(rpl->filename= (char *)malloc(len)))
+ return 1;
+ memcpy(rpl->filename, filename, len);
+ rpl->filename_length= (uint32_t)len;
+ return 0;
+}
+
+/*
+ * Returns compression info:
+ * Ofs Len
+ * 0 1 header:
+ * ofs & 0x07 >> 4: algorithm, always 0=zlib
+ * ofs & 0x07: header size
+ * 1 header size uncompressed length in MyISAM format.
+ */
+static uint32_t get_compression_info(const unsigned char *buf,
+ uint8_t *algorithm,
+ uint8_t *header_size)
+{
+ uint8_t alg, header;
+ uint32 len= 0;
+
+ if (!algorithm)
+ algorithm= &alg;
+ if (!header_size)
+ header_size= &header;
+
+ *header_size= 0;
+ *algorithm= 0;
+
+ if (!buf)
+ return len;
+
+ if ((buf[0] & 0xe0) != 0x80)
+ return len;
+
+ *header_size= buf[0] & 0x07;
+ *algorithm = (buf[0] & 0x07) >> 4;
+
+ buf++;
+
+ /* Attention: we can't use uint*korr, here, we need myisam macros since
+ length is stored in high byte first order
+ */
+ switch(*header_size) {
+ case 1:
+ len= *buf;
+ break;
+ case 2:
+ len= myisam_uint2korr(buf);
+ break;
+ case 3:
+ len= myisam_uint3korr(buf);
+ break;
+ case 4:
+ len= myisam_uint4korr(buf);
+ break;
+ default:
+ len= 0;
+ break;
+ }
+
+ *header_size += 1;
+ return len;
+}
+
+static uint8_t mariadb_rpl_send_semisync_ack(MARIADB_RPL* rpl, MARIADB_RPL_EVENT* event)
+{
+ size_t buf_size = 0;
+ uchar* buf;
+
+ if (!rpl)
+ return 1;
+
+ if (!event)
+ {
+ rpl_set_error(rpl, CR_BINLOG_SEMI_SYNC_ERROR, 0, "Invalid event");
+ return 1;
+ }
+
+ if (!rpl->is_semi_sync)
+ {
+ rpl_set_error(rpl, CR_BINLOG_SEMI_SYNC_ERROR, 0, "semi synchronous replication is not enabled");
+ return 1;
+ }
+ if (!event->is_semi_sync || (event->semi_sync_flags != SEMI_SYNC_ACK_REQ))
+ {
+ rpl_set_error(rpl, CR_BINLOG_SEMI_SYNC_ERROR, 0, "This event doesn't require to send semi synchronous acknoledgement");
+ return 1;
+ }
+
+ buf_size = rpl->filename_length + 9;
+ buf = alloca(buf_size);
+
+ buf[0] = SEMI_SYNC_INDICATOR;
+ int8store(buf + 1, (uint64_t)event->next_event_pos);
+ memcpy(buf + 9, rpl->filename, rpl->filename_length);
+
+ ma_net_clear(&rpl->mysql->net);
+
+ if (ma_net_write(&rpl->mysql->net, buf, buf_size) ||
+ (ma_net_flush(&rpl->mysql->net)))
+ {
+ rpl_set_error(rpl, CR_CONNECTION_ERROR, 0);
+ return 1;
+ }
+
+ return 0;
+}
+
+MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVENT *event)
+{
+ unsigned char *ev= 0;
+ unsigned char *checksum_start= 0;
+ unsigned char *ev_start= 0;
+ unsigned char *ev_end= 0;
+ size_t len= 0;
+ MARIADB_RPL_EVENT *rpl_event= 0;
+
+ if (!rpl || (!rpl->mysql && !rpl->fp))
+ return 0;
+
+ if (event)
+ {
+ MA_MEM_ROOT memroot= event->memroot;
+ rpl_event= event;
+ ma_free_root(&memroot, MYF(MY_KEEP_PREALLOC));
+ memset(rpl_event, 0, sizeof(MARIADB_RPL_EVENT));
+ rpl_event->memroot= memroot;
+ } else {
+ if (!(rpl_event = (MARIADB_RPL_EVENT *)malloc(sizeof(MARIADB_RPL_EVENT))))
+ goto mem_error;
+ memset(rpl_event, 0, sizeof(MARIADB_RPL_EVENT));
+ ma_init_alloc_root(&rpl_event->memroot, 8192, 0);
+ }
+
+ rpl_event->rpl= rpl;
+
+ while (1) {
+ unsigned long pkt_len;
+
+ if (rpl->mysql)
+ {
+ pkt_len= ma_net_safe_read(rpl->mysql);
+
+ if (pkt_len == packet_error)
+ {
+ mariadb_free_rpl_event(rpl_event);
+ return 0;
+ }
+
+ /* EOF packet:
+ see https://mariadb.com/kb/en/library/eof_packet/
+ Packet length must be less than 9 bytes, EOF header
+ is 0xFE.
+ */
+ if (pkt_len < 9 && rpl->mysql->net.read_pos[0] == 0xFE)
+ {
+ mariadb_free_rpl_event(rpl_event);
+ return 0;
+ }
+
+ /* if ignore heartbeat flag was set, we ignore this
+ record and continue to fetch next record.
+ The first byte is always status byte (0x00)
+ For event header description see
+ https://mariadb.com/kb/en/library/2-binlog-event-header/ */
+ if (rpl->flags & MARIADB_RPL_IGNORE_HEARTBEAT)
+ {
+ if (rpl->mysql->net.read_pos[1 + 4] == HEARTBEAT_LOG_EVENT)
+ continue;
+ }
+
+ if (!(rpl_event->raw_data= ma_alloc_root(&rpl_event->memroot, pkt_len)))
+ goto mem_error;
+
+ rpl_event->raw_data_size= pkt_len;
+ memcpy(rpl_event->raw_data, rpl->mysql->net.read_pos, pkt_len);
+ ev= rpl_event->raw_data;
+ } else if (rpl->fp) {
+ char buf[EVENT_HEADER_OFS]; /* header */
+ size_t rc;
+ uint32_t len= 0;
+ char *p= buf;
+
+ if (ma_feof(rpl->fp))
+ {
+ return NULL;
+ }
+
+ memset(buf, 0, EVENT_HEADER_OFS);
+ if ((rc= ma_read(buf, 1, EVENT_HEADER_OFS - 1, rpl->fp)) != EVENT_HEADER_OFS - 1)
+ {
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, "Can't read event header");
+ mariadb_free_rpl_event(rpl_event);
+ return NULL;
+ }
+ len= uint4korr(p + 9);
+
+ if (!(rpl_event->raw_data= ma_alloc_root(&rpl_event->memroot, len)))
+ {
+ rpl_set_error(rpl, CR_OUT_OF_MEMORY, 0);
+ mariadb_free_rpl_event(rpl_event);
+ return NULL;
+ }
+
+ rpl_event->raw_data_size= len;
+ memcpy(rpl_event->raw_data, buf, EVENT_HEADER_OFS - 1);
+ len-= (EVENT_HEADER_OFS - 1);
+ rc= ma_read(rpl_event->raw_data + EVENT_HEADER_OFS - 1, 1, len, rpl->fp);
+ if (rc != len)
+ {
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, "Error while reading post header");
+ mariadb_free_rpl_event(rpl_event);
+ return NULL;
+ }
+ ev= rpl_event->raw_data;
+
+ /* We don't decrypt yet */
+ if (rpl->encrypted) {
+ return rpl_event;
+ }
+ }
+
+ ev_end= rpl_event->raw_data + rpl_event->raw_data_size;
+
+
+ if (rpl->mysql)
+ {
+ RPL_CHECK_POS(ev, ev_end, 1);
+ rpl_event->ok= *ev++;
+
+ /* CONC-470: add support for semi snychronous replication */
+ if (rpl->is_semi_sync && (rpl_event->is_semi_sync= (*ev == SEMI_SYNC_INDICATOR)))
+ {
+ RPL_CHECK_POS(ev, ev_end, 1);
+ ev++;
+ rpl_event->semi_sync_flags= *ev++;
+ }
+ }
+ rpl_event->raw_data_ofs= ev - rpl_event->raw_data;
+
+ /* check sum verification:
+ check sum will be calculated from begin of binlog header
+ */
+ checksum_start= ev;
+
+ /******************************************************************
+ Binlog event header:
+
+ All binary log events have the same header:
+ - uint32_t timestamp: creation time
+ - uint8_t event_type: type code of the event
+ - uint32_t server_id: server which created the event
+ - uint32_t event_len: length of the event. If checksum is
+ enabled, the length also include 4 bytes
+ of checksum
+ ------------- if START_ENCRYPTION_EVENT was sent, ---------------
+ encrypted part starts here:
+ - uint32_t next_pos: Position of next binary log event
+ - uint16_t flags: flags
+
+ The size of binlog event header must match the header size returned
+ by FORMAT_DESCIPTION_EVENT. In version 4 it is always 19.
+ ********************************************************************/
+ RPL_CHECK_POS(ev, ev_end, RPL_EVENT_HEADER_SIZE);
+ rpl_event->timestamp= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event_type= (unsigned char)*ev++;
+ rpl_event->server_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event_length= uint4korr(ev);
+ ev+= 4;
+ rpl_event->next_event_pos= uint4korr(ev);
+ ev+= 4;
+ rpl_event->flags= uint2korr(ev);
+ ev+=2;
+ rpl_event->checksum= 0;
+
+ /* start of post_header */
+ ev_start= ev;
+
+ DBUG_ASSERT(rpl_event->event_type < ENUM_END_EVENT);
+
+ switch(rpl_event->event_type) {
+ case UNKNOWN_EVENT:
+ case SLAVE_EVENT:
+ return rpl_event;
+ break;
+ case HEARTBEAT_LOG_EVENT:
+ /* no post header size */
+ RPL_CHECK_POS(ev, ev_end, 11);
+ rpl_event->event.heartbeat.timestamp= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.heartbeat.next_position= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.heartbeat.type= (uint8_t)*ev;
+ ev+= 1;
+ rpl_event->event.heartbeat.flags= uint2korr(ev);
+ ev+= 2;
+
+ break;
+
+ case BEGIN_LOAD_QUERY_EVENT:
+ /* check post header size */
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, BEGIN_LOAD_QUERY_EVENT);
+ rpl_event->event.begin_load_query.file_id= uint4korr(ev);
+ ev+= 4;
+
+ /* Payload: query_data (zero terminated) */
+ RPL_CHECK_NULL_POS(ev, ev_end);
+ rpl_event->event.begin_load_query.data= ev;
+ RPL_CHECK_NULL_POS(ev, ev_end);
+ ev+= strlen((char *)ev);
+ /* terminating zero */
+ RPL_CHECK_POS(ev, ev_end, 1);
+ ev++;
+ break;
+
+ case START_ENCRYPTION_EVENT:
+ /* Post header */
+ RPL_CHECK_POS(ev, ev_end, 17);
+ rpl_event->event.start_encryption.scheme= *ev++;
+ rpl_event->event.start_encryption.key_version= uint4korr(ev);
+ ev+= 4;
+ memcpy(rpl_event->event.start_encryption.nonce, ev, 12);
+ memcpy(rpl->nonce, ev, 12);
+ ev+= 12;
+ rpl->encrypted= 1;
+ break;
+
+ case EXECUTE_LOAD_QUERY_EVENT:
+ {
+ uint16_t status_len;
+ uint8_t schema_len;
+
+ /* Post header */
+ RPL_CHECK_POS(ev, ev_end, rpl->post_header_len[EXECUTE_LOAD_QUERY_EVENT - 1]);
+ rpl_event->event.execute_load_query.thread_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.execute_load_query.execution_time= uint4korr(ev);
+ ev+= 4;
+ schema_len= *ev++;
+ rpl_event->event.execute_load_query.error_code= uint2korr(ev);
+ ev+= 2;
+ status_len= uint2korr(ev);
+ ev+= 2;
+ rpl_event->event.execute_load_query.file_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.execute_load_query.ofs1= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.execute_load_query.ofs2= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.execute_load_query.duplicate_flag= *ev++;
+
+ /* Payload:
+ - status variables
+ - query schema
+ - statement */
+ RPL_CHECK_POS(ev, ev_end, status_len);
+ rpl_set_string_and_len(&rpl_event->event.execute_load_query.status_vars, ev, status_len);
+ ev+= status_len;
+ RPL_CHECK_POS(ev, ev_end, schema_len);
+ rpl_set_string_and_len(&rpl_event->event.execute_load_query.schema, ev, schema_len);
+ /* terminating zero */
+ RPL_CHECK_POS(ev, ev_end, 1);
+ ev+= (schema_len + 1);
+
+ len= rpl_event->event_length - (ev - ev_start) - (rpl->use_checksum ? 4 : 0) - (EVENT_HEADER_OFS - 1);
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.execute_load_query.statement, ev, len);
+ ev+= len;
+ break;
+ }
+ case BINLOG_CHECKPOINT_EVENT:
+ /* Post header */
+ RPL_CHECK_POS(ev, ev_end, rpl->post_header_len[BINLOG_CHECKPOINT_EVENT - 1]);
+ len= uint4korr(ev);
+ ev+= 4;
+
+ /* payload: filename */
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.checkpoint.filename, ev, len);
+ if (ma_set_rpl_filename(rpl, ev, len))
+ goto mem_error;
+ ev+= len;
+ break;
+
+ case FORMAT_DESCRIPTION_EVENT:
+ /*
+ FORMAT_DESCRIPTION_EVENT:
+
+ Header:
+ uint<2> binary log version
+ (we support only version 4)
+ str<50> server version, right padded with \0
+ uint<4> timestamp <redundant>
+ uint<1> header length
+ byte<n> post header lengths. Length can be calculated by
+ ev_end - end - 1 - 4
+ uint<1> check sum algorithm byte
+ uint<4> CRC32 checksum
+ */
+
+
+ /* We don't speak bing log protocol version < 4, in case it's an older
+ protocol version an error will be returned. */
+ RPL_CHECK_POS(ev, ev_end, 57);
+ if ((rpl_event->event.format_description.format = uint2korr(ev)) < 4)
+ {
+ mariadb_free_rpl_event(rpl_event);
+ rpl_set_error(rpl, CR_ERR_UNSUPPORTED_BINLOG_FORMAT, SQLSTATE_UNKNOWN, 0,
+ RPL_ERR_POS(rpl), uint2korr(ev));
+ return 0;
+ }
+
+ ev+= 2;
+ rpl_event->event.format_description.server_version = (char *)(ev);
+ ev+= 50;
+ rpl_event->event.format_description.timestamp= uint4korr(ev);
+ ev+= 4;
+ rpl->fd_header_len= rpl_event->event.format_description.header_len= *ev;
+ ev+= 1;
+ /*Post header lengths: 1 byte for each event, non used events/gaps in enum should
+ have a zero value */
+ len= ev_end - ev - 5;
+ rpl_set_string_and_len(&rpl_event->event.format_description.post_header_lengths, ev, len);
+ memset(rpl->post_header_len, 0, ENUM_END_EVENT);
+ memcpy(rpl->post_header_len, rpl_event->event.format_description.post_header_lengths.str,
+ MIN(len, ENUM_END_EVENT));
+
+ ev+= len;
+ RPL_CHECK_POS(ev, ev_end, 5);
+ if ((rpl->use_checksum= *ev++))
+ {
+ rpl_event->checksum= uint4korr(ev);
+ ev+= 4;
+ }
+ break;
+
+ case QUERY_COMPRESSED_EVENT:
+ case QUERY_EVENT:
+ {
+ size_t db_len, status_len;
+
+ /***********
+ post_header
+ ***********/
+ RPL_CHECK_POS(ev, ev_end, rpl->post_header_len[rpl_event->event_type - 1]);
+ rpl_event->event.query.thread_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.query.seconds= uint4korr(ev);
+ ev+= 4;
+ db_len= *ev;
+ ev++;
+ rpl_event->event.query.errornr= uint2korr(ev);
+ ev+= 2;
+ status_len= uint2korr(ev);
+ ev+= 2;
+
+ /*******
+ payload
+ ******/
+ RPL_CHECK_POS(ev, ev_end, status_len + db_len + 1);
+ rpl_set_string_and_len(&rpl_event->event.query.status, ev, status_len);
+ ev+= status_len;
+
+ rpl_set_string_and_len(&rpl_event->event.query.database, ev, db_len);
+ ev+= db_len + 1; /* zero terminated */
+
+ len= rpl_event->event_length - (ev - ev_start) - (rpl->use_checksum ? 4 : 0) - (EVENT_HEADER_OFS - 1);
+ RPL_CHECK_POS(ev, ev_end, len);
+
+ if (rpl_event->event_type == QUERY_EVENT || !rpl->uncompress) {
+ rpl_set_string_and_len(&rpl_event->event.query.statement, ev, len);
+ }
+ else if (rpl_event->event_type == QUERY_COMPRESSED_EVENT)
+ {
+ uint8_t header_size= 0,
+ algorithm= 0;
+
+ uint32_t uncompressed_len= get_compression_info(ev, &algorithm, &header_size);
+
+ len-= header_size;
+ if (!(rpl_event->event.query.statement.str = ma_calloc_root(&rpl_event->memroot, uncompressed_len)))
+ goto mem_error;
+
+ if ((uncompress((Bytef*)rpl_event->event.query.statement.str, (uLongf *)&uncompressed_len,
+ (Bytef*)ev + header_size, (uLongf)*&len) != Z_OK))
+ {
+ mariadb_free_rpl_event(rpl_event);
+ rpl_set_error(rpl, CR_ERR_BINLOG_UNCOMPRESS, SQLSTATE_UNKNOWN, RPL_ERR_POS(rpl));
+ return 0;
+ }
+ rpl_event->event.query.statement.length= uncompressed_len;
+ }
+ break;
+ }
+ case TABLE_MAP_EVENT:
+ {
+ /*
+ TABLE_MAP_EVENT:
+
+ Header:
+ uint<6> table_id
+ uint<2> unused
+
+ Payload:
+ uint<1> schema_name length
+ str<NULL> schema_name (zero terminated)
+ uint<1> table_name length
+ str<NULL> table_name (zero terminated)
+ int<lenc> column_count
+ byte<n> column_types[column_count], 1 byte for
+ each column
+ int<lenc> meta_data_size
+ byte<n> netadata{metadata_size]
+ byte<n> bit fields, indicating which column can be null
+ n= (column_count + 7) / 8;
+
+ if (remaining_bytes)
+ byte<n> optional metadata
+ */
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, TABLE_MAP_EVENT);
+
+ /* Post header */
+ rpl_event->event.table_map.table_id= uint6korr(ev);
+ ev+= 8; /* 2 byte in header ignored */
+
+ /* Payload */
+ RPL_CHECK_POS(ev, ev_end, 1);
+ len= *ev++;
+ RPL_CHECK_POS(ev, ev_end, len + 1);
+
+ rpl_set_string_and_len(&rpl_event->event.table_map.database, ev, len);
+ ev+= len + 1; /* Zero terminated */
+
+ RPL_CHECK_POS(ev, ev_end, 1);
+ len= *ev++;
+ RPL_CHECK_POS(ev, ev_end, len + 1);
+ rpl_set_string_and_len(&rpl_event->event.table_map.table, ev, len);
+ ev+= len + 1; /* Zero terminated */
+
+ RPL_CHECK_FIELD_LENGTH(ev, ev_end);
+ len= rpl_event->event.table_map.column_count= mysql_net_field_length(&ev);
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.table_map.column_types, ev, len);
+ ev+= len;
+
+ RPL_CHECK_FIELD_LENGTH(ev, ev_end);
+ len= mysql_net_field_length(&ev);
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.table_map.metadata, ev, len);
+ ev+= len;
+
+ len= (rpl_event->event.table_map.column_count + 7) / 8;
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_event->event.table_map.null_indicator= ev;
+ ev+= len;
+
+ len= ev_end - ev - (rpl->use_checksum ? 4 : 0);
+
+ if (len > 0) /* optional metadata */
+ {
+ rpl_parse_opt_metadata(rpl_event, ev, len);
+ ev+= len;
+ }
+
+ break;
+
+ case RAND_EVENT:
+ RPL_CHECK_POS(ev, ev_end, 16);
+ rpl_event->event.rand.first_seed= uint8korr(ev);
+ ev+= 8;
+ rpl_event->event.rand.second_seed= uint8korr(ev);
+ ev+= 8;
+
+ break;
+ }
+
+ case INTVAR_EVENT:
+ RPL_CHECK_POS(ev, ev_end, 9);
+ rpl_event->event.intvar.type= *ev;
+ ev++;
+ rpl_event->event.intvar.value= uint8korr(ev);
+ ev+= 8;
+ break;
+
+ case USER_VAR_EVENT:
+ RPL_CHECK_POS(ev, ev_end, 4);
+ len= uint4korr(ev);
+ ev+= 4;
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.uservar.name, ev, len);
+ ev+= len;
+ RPL_CHECK_POS(ev, ev_end, 1);
+ if (!(rpl_event->event.uservar.is_null= (uint8)*ev))
+ {
+ ev++;
+ RPL_CHECK_POS(ev, ev_end, 9);
+ rpl_event->event.uservar.type= *ev;
+ ev++;
+ rpl_event->event.uservar.charset_nr= uint4korr(ev);
+ ev+= 4;
+ len= uint4korr(ev);
+ ev+= 4;
+ RPL_CHECK_POS(ev, ev_end, len);
+
+ if (rpl_event->event.uservar.type == DECIMAL_RESULT)
+ {
+ char str[200];
+ int s_len= sizeof(str) - 1;
+ int precision= (int)ev[0],
+ scale= (int)ev[1];
+ decimal d;
+ decimal_digit buf[10];
+ d.len= 10;
+ d.buf= buf;
+ bin2decimal((char *)(ev+2), &d, precision, scale);
+ decimal2string(&d, str, &s_len);
+ if (!(rpl_event->event.uservar.value.str =
+ (char *)ma_calloc_root(&rpl_event->memroot, s_len)))
+ goto mem_error;
+ memcpy(rpl_event->event.uservar.value.str, str, s_len);
+ rpl_event->event.uservar.value.length= s_len;
+ } else if (rpl_event->event.uservar.type == INT_RESULT)
+ {
+ uint64_t val64;
+ if (!(rpl_event->event.uservar.value.str =
+ (char *)ma_calloc_root(&rpl_event->memroot, sizeof(longlong))))
+ goto mem_error;
+ val64= uint8korr(ev);
+ memcpy(rpl_event->event.uservar.value.str, &val64, sizeof(uint64_t));
+ rpl_event->event.uservar.value.length= sizeof(uint64_t);
+ } else if (rpl_event->event.uservar.type == REAL_RESULT)
+ {
+ double d;
+ float8get(d, ev);
+ ev+= 8;
+ if (!(rpl_event->event.uservar.value.str =
+ (char *)ma_calloc_root(&rpl_event->memroot, 24)))
+ goto mem_error;
+ memset(rpl_event->event.uservar.value.str, 0, 24);
+ sprintf(rpl_event->event.uservar.value.str, "%.14g", d);
+ rpl_event->event.uservar.value.length= strlen(rpl_event->event.uservar.value.str);
+ }
+ else
+ rpl_set_string_and_len(&rpl_event->event.uservar.value, ev, len);
+ ev+= len;
+ if ((unsigned long)(ev - rpl_event->raw_data) < rpl_event->raw_data_size)
+ rpl_event->event.uservar.flags= *ev;
+ ev++;
+ }
+ break;
+
+ case ANNOTATE_ROWS_EVENT:
+ /* Payload */
+ len= ev_end - ev - (rpl->use_checksum ? 4 : 0);
+ if (len > 0)
+ rpl_set_string_and_len(&rpl_event->event.annotate_rows.statement, ev, len);
+ break;
+
+ case ROTATE_EVENT:
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, ROTATE_EVENT);
+
+ rpl_event->event.rotate.position= uint8korr(ev);
+ ev+= 8;
+
+ /* Payload */
+ len= ev_end - ev - 4;
+ if (!len)
+ goto malformed_packet;
+
+ if (rpl_event->timestamp == 0 &&
+ rpl_event->flags & LOG_EVENT_ARTIFICIAL_F)
+ {
+ if (rpl->artificial_checksum)
+ {
+ unsigned long crc= crc32(0L, Z_NULL, 0);
+ rpl_event->checksum= (uint32_t) crc32(crc, checksum_start, (uint32_t)(ev_end - checksum_start));
+ }
+ }
+ rpl_set_string_and_len(&rpl_event->event.rotate.filename, ev, len);
+ if (ma_set_rpl_filename(rpl, ev, len))
+ goto mem_error;
+
+ ev+= len;
+ break;
+
+ case XID_EVENT:
+ /*
+ XID_EVENT was generated if a transaction which modified tables was
+ committed.
+
+ Header:
+ - uint64_t transaction number
+ */
+ RPL_CHECK_POS(ev, ev_end, 8);
+
+ rpl_event->event.xid.transaction_nr= uint8korr(ev);
+ break;
+
+ case XA_PREPARE_LOG_EVENT:
+ /*
+ MySQL only!
+
+ Header:
+ uint8_t one phase commit
+ uint32_t format_id
+ uint32_t length of gtrid
+ uint32_t length of bqual
+
+ Payload:
+ char<n> xid, where n is sum of gtrid and bqual lengths
+ */
+ RPL_CHECK_POS(ev, ev_end, 13);
+
+ rpl_event->event.xa_prepare_log.one_phase= *ev;
+ ev++;
+ rpl_event->event.xa_prepare_log.format_id= uint4korr(ev);
+ ev+= 4;
+ len= rpl_event->event.xa_prepare_log.gtrid_len= uint4korr(ev);
+ ev+= 4;
+ len+= rpl_event->event.xa_prepare_log.bqual_len= uint4korr(ev);
+ ev+= 4;
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.xa_prepare_log.xid, ev, len);
+ break;
+
+ case STOP_EVENT:
+ /*
+ STOP_EVENT - server shutdown or crash. It's always the last written
+ event after shutdown or after resuming from crash.
+
+ After starting the server a new binary log file will be created, additionally
+ a ROTATE_EVENT will be appended to the old log file.
+
+ No data to process.
+ */
+ break;
+
+ case PREVIOUS_GTIDS_LOG_EVENT:
+ {
+ /*
+ PREVIOUS_GTID_LOG_EVENT (MySQL only):
+
+ 8-bytes, always zero ?!
+ */
+ ssize_t len= ev_end - ev - rpl->use_checksum * 4;
+
+ if (len)
+ {
+ rpl_event->event.previous_gtid.content.data= ev;
+ rpl_event->event.previous_gtid.content.length= len;
+ ev+= len;
+ }
+ break;
+ }
+ case ANONYMOUS_GTID_LOG_EVENT:
+ case GTID_LOG_EVENT:
+ /*
+ ANONYMOUS_GTID_LOG_EVENT
+
+ uint32_t thread_id
+
+ Header:
+ uint8_t flag: commit flag
+ byte<16> source_id: numerical representation of server's UUID
+ uint64_t sequence_nr: sequence number
+ */
+ RPL_CHECK_POS(ev, ev_end, 25);
+ rpl_event->event.gtid_log.commit_flag= *ev;
+ ev++;
+ memcpy(rpl_event->event.gtid_log.source_id, ev, 16);
+ ev+= 16;
+ rpl_event->event.gtid_log.sequence_nr= uint8korr(ev);
+ ev+= 8;
+ break;
+
+ case GTID_EVENT:
+ /*
+ GTID_EVENT (MariaDB Only):
+
+ A New transaction (BEGIN) was started, or a single transaction
+ (ddl) statement was executed. In case a single transaction was
+ executed, the FL_GROUP_COMMIT id flag is not set.
+
+ Header:
+ uint64_t sequence_nr
+ uint64_t domain_id
+ uint8_t flags
+
+ if (flags & FL_GROUP_COMMIT_D)
+ uint64_t commit_id
+ else
+ char[6] unused
+ */
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, GTID_EVENT);
+ rpl_event->event.gtid.sequence_nr= uint8korr(ev);
+ ev+= 8;
+ rpl_event->event.gtid.domain_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.gtid.flags= *ev;
+ ev++;
+ if (rpl_event->event.gtid.flags & FL_GROUP_COMMIT_ID)
+ {
+ RPL_CHECK_POS(ev, ev_end, 8);
+ rpl_event->event.gtid.commit_id= uint8korr(ev);
+ ev+= 8;
+ }
+ else if (rpl_event->event.gtid.flags & (FL_PREPARED_XA | FL_COMPLETED_XA))
+ {
+ uint16_t len;
+ RPL_CHECK_POS(ev, ev_end, 6);
+ rpl_event->event.gtid.format_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.gtid.gtrid_len= *ev;
+ ev++;
+ rpl_event->event.gtid.bqual_len= *ev;
+ ev++;
+ len= rpl_event->event.gtid.gtrid_len + rpl_event->event.gtid.bqual_len;
+ RPL_CHECK_POS(ev, ev_end, len);
+ rpl_set_string_and_len(&rpl_event->event.gtid.xid, ev, len);
+ ev+= len;
+ }
+ else
+ ev+= 6;
+ break;
+
+ case GTID_LIST_EVENT:
+ /*
+ GTID_LIST_EVENT (MariaDB only)
+
+ Logged in every binlog to record the current replication state.
+ Consists of the last GTID seen for each replication domain.
+
+ The Global Transaction ID, GTID for short, consists of three components:
+ replication domain id, server id and sequence nr
+
+ Header:
+ uint32_t gtid_cnt - number of global transaction id's
+
+ Payload:
+ for i=0; i < gtid_cnt; i++
+ uint32_t domain_id
+ uint32_t server_id
+ uint64_t sequence_nr
+ */
+
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, GTID_LIST_EVENT);
+ rpl_event->event.gtid_list.gtid_cnt= uint4korr(ev);
+ ev+=4;
+
+ RPL_CHECK_POS(ev, ev_end, rpl_event->event.gtid_list.gtid_cnt * 16);
+ /* Payload */
+ if (rpl_event->event.gtid_list.gtid_cnt)
+ {
+ uint32_t i;
+ if (!(rpl_event->event.gtid_list.gtid=
+ (MARIADB_GTID *)ma_calloc_root(&rpl_event->memroot,
+ sizeof(MARIADB_GTID) * rpl_event->event.gtid_list.gtid_cnt)))
+ goto mem_error;
+ for (i=0; i < rpl_event->event.gtid_list.gtid_cnt; i++)
+ {
+ rpl_event->event.gtid_list.gtid[i].domain_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.gtid_list.gtid[i].server_id= uint4korr(ev);
+ ev+= 4;
+ rpl_event->event.gtid_list.gtid[i].sequence_nr= uint8korr(ev);
+ ev+= 8;
+ }
+ }
+ break;
+
+ case WRITE_ROWS_COMPRESSED_EVENT_V1:
+ case UPDATE_ROWS_COMPRESSED_EVENT_V1:
+ case DELETE_ROWS_COMPRESSED_EVENT_V1:
+ case WRITE_ROWS_EVENT_V1:
+ case UPDATE_ROWS_EVENT_V1:
+ case DELETE_ROWS_EVENT_V1:
+ case WRITE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT:
+ {
+ /*
+ WRITE/UPDATE/DELETE_ROWS_EVENT_V1 (MariaDB only)
+ WRITE/UPDATE/DELETE_ROWS_EVENT_COMPRESSED_V1 (MariaDB only)
+ WRITE/UPDATE/DELETE_ROWS_EVENT (MySQL only)
+
+ ROWS events are written for row based replicatoin if data is
+ inserted, deleted or updated.
+
+ Header
+ uint<6> table_id
+ uint<2> flags
+
+ if MySQL (version 2)
+ uint<<2> extra_data_length
+ char[extra_data_length] extra_data
+
+ uint<lenenc> number of columns
+ uint8_t<n> Bitmap of columns used.
+ n= (number of columns + 7) / 8
+
+ if UPDATE_ROWS_v1 (MariaDB)
+ uint8_t<n> columns updated
+ n= (number of columns + 7) / 8
+
+ uint7_t<n> null bitmap
+ n= (number of columns + 7) / 8
+
+ str<len> Column data. If event is not compressed,
+ length must be calculated.
+
+ if UPDATE_ROWS_v1 (MariaDB)
+ byte<n> Null bitmap update
+ n= (number of columns + 7) / 8
+ str<len> Update column data
+
+ */
+
+ uint32_t bitmap_len= 0;
+
+ RPL_CHECK_POST_HEADER_LEN(ev, ev_end, rpl_event->event_type);
+
+ if (rpl_event->event_type >= WRITE_ROWS_COMPRESSED_EVENT) {
+ return rpl_event;
+ rpl_event->event.rows.compressed= 1;
+ rpl_event->event.rows.type= rpl_event->event_type - WRITE_ROWS_COMPRESSED_EVENT;
+ } else if (rpl_event->event_type >= WRITE_ROWS_COMPRESSED_EVENT_V1) {
+ rpl_event->event.rows.compressed= 1;
+ rpl_event->event.rows.type= rpl_event->event_type - WRITE_ROWS_COMPRESSED_EVENT_V1;
+ } else if (rpl_event->event_type >= WRITE_ROWS_EVENT)
+ rpl_event->event.rows.type= rpl_event->event_type - WRITE_ROWS_EVENT;
+ else
+ rpl_event->event.rows.type= rpl_event->event_type - WRITE_ROWS_EVENT_V1;
+
+ rpl_event->event.rows.table_id= uint6korr(ev);
+ ev+= 6;
+
+ rpl_event->event.rows.flags= uint2korr(ev);
+ ev+= 2;
+
+ /* payload */
+
+ /* ROWS_EVENT V2 has the extra-data field.
+ See also: https://dev.mysql.com/doc/internals/en/rows-event.html
+ */
+ if (IS_ROW_VERSION2(rpl_event->event_type))
+ {
+ RPL_CHECK_POS(ev, ev_end, 2);
+ rpl_event->event.rows.extra_data_size= uint2korr(ev);
+ ev+= 2;
+ RPL_CHECK_POS(ev, ev_end, rpl_event->event.rows.extra_data_size);
+ if (rpl_event->event.rows.extra_data_size - 2 > 0)
+ {
+ rpl_alloc_set_string_and_len(rpl_event, rpl_event->event.rows.extra_data, ev, rpl_event->event.rows.extra_data_size - 2);
+ ev+= rpl_event->event.rows.extra_data_size;
+ }
+ }
+ /* END_ROWS_EVENT_V2 */
+
+ /* number of columns */
+ RPL_CHECK_FIELD_LENGTH(ev, ev_end);
+ rpl_event->event.rows.column_count= mysql_net_field_length(&ev);
+ bitmap_len= (rpl_event->event.rows.column_count + 7) / 8;
+ DBUG_ASSERT(rpl_event->event.rows.column_count > 0);
+
+ /* columns updated bitmap */
+ RPL_CHECK_POS(ev, ev_end, bitmap_len);
+ rpl_event->event.rows.column_bitmap= ev;
+ ev+= bitmap_len;
+
+ if (rpl_event->event_type == UPDATE_ROWS_EVENT_V1 ||
+ rpl_event->event_type == UPDATE_ROWS_COMPRESSED_EVENT_V1)
+ {
+ RPL_CHECK_POS(ev, ev_end, bitmap_len);
+ rpl_event->event.rows.column_update_bitmap= ev;
+ ev+= bitmap_len;
+ }
+
+ len= ev_end - ev - (rpl->use_checksum ? 4 : 0);
+
+ if (rpl_event->event.rows.compressed)
+ {
+ uint8_t algorithm= 0, header_size= 0;
+ uint32_t uncompressed_len= get_compression_info(ev, &algorithm, &header_size);
+
+ if (!(rpl_event->event.rows.row_data = ma_calloc_root(&rpl_event->memroot, uncompressed_len)))
+ goto mem_error;
+
+ if ((uncompress((Bytef*)rpl_event->event.rows.row_data, (uLong *)&uncompressed_len,
+ (Bytef*)ev + header_size, (uLongf )len) != Z_OK))
+ {
+ rpl_set_error(rpl, CR_ERR_BINLOG_UNCOMPRESS, SQLSTATE_UNKNOWN, 0, RPL_ERR_POS(rpl));
+ mariadb_free_rpl_event(rpl_event);
+ return 0;
+ }
+ rpl_event->event.rows.row_data_size= uncompressed_len;
+ RPL_CHECK_POS(ev, ev_end, header_size + len);
+ ev+= header_size + len;
+ } else {
+ rpl_event->event.rows.row_data_size= ev_end - ev - (rpl->use_checksum ? 4 : 0);
+ if (!(rpl_event->event.rows.row_data =
+ (char *)ma_calloc_root(&rpl_event->memroot, rpl_event->event.rows.row_data_size)))
+ goto mem_error;
+ memcpy(rpl_event->event.rows.row_data, ev, rpl_event->event.rows.row_data_size);
+ }
+ break;
+ }
+ default:
+ /* We need to report an error if this event can't be ignored */
+ if (!(rpl_event->flags & LOG_EVENT_IGNORABLE_F))
+ {
+ mariadb_free_rpl_event(rpl_event);
+ rpl_set_error(rpl, CR_UNKNOWN_BINLOG_EVENT, 0, RPL_ERR_POS(rpl),
+ rpl_event->event_type);
+ return 0;
+ }
+ return rpl_event;
+ break;
+ }
+
+ /* check if we have to send acknowledgement to primary
+ when semi sync replication is used */
+ if (rpl_event->is_semi_sync &&
+ rpl_event->semi_sync_flags == SEMI_SYNC_ACK_REQ)
+ {
+ if (mariadb_rpl_send_semisync_ack(rpl, rpl_event))
+ {
+ /* ACK failed and rpl->error was set */
+ return rpl_event;
+ }
+ }
+
+ if (rpl->use_checksum && !rpl_event->checksum)
+ {
+ rpl_event->checksum= uint4korr(ev_end - 4);
+
+ if (rpl_event->checksum && rpl->verify_checksum)
+ {
+ unsigned long crc= crc32(0L, Z_NULL, 0);
+ crc= crc32(crc, checksum_start, (uint32_t)(ev_end - checksum_start - 4));
+ if (rpl_event->checksum != (uint32_t)crc)
+ {
+ rpl_set_error(rpl, CR_ERR_CHECKSUM_VERIFICATION_ERROR, SQLSTATE_UNKNOWN, 0,
+ RPL_ERR_POS(rpl),
+ rpl_event->checksum, (uint32_t)crc);
+ mariadb_free_rpl_event(rpl_event);
+ return 0;
+ }
+ }
+ }
+ return rpl_event;
+ }
+mem_error:
+ mariadb_free_rpl_event(rpl_event);
+ rpl_set_error(rpl, CR_OUT_OF_MEMORY, 0);
+ return 0;
+malformed_packet:
+ rpl_set_error(rpl, CR_BINLOG_ERROR, 0, RPL_ERR_POS(rpl),
+ "Malformed packet");
+ mariadb_free_rpl_event(rpl_event);
+ return 0;
+}
+
+void STDCALL mariadb_rpl_close(MARIADB_RPL *rpl)
+{
+ if (!rpl)
+ return;
+ free((void *)rpl->filename);
+ if (rpl->fp)
+ {
+ ma_close(rpl->fp);
+ }
+ free(rpl->host);
+ free(rpl);
+ return;
+}
+
+int STDCALL mariadb_rpl_optionsv(MARIADB_RPL *rpl,
+ enum mariadb_rpl_option option,
+ ...)
+{
+ va_list ap;
+ int rc= 0;
+
+ if (!rpl)
+ return 1;
+
+ va_start(ap, option);
+
+ switch (option) {
+ case MARIADB_RPL_FILENAME:
+ {
+ char *arg1= va_arg(ap, char *);
+ rpl->filename_length= (uint32_t)va_arg(ap, size_t);
+ free((void *)rpl->filename);
+ rpl->filename= NULL;
+ if (rpl->filename_length)
+ {
+ rpl->filename= (char *)malloc(rpl->filename_length);
+ memcpy((void *)rpl->filename, arg1, rpl->filename_length);
+ }
+ else if (arg1)
+ {
+ rpl->filename= strdup((const char *)arg1);
+ rpl->filename_length= (uint32_t)strlen(rpl->filename);
+ }
+ break;
+ }
+ case MARIADB_RPL_SERVER_ID:
+ {
+ rpl->server_id= va_arg(ap, unsigned int);
+ break;
+ }
+ case MARIADB_RPL_FLAGS:
+ {
+ rpl->flags= va_arg(ap, unsigned int);
+ break;
+ }
+ case MARIADB_RPL_START:
+ {
+ rpl->start_position= va_arg(ap, unsigned long);
+ break;
+ }
+ case MARIADB_RPL_VERIFY_CHECKSUM:
+ {
+ rpl->verify_checksum= va_arg(ap, uint32_t);
+ break;
+ }
+ case MARIADB_RPL_UNCOMPRESS:
+ {
+ rpl->uncompress= (uint8_t)va_arg(ap, uint32_t);
+ break;
+ }
+ case MARIADB_RPL_PORT:
+ {
+ rpl->port= va_arg(ap, uint32_t);
+ break;
+ }
+ case MARIADB_RPL_HOST:
+ {
+ rpl->host= strdup(va_arg(ap, char *));
+ break;
+ }
+ case MARIADB_RPL_EXTRACT_VALUES:
+ {
+ rpl->extract_values= (uint8_t)va_arg(ap, uint32_t);
+ break;
+ }
+ case MARIADB_RPL_SEMI_SYNC:
+ {
+ rpl->is_semi_sync = (uint8_t)va_arg(ap, uint32_t);
+ break;
+ }
+ default:
+ rc= -1;
+ goto end;
+ }
+end:
+ va_end(ap);
+ return rc;
+}
+
+int STDCALL mariadb_rpl_get_optionsv(MARIADB_RPL *rpl,
+ enum mariadb_rpl_option option,
+ ...)
+{
+ va_list ap;
+
+ if (!rpl)
+ return 1;
+
+ va_start(ap, option);
+
+ switch (option) {
+ case MARIADB_RPL_FILENAME:
+ {
+ const char **name= (const char **)va_arg(ap, char **);
+ size_t *len= (size_t*)va_arg(ap, size_t *);
+
+ *name= rpl->filename;
+ *len= rpl->filename_length;
+ break;
+ }
+ case MARIADB_RPL_SERVER_ID:
+ {
+ unsigned int *id= va_arg(ap, unsigned int *);
+ *id= rpl->server_id;
+ break;
+ }
+ case MARIADB_RPL_FLAGS:
+ {
+ unsigned int *flags= va_arg(ap, unsigned int *);
+ *flags= rpl->flags;
+ break;
+ }
+ case MARIADB_RPL_START:
+ {
+ unsigned long *start= va_arg(ap, unsigned long *);
+ *start= rpl->start_position;
+ break;
+ }
+ case MARIADB_RPL_SEMI_SYNC:
+ {
+ unsigned int* semi_sync = va_arg(ap, unsigned int*);
+ *semi_sync = rpl->is_semi_sync;
+ break;
+ }
+
+ default:
+ va_end(ap);
+ return 1;
+ break;
+ }
+ va_end(ap);
+ return 0;
+}
diff --git a/libmariadb/libmariadb/mariadb_stmt.c b/libmariadb/libmariadb/mariadb_stmt.c
new file mode 100644
index 00000000..5bcecfde
--- /dev/null
+++ b/libmariadb/libmariadb/mariadb_stmt.c
@@ -0,0 +1,2636 @@
+/****************************************************************************
+ Copyright (C) 2012 Monty Program AB
+ 2013, 2022 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 <http://www.gnu.org/licenses>
+ 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
+ *****************************************************************************/
+
+/* The implementation for prepared statements was ported from PHP's mysqlnd
+ extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
+
+ Original file header:
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2006-2011 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Georg Richter <georg@mysql.com> |
+ | Andrey Hristov <andrey@mysql.com> |
+ | Ulf Wendel <uwendel@mysql.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "ma_global.h"
+#include <ma_sys.h>
+#include <ma_string.h>
+#include <mariadb_ctype.h>
+#include "mysql.h"
+#include "errmsg.h"
+#include <ma_pvio.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <time.h>
+#include <mysql/client_plugin.h>
+#include <ma_common.h>
+#include "ma_priv.h"
+#include <assert.h>
+
+
+#define UPDATE_STMT_ERROR(stmt)\
+stmt_set_error((stmt), (stmt)->mysql->net.last_errno, (stmt)->mysql->net.sqlstate, (stmt)->mysql->net.last_error)
+
+#define STMT_NUM_OFS(type, a, r) (((type *)(a))[r])
+#define MADB_RESET_ERROR 1
+#define MADB_RESET_LONGDATA 2
+#define MADB_RESET_SERVER 4
+#define MADB_RESET_BUFFER 8
+#define MADB_RESET_STORED 16
+
+#define MAX_TIME_STR_LEN 13
+#define MAX_DATE_STR_LEN 5
+#define MAX_DATETIME_STR_LEN 12
+
+typedef struct
+{
+ MA_MEM_ROOT fields_ma_alloc_root;
+} MADB_STMT_EXTENSION;
+
+static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove);
+
+static my_bool is_not_null= 0;
+static my_bool is_null= 1;
+
+void stmt_set_error(MYSQL_STMT *stmt,
+ unsigned int error_nr,
+ const char *sqlstate,
+ const char *format,
+ ...)
+{
+ va_list ap;
+
+ const char *errmsg= format;
+
+ stmt->last_errno= error_nr;
+ ma_strmake(stmt->sqlstate, sqlstate, SQLSTATE_LENGTH);
+
+ if (!format)
+ {
+ if (IS_MYSQL_ERROR(error_nr) || IS_MARIADB_ERROR(error_nr))
+ errmsg= ER(error_nr);
+ else {
+ snprintf(stmt->last_error, MYSQL_ERRMSG_SIZE - 1,
+ ER_UNKNOWN_ERROR_CODE, error_nr);
+ return;
+ }
+ }
+
+ /* Fix for CONC-627: If this is a server error message, we don't
+ need to substitute and possible variadic arguments will be
+ ignored */
+ if (!IS_MYSQL_ERROR(error_nr) && !IS_MARIADB_ERROR(error_nr))
+ {
+ strncpy(stmt->last_error, format, MYSQL_ERRMSG_SIZE - 1);
+ return;
+ }
+
+ va_start(ap, format);
+ vsnprintf(stmt->last_error, MYSQL_ERRMSG_SIZE - 1, errmsg, ap);
+ va_end(ap);
+ return;
+}
+
+my_bool mthd_supported_buffer_type(enum enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_YEAR:
+ return 1;
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags);
+static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close);
+static int stmt_unbuffered_eof(MYSQL_STMT *stmt __attribute__((unused)),
+ uchar **row __attribute__((unused)))
+{
+ return MYSQL_NO_DATA;
+}
+
+static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ ulong pkt_len;
+
+ pkt_len= ma_net_safe_read(stmt->mysql);
+
+ if (pkt_len == packet_error)
+ {
+ stmt->fetch_row_func= stmt_unbuffered_eof;
+ return(1);
+ }
+
+ if (stmt->mysql->net.read_pos[0] == 254)
+ {
+ *row = NULL;
+ stmt->fetch_row_func= stmt_unbuffered_eof;
+ return(MYSQL_NO_DATA);
+ }
+ else
+ *row = stmt->mysql->net.read_pos;
+ stmt->result.rows++;
+ return(0);
+}
+
+static int stmt_buffered_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ if (!stmt->result_cursor)
+ {
+ *row= NULL;
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ return MYSQL_NO_DATA;
+ }
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+ *row= (uchar *)stmt->result_cursor->data;
+
+ stmt->result_cursor= stmt->result_cursor->next;
+ return 0;
+}
+
+int mthd_stmt_read_all_rows(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result= &stmt->result;
+ MYSQL_ROWS *current, **pprevious;
+ ulong packet_len;
+ unsigned char *p;
+
+ pprevious= &result->data;
+
+ while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error)
+ {
+ p= stmt->mysql->net.read_pos;
+ if (packet_len > 7 || p[0] != 254)
+ {
+ /* allocate space for rows */
+ if (!(current= (MYSQL_ROWS *)ma_alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len)))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ current->data= (MYSQL_ROW)(current + 1);
+ *pprevious= current;
+ pprevious= &current->next;
+
+ /* copy binary row, we will encode it during mysql_stmt_fetch */
+ memcpy((char *)current->data, (char *)p, packet_len);
+
+ if (stmt->update_max_length)
+ {
+ uchar *null_ptr, bit_offset= 4;
+ uchar *cp= p;
+ unsigned int i;
+
+ cp++; /* skip first byte */
+ null_ptr= cp;
+ cp+= (stmt->field_count + 9) / 8;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (!(*null_ptr & bit_offset))
+ {
+ if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len < 0)
+ {
+ /* We need to calculate the sizes for date and time types */
+ size_t len= net_field_length(&cp);
+ switch(stmt->fields[i].type) {
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len;
+ break;
+ default:
+ if (len > stmt->fields[i].max_length)
+ stmt->fields[i].max_length= (ulong)len;
+ break;
+ }
+ cp+= len;
+ }
+ else
+ {
+ if (stmt->fields[i].flags & ZEROFILL_FLAG)
+ {
+ /* The -1 is because a ZEROFILL:ed field is always unsigned */
+ size_t len= MAX(stmt->fields[i].length, mysql_ps_fetch_functions[stmt->fields[i].type].max_len-1);
+ if (len > stmt->fields[i].max_length)
+ stmt->fields[i].max_length= (unsigned long)len;
+ }
+ else if (!stmt->fields[i].max_length)
+ {
+ stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len;
+ if (stmt->fields[i].flags & UNSIGNED_FLAG &&
+ stmt->fields[i].type != MYSQL_TYPE_INT24 &&
+ stmt->fields[i].type != MYSQL_TYPE_LONGLONG)
+ {
+ /*
+ Unsigned integers has one character less than signed integers
+ as '-' is counted as part of max_length
+ */
+ stmt->fields[i].max_length--;
+ }
+ }
+ cp+= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len;
+ }
+ }
+ if (!((bit_offset <<=1) & 255))
+ {
+ bit_offset= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+ }
+ current->length= packet_len;
+ result->rows++;
+ } else /* end of stream */
+ {
+ unsigned int last_status= stmt->mysql->server_status;
+ *pprevious= 0;
+ /* sace status info */
+ p++;
+ stmt->upsert_status.warning_count= stmt->mysql->warning_count= uint2korr(p);
+ p+=2;
+ stmt->upsert_status.server_status= stmt->mysql->server_status= uint2korr(p);
+ ma_status_callback(stmt->mysql, last_status);
+ stmt->result_cursor= result->data;
+ return(0);
+ }
+ }
+ stmt->result_cursor= 0;
+ stmt_set_error(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate,
+ stmt->mysql->net.last_error);
+ return(1);
+}
+
+static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row)
+{
+ uchar buf[STMT_ID_LENGTH + 4];
+ MYSQL_DATA *result= &stmt->result;
+
+ if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED)
+ {
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* do we have some prefetched rows available ? */
+ if (stmt->result_cursor)
+ return(stmt_buffered_fetch(stmt, row));
+ if (stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT)
+ stmt->upsert_status.server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ else
+ {
+ int4store(buf, stmt->stmt_id);
+ int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows);
+
+ if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt))
+ {
+ UPDATE_STMT_ERROR(stmt);
+ return(1);
+ }
+
+ /* free previously allocated buffer */
+ ma_free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
+ result->data= 0;
+ result->rows= 0;
+
+ if (!stmt->mysql->options.extension->skip_read_response)
+ {
+ if (stmt->mysql->methods->db_stmt_read_all_rows(stmt))
+ return(1);
+
+ return(stmt_buffered_fetch(stmt, row));
+ }
+ }
+ /* no more cursor data available */
+ *row= NULL;
+ return(MYSQL_NO_DATA);
+}
+
+/* flush one result set */
+void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt)
+{
+ ulong packet_len;
+ int in_resultset= stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->state < MYSQL_STMT_FETCH_DONE;
+ while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error)
+ {
+ unsigned int last_status= stmt->mysql->server_status;
+ uchar *pos= stmt->mysql->net.read_pos;
+
+ if (!in_resultset && *pos == 0) /* OK */
+ {
+ pos++;
+ net_field_length(&pos);
+ net_field_length(&pos);
+ stmt->mysql->server_status= uint2korr(pos);
+ ma_status_callback(stmt->mysql, last_status);
+ goto end;
+ }
+ if (packet_len < 8 && *pos == 254) /* EOF */
+ {
+ if (mariadb_connection(stmt->mysql))
+ {
+ stmt->mysql->server_status= uint2korr(pos + 3);
+ ma_status_callback(stmt->mysql, last_status);
+ if (in_resultset)
+ goto end;
+ in_resultset= 1;
+ }
+ else
+ goto end;
+ }
+ }
+end:
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+}
+
+int mthd_stmt_fetch_to_bind(MYSQL_STMT *stmt, unsigned char *row)
+{
+ uint i;
+ size_t truncations= 0;
+ unsigned char *null_ptr, bit_offset= 4;
+ row++; /* skip status byte */
+ null_ptr= row;
+ row+= (stmt->field_count + 9) / 8;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ /* save row position for fetching values in pieces */
+ if (*null_ptr & bit_offset)
+ {
+ if (stmt->result_callback)
+ stmt->result_callback(stmt->user_data, i, NULL);
+ else
+ {
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ *stmt->bind[i].is_null= 1;
+ stmt->bind[i].u.row_ptr= NULL;
+ }
+ } else
+ {
+ stmt->bind[i].u.row_ptr= row;
+ if (!stmt->bind_result_done ||
+ stmt->bind[i].flags & MADB_BIND_DUMMY)
+ {
+ unsigned long length;
+
+ if (stmt->result_callback)
+ stmt->result_callback(stmt->user_data, i, &row);
+ else {
+ if (mysql_ps_fetch_functions[stmt->fields[i].type].pack_len >= 0)
+ length= mysql_ps_fetch_functions[stmt->fields[i].type].pack_len;
+ else
+ length= net_field_length(&row);
+ row+= length;
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ *stmt->bind[i].length= stmt->bind[i].length_value= length;
+ }
+ }
+ else
+ {
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ *stmt->bind[i].is_null= 0;
+ mysql_ps_fetch_functions[stmt->fields[i].type].func(&stmt->bind[i], &stmt->fields[i], &row);
+ if (stmt->mysql->options.report_data_truncation)
+ truncations+= *stmt->bind[i].error;
+ }
+ }
+
+ if (!((bit_offset <<=1) & 255)) {
+ bit_offset= 1; /* To next byte */
+ null_ptr++;
+ }
+ }
+ return((truncations) ? MYSQL_DATA_TRUNCATED : 0);
+}
+
+MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+
+ if (!stmt->field_count ||
+ (!stmt->cursor_exists && mysql->status != MYSQL_STATUS_STMT_RESULT) ||
+ (stmt->cursor_exists && mysql->status != MYSQL_STATUS_READY) ||
+ (stmt->state != MYSQL_STMT_WAITING_USE_OR_STORE))
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ stmt->state = MYSQL_STMT_USE_OR_STORE_CALLED;
+ if (!stmt->cursor_exists)
+ stmt->fetch_row_func= stmt_unbuffered_fetch; //mysql_stmt_fetch_unbuffered_row;
+ else
+ stmt->fetch_row_func= stmt_cursor_fetch;
+
+ return(NULL);
+}
+
+unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong length)
+{
+ if (length < (unsigned long long) L64(251)) {
+ *packet = (unsigned char) length;
+ return packet + 1;
+ }
+
+ if (length < (unsigned long long) L64(65536)) {
+ *packet++ = 252;
+ int2store(packet,(uint) length);
+ return packet + 2;
+ }
+
+ if (length < (unsigned long long) L64(16777216)) {
+ *packet++ = 253;
+ int3store(packet,(ulong) length);
+ return packet + 3;
+ }
+ *packet++ = 254;
+ int8store(packet, length);
+ return packet + 8;
+}
+
+static long ma_get_length(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr)
+{
+ if (!stmt->params[param_nr].length)
+ return 0;
+ if (stmt->param_callback)
+ return (long)*stmt->params[param_nr].length;
+ if (stmt->row_size)
+ return *(long *)((char *)stmt->params[param_nr].length + row_nr * stmt->row_size);
+ else
+ return stmt->params[param_nr].length[row_nr];
+}
+
+static signed char ma_get_indicator(MYSQL_STMT *stmt, unsigned int param_nr, unsigned long row_nr)
+{
+ if (!MARIADB_STMT_BULK_SUPPORTED(stmt) ||
+ !stmt->array_size ||
+ !stmt->params[param_nr].u.indicator)
+ return 0;
+ if (stmt->param_callback)
+ return *stmt->params[param_nr].u.indicator;
+ if (stmt->row_size)
+ return *((char *)stmt->params[param_nr].u.indicator + (row_nr * stmt->row_size));
+ return stmt->params[param_nr].u.indicator[row_nr];
+}
+
+static void *ma_get_buffer_offset(MYSQL_STMT *stmt, enum enum_field_types type,
+ void *buffer, unsigned long row_nr)
+{
+ if (stmt->param_callback)
+ return buffer;
+
+ if (stmt->array_size)
+ {
+ int len;
+ if (stmt->row_size)
+ return (void *)((char *)buffer + stmt->row_size * row_nr);
+ len= mysql_ps_fetch_functions[type].pack_len;
+ if (len > 0)
+ return (void *)((char *)buffer + len * row_nr);
+ return ((void **)buffer)[row_nr];
+ }
+ return buffer;
+}
+
+int store_param(MYSQL_STMT *stmt, int column, unsigned char **p, unsigned long row_nr)
+{
+ void *buf= ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ signed char indicator= ma_get_indicator(stmt, column, row_nr);
+
+ switch (stmt->params[column].buffer_type) {
+ case MYSQL_TYPE_TINY:
+ int1store(*p, (*(uchar *)buf));
+ (*p) += 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ int2store(*p, (*(short *)buf));
+ (*p) += 2;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ float4store(*p, (*(float *)buf));
+ (*p) += 4;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ float8store(*p, (*(double *)buf));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ int8store(*p, (*(ulonglong *)buf));
+ (*p) += 8;
+ break;
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_INT24:
+ int4store(*p, (*(int32 *)buf));
+ (*p)+= 4;
+ break;
+ case MYSQL_TYPE_TIME:
+ {
+ /* binary encoding:
+ Offset Length Field
+ 0 1 Length
+ 1 1 negative
+ 2-5 4 day
+ 6 1 hour
+ 7 1 minute
+ 8 1 second;
+ 9-13 4 second_part
+ */
+ MYSQL_TIME *t= (MYSQL_TIME *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ char t_buffer[MAX_TIME_STR_LEN];
+ uint len= 0;
+
+ t_buffer[1]= t->neg ? 1 : 0;
+ int4store(t_buffer + 2, t->day);
+ t_buffer[6]= (uchar) t->hour;
+ t_buffer[7]= (uchar) t->minute;
+ t_buffer[8]= (uchar) t->second;
+ if (t->second_part)
+ {
+ int4store(t_buffer + 9, t->second_part);
+ len= 12;
+ }
+ else if (t->day || t->hour || t->minute || t->second)
+ len= 8;
+ t_buffer[0]= len++;
+ memcpy(*p, t_buffer, len);
+ (*p)+= len;
+ break;
+ }
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ {
+ /* binary format for date, timestamp and datetime
+ Offset Length Field
+ 0 1 Length
+ 1-2 2 Year
+ 3 1 Month
+ 4 1 Day
+ 5 1 Hour
+ 6 1 minute
+ 7 1 second
+ 8-11 4 secondpart
+ */
+ MYSQL_TIME *t= (MYSQL_TIME *)ma_get_buffer_offset(stmt, stmt->params[column].buffer_type,
+ stmt->params[column].buffer, row_nr);
+ char t_buffer[MAX_DATETIME_STR_LEN];
+ uint len= 0;
+
+ int2store(t_buffer + 1, t->year);
+ t_buffer[3]= (char) t->month;
+ t_buffer[4]= (char) t->day;
+ t_buffer[5]= (char) t->hour;
+ t_buffer[6]= (char) t->minute;
+ t_buffer[7]= (char) t->second;
+ if (t->second_part)
+ {
+ int4store(t_buffer + 8, t->second_part);
+ len= 11;
+ }
+ else if (t->hour || t->minute || t->second)
+ len= 7;
+ else if (t->year || t->month || t->day)
+ len= 4;
+ else
+ len=0;
+ t_buffer[0]= len++;
+ memcpy(*p, t_buffer, len);
+ (*p)+= len;
+ break;
+ }
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ {
+ ulong len;
+ /* to is after p. The latter hasn't been moved */
+ uchar *to;
+
+ if (indicator == STMT_INDICATOR_NTS)
+ len= -1;
+ else
+ len= ma_get_length(stmt, column, row_nr);
+
+ if (len == (ulong)-1)
+ len= (ulong)strlen((char *)buf);
+
+ to = mysql_net_store_length(*p, len);
+
+ if (len)
+ memcpy(to, buf, len);
+ (*p) = to + len;
+ break;
+ }
+
+ default:
+ /* unsupported parameter type */
+ stmt_set_error(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+ return 0;
+}
+
+/* {{{ ma_stmt_execute_generate_simple_request */
+unsigned char* ma_stmt_execute_generate_simple_request(MYSQL_STMT *stmt, size_t *request_len)
+{
+ /* execute packet has the following format:
+ Offset Length Description
+ -----------------------------------------
+ 0 4 Statement id
+ 4 1 Flags (cursor type)
+ 5 4 Iteration count
+ -----------------------------------------
+ if (stmt->param_count):
+ 6 (paramcount+7)/8 null bitmap
+ ------------------------------------------
+ if (stmt->send_types_to_server):
+ param_count*2 parameter types
+ 1st byte: parameter type
+ 2nd byte flag:
+ unsigned flag (32768)
+ indicator variable exists (16384)
+ ------------------------------------------
+ n data from bind_buffer
+
+ */
+
+ size_t length= 1024;
+ size_t free_bytes= 0;
+ size_t null_byte_offset= 0;
+ uchar *tmp_start;
+ uint i;
+
+ uchar *start= NULL, *p;
+
+ /* preallocate length bytes */
+ /* check: gr */
+ if (!(start= p= (uchar *)malloc(length)))
+ goto mem_error;
+
+ int4store(p, stmt->stmt_id);
+ p += STMT_ID_LENGTH;
+
+ /* flags is 4 bytes, we store just 1 */
+ int1store(p, (unsigned char) stmt->flags);
+ p++;
+
+ int4store(p, 1);
+ p+= 4;
+
+ if (stmt->param_count)
+ {
+ size_t null_count= (stmt->param_count + 7) / 8;
+
+ free_bytes= length - (p - start);
+ if (null_count + 20 > free_bytes)
+ {
+ size_t offset= p - start;
+ length+= offset + null_count + 20;
+ if (!(tmp_start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ start= tmp_start;
+ p= start + offset;
+ }
+
+ null_byte_offset= p - start;
+ memset(p, 0, null_count);
+ p += null_count;
+
+ int1store(p, stmt->send_types_to_server);
+ p++;
+
+ free_bytes= length - (p - start);
+
+ /* Store type information:
+ 2 bytes per type
+ */
+ if (stmt->send_types_to_server)
+ {
+ if (free_bytes < stmt->param_count * 2 + 20)
+ {
+ size_t offset= p - start;
+ length= offset + stmt->param_count * 2 + 20;
+ if (!(tmp_start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ start= tmp_start;
+ p= start + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++)
+ {
+ /* this differs from mysqlnd, c api supports unsigned !! */
+ uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
+ /* check if parameter requires indicator variable */
+ int2store(p, buffer_type);
+ p+= 2;
+ }
+ }
+
+ /* calculate data size */
+ for (i=0; i < stmt->param_count; i++)
+ {
+ size_t size= 0;
+ my_bool has_data= TRUE;
+
+ if (stmt->params[i].long_data_used)
+ {
+ has_data= FALSE;
+ stmt->params[i].long_data_used= 0;
+ }
+
+ if (has_data)
+ {
+ switch (stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ has_data= FALSE;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_SET:
+ size+= 9; /* max 8 bytes for size */
+ size+= (size_t)ma_get_length(stmt, i, 0);
+ break;
+ case MYSQL_TYPE_TIME:
+ size+= MAX_TIME_STR_LEN;
+ break;
+ case MYSQL_TYPE_DATE:
+ size+= MAX_DATE_STR_LEN;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ size+= MAX_DATETIME_STR_LEN;
+ break;
+ default:
+ size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len;
+ break;
+ }
+ }
+ free_bytes= length - (p - start);
+ if (free_bytes < size + 20)
+ {
+ size_t offset= p - start;
+ length= MAX(2 * length, offset + size + 20);
+ if (!(tmp_start= (uchar *)realloc(start, length)))
+ goto mem_error;
+ start= tmp_start;
+ p= start + offset;
+ }
+ if (((stmt->params[i].is_null && *stmt->params[i].is_null) ||
+ stmt->params[i].buffer_type == MYSQL_TYPE_NULL ||
+ !stmt->params[i].buffer))
+ {
+ has_data= FALSE;
+ (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7));
+ }
+
+ if (has_data)
+ {
+ store_param(stmt, i, &p, 0);
+ }
+ }
+ }
+ stmt->send_types_to_server= 0;
+ *request_len = (size_t)(p - start);
+ return start;
+mem_error:
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ free(start);
+ *request_len= 0;
+ return NULL;
+}
+/* }}} */
+
+/* {{{ mysql_stmt_skip_paramset */
+my_bool mysql_stmt_skip_paramset(MYSQL_STMT *stmt, uint row)
+{
+ uint i;
+ for (i=0; i < stmt->param_count; i++)
+ {
+ if (ma_get_indicator(stmt, i, row) == STMT_INDICATOR_IGNORE_ROW)
+ return '\1';
+ }
+
+ return '\0';
+}
+/* }}} */
+
+/* {{{ ma_stmt_execute_generate_bulk_request */
+unsigned char* ma_stmt_execute_generate_bulk_request(MYSQL_STMT *stmt, size_t *request_len)
+{
+ /* execute packet has the following format:
+ Offset Length Description
+ -----------------------------------------
+ 0 4 Statement id
+ 4 2 Flags (cursor type):
+ STMT_BULK_FLAG_CLIENT_SEND_TYPES = 128
+ STMT_BULK_FLAG_INSERT_ID_REQUEST = 64
+ -----------------------------------------
+ if (stmt->send_types_to_server):
+ for (i=0; i < param_count; i++)
+ 1st byte: parameter type
+ 2nd byte flag:
+ unsigned flag (32768)
+ ------------------------------------------
+ for (i=0; i < param_count; i++)
+ 1 indicator variable
+ STMT_INDICATOR_NONE 0
+ STMT_INDICATOR_NULL 1
+ STMT_INDICATOR_DEFAULT 2
+ STMT_INDICATOR_IGNORE 3
+ STMT_INDICATOR_SKIP_SET 4
+ n data from bind buffer
+
+ */
+
+ size_t length= 1024;
+ size_t free_bytes= 0;
+ ushort flags= 0;
+ uchar *tmp_start;
+ uint i, j;
+
+ uchar *start= NULL, *p;
+
+ if (!MARIADB_STMT_BULK_SUPPORTED(stmt))
+ {
+ stmt_set_error(stmt, CR_FUNCTION_NOT_SUPPORTED, "IM001",
+ CER(CR_FUNCTION_NOT_SUPPORTED), "Bulk operation");
+ return NULL;
+ }
+
+ if (!stmt->param_count)
+ {
+ stmt_set_error(stmt, CR_BULK_WITHOUT_PARAMETERS, "IM001",
+ CER(CR_BULK_WITHOUT_PARAMETERS));
+ return NULL;
+ }
+
+ /* preallocate length bytes */
+ if (!(start= p= (uchar *)malloc(length)))
+ {
+ goto mem_error;
+ }
+
+ int4store(p, stmt->stmt_id);
+ p += STMT_ID_LENGTH;
+
+ /* todo: request to return auto generated ids */
+ if (stmt->send_types_to_server)
+ flags|= STMT_BULK_FLAG_CLIENT_SEND_TYPES;
+ int2store(p, flags);
+ p+=2;
+
+ /* When using mariadb_stmt_execute_direct stmt->paran_count is
+ not known, so we need to assign prebind_params, which was previously
+ set by mysql_stmt_attr_set
+ */
+ if (!stmt->param_count && stmt->prebind_params)
+ stmt->param_count= stmt->prebind_params;
+
+ if (stmt->param_count)
+ {
+ free_bytes= length - (p - start);
+
+ /* Store type information:
+ 2 bytes per type
+ */
+ if (stmt->send_types_to_server)
+ {
+ if (free_bytes < stmt->param_count * 2 + 20)
+ {
+ size_t offset= p - start;
+ length= offset + stmt->param_count * 2 + 20;
+ if (!(tmp_start= (uchar *)realloc(start, length)))
+ {
+ goto mem_error;
+ }
+ start= tmp_start;
+ p= start + offset;
+ }
+ for (i = 0; i < stmt->param_count; i++)
+ {
+ /* this differs from mysqlnd, c api supports unsigned !! */
+ uint buffer_type= stmt->params[i].buffer_type | (stmt->params[i].is_unsigned ? 32768 : 0);
+ int2store(p, buffer_type);
+ p+= 2;
+ }
+ }
+
+ /* calculate data size */
+ for (j=0; j < stmt->array_size; j++)
+ {
+ /* If callback for parameters was specified, we need to
+ update bind information for new row */
+ if (stmt->param_callback)
+ {
+ if (stmt->param_callback(stmt->user_data, stmt->params, j))
+ {
+ stmt_set_error(stmt, CR_ERR_STMT_PARAM_CALLBACK, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ }
+
+ if (mysql_stmt_skip_paramset(stmt, j))
+ continue;
+
+ for (i=0; i < stmt->param_count; i++)
+ {
+ size_t size= 0;
+ my_bool has_data= TRUE;
+ signed char indicator= ma_get_indicator(stmt, i, j);
+ /* check if we need to send data */
+ if (indicator > 0)
+ has_data= FALSE;
+ size= 1;
+
+ /* Please note that mysql_stmt_send_long_data is not supported
+ current when performing bulk execute */
+
+ if (has_data)
+ {
+ switch (stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ has_data= FALSE;
+ indicator= STMT_INDICATOR_NULL;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_GEOMETRY:
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_SET:
+ size+= 5; /* max 8 bytes for size */
+ if (!stmt->param_callback)
+ {
+ if (indicator == STMT_INDICATOR_NTS ||
+ (!stmt->row_size && ma_get_length(stmt,i,j) == -1))
+ {
+ size+= strlen(ma_get_buffer_offset(stmt,
+ stmt->params[i].buffer_type,
+ stmt->params[i].buffer,j));
+ }
+ else
+ size+= (size_t)ma_get_length(stmt, i, j);
+ }
+ else {
+ size+= stmt->params[i].buffer_length;
+ }
+ break;
+ default:
+ size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len;
+ break;
+ }
+ }
+ free_bytes= length - (p - start);
+ if (free_bytes < size + 20)
+ {
+ size_t offset= p - start;
+ length= MAX(2 * length, offset + size + 20);
+ if (!(tmp_start= (uchar *)realloc(start, length)))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ goto error;
+ }
+ start= tmp_start;
+ p= start + offset;
+ }
+
+ int1store(p, indicator > 0 ? indicator : 0);
+ p++;
+ if (has_data) {
+ store_param(stmt, i, &p, (stmt->param_callback) ? 0 : j);
+ }
+ }
+ }
+
+ }
+ stmt->send_types_to_server= 0;
+ *request_len = (size_t)(p - start);
+ return start;
+mem_error:
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+error:
+ free(start);
+ *request_len= 0;
+ return NULL;
+}
+/* }}} */
+
+
+unsigned char* ma_stmt_execute_generate_request(MYSQL_STMT *stmt, size_t *request_len, my_bool internal)
+{
+ unsigned char *buf;
+
+
+ if (stmt->request_buffer)
+ {
+ *request_len= stmt->request_length;
+ buf= stmt->request_buffer;
+ /* store actual stmt id */
+ int4store(buf, stmt->stmt_id);
+ /* clear buffer, memory will be freed in execute */
+ stmt->request_buffer= NULL;
+ stmt->request_length= 0;
+ return buf;
+ }
+ if (stmt->array_size > 0)
+ buf= ma_stmt_execute_generate_bulk_request(stmt, request_len);
+ else
+ buf= ma_stmt_execute_generate_simple_request(stmt, request_len);
+
+ if (internal)
+ {
+ if (stmt->request_buffer)
+ free(stmt->request_buffer);
+ stmt->request_buffer= buf;
+ stmt->request_length= *request_len;
+ }
+ return buf;
+}
+
+
+/*!
+ *******************************************************************************
+
+ \fn unsigned long long mysql_stmt_affected_rows
+ \brief returns the number of affected rows from last mysql_stmt_execute
+ call
+
+ \param[in] stmt The statement handle
+ *******************************************************************************
+ */
+unsigned long long STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.affected_rows;
+}
+
+my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, void *value)
+{
+ switch (attr_type) {
+ case STMT_ATTR_STATE:
+ *(enum mysql_stmt_state *)value= stmt->state;
+ break;
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ *(my_bool *)value= stmt->update_max_length;
+ break;
+ case STMT_ATTR_CURSOR_TYPE:
+ *(unsigned long *)value= stmt->flags;
+ break;
+ case STMT_ATTR_PREFETCH_ROWS:
+ *(unsigned long *)value= stmt->prefetch_rows;
+ break;
+ case STMT_ATTR_PREBIND_PARAMS:
+ *(unsigned int *)value= stmt->prebind_params;
+ break;
+ case STMT_ATTR_ARRAY_SIZE:
+ *(unsigned int *)value= stmt->array_size;
+ break;
+ case STMT_ATTR_ROW_SIZE:
+ *(size_t *)value= stmt->row_size;
+ break;
+ case STMT_ATTR_CB_USER_DATA:
+ *((void **)value) = stmt->user_data;
+ break;
+ default:
+ return(1);
+ }
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value)
+{
+ switch (attr_type) {
+ case STMT_ATTR_UPDATE_MAX_LENGTH:
+ stmt->update_max_length= *(my_bool *)value;
+ break;
+ case STMT_ATTR_CURSOR_TYPE:
+ if (*(ulong *)value > (unsigned long) CURSOR_TYPE_READ_ONLY)
+ {
+ stmt_set_error(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ stmt->flags = *(ulong *)value;
+ break;
+ case STMT_ATTR_PREFETCH_ROWS:
+ if (*(ulong *)value == 0)
+ *(long *)value= MYSQL_DEFAULT_PREFETCH_ROWS;
+ else
+ stmt->prefetch_rows= *(long *)value;
+ break;
+ case STMT_ATTR_PREBIND_PARAMS:
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ mysql_stmt_internal_reset(stmt, 1);
+ net_stmt_close(stmt, 0);
+ stmt->state= MYSQL_STMT_INITTED;
+ stmt->params= 0;
+ }
+ stmt->prebind_params= stmt->param_count= *(unsigned int *)value;
+ break;
+ case STMT_ATTR_ARRAY_SIZE:
+ stmt->array_size= *(unsigned int *)value;
+ break;
+ case STMT_ATTR_ROW_SIZE:
+ stmt->row_size= *(size_t *)value;
+ break;
+ case STMT_ATTR_CB_RESULT:
+ stmt->result_callback= (ps_result_callback)value;
+ break;
+ case STMT_ATTR_CB_PARAM:
+ stmt->param_callback= (ps_param_callback)value;
+ break;
+ case STMT_ATTR_CB_USER_DATA:
+ stmt->user_data= (void *)value;
+ break;
+ default:
+ stmt_set_error(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind)
+{
+ MYSQL *mysql= stmt->mysql;
+
+ if (!mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* If number of parameters was specified via mysql_stmt_attr_set we need to realloc
+ them, e.g. for mariadb_stmt_execute_direct()
+ */
+ if ((stmt->state < MYSQL_STMT_PREPARED || stmt->state >= MYSQL_STMT_EXECUTED) &&
+ stmt->prebind_params > 0)
+ {
+ if (!stmt->params && stmt->prebind_params)
+ {
+ if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->prebind_params * sizeof(MYSQL_BIND))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ memset(stmt->params, '\0', stmt->prebind_params * sizeof(MYSQL_BIND));
+ }
+ stmt->param_count= stmt->prebind_params;
+ }
+ else if (stmt->state < MYSQL_STMT_PREPARED) {
+ stmt_set_error(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->param_count && bind)
+ {
+ uint i;
+
+ memcpy(stmt->params, bind, sizeof(MYSQL_BIND) * stmt->param_count);
+ stmt->send_types_to_server= 1;
+
+ for (i=0; i < stmt->param_count; i++)
+ {
+ if (stmt->mysql->methods->db_supported_buffer_type &&
+ !stmt->mysql->methods->db_supported_buffer_type(stmt->params[i].buffer_type))
+ {
+ stmt_set_error(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ if (!stmt->params[i].is_null)
+ stmt->params[i].is_null= &is_not_null;
+
+ if (stmt->params[i].long_data_used)
+ stmt->params[i].long_data_used= 0;
+
+ if (!stmt->params[i].length)
+ stmt->params[i].length= &stmt->params[i].buffer_length;
+
+ switch(stmt->params[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ stmt->params[i].is_null= &is_null;
+ break;
+ case MYSQL_TYPE_TINY:
+ stmt->params[i].buffer_length= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ stmt->params[i].buffer_length= 2;
+ break;
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ stmt->params[i].buffer_length= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_DOUBLE:
+ stmt->params[i].buffer_length= 8;
+ break;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ stmt->params[i].buffer_length= 12;
+ break;
+ case MYSQL_TYPE_TIME:
+ stmt->params[i].buffer_length= 13;
+ break;
+ case MYSQL_TYPE_DATE:
+ stmt->params[i].buffer_length= 5;
+ break;
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_JSON:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ break;
+ default:
+ stmt_set_error(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ break;
+ }
+ }
+ }
+ stmt->bind_param_done= stmt->send_types_to_server= 1;
+
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ return(0);
+}
+
+my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
+{
+ uint i;
+
+ if (stmt->state < MYSQL_STMT_PREPARED)
+ {
+ stmt_set_error(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->field_count)
+ {
+ stmt_set_error(stmt, CR_NO_STMT_METADATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!bind)
+ return(1);
+
+ /* In case of a stored procedure we don't allocate memory for bind
+ in mysql_stmt_prepare
+ */
+
+ if (stmt->field_count && !stmt->bind)
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root=
+ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ }
+
+ memcpy(stmt->bind, bind, sizeof(MYSQL_BIND) * stmt->field_count);
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ if (stmt->mysql->methods->db_supported_buffer_type &&
+ !stmt->mysql->methods->db_supported_buffer_type(bind[i].buffer_type))
+ {
+ stmt_set_error(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->bind[i].is_null)
+ stmt->bind[i].is_null= &stmt->bind[i].is_null_value;
+ if (!stmt->bind[i].length)
+ stmt->bind[i].length= &stmt->bind[i].length_value;
+ if (!stmt->bind[i].error)
+ stmt->bind[i].error= &stmt->bind[i].error_value;
+
+ /* set length values for numeric types */
+ switch(bind[i].buffer_type) {
+ case MYSQL_TYPE_NULL:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 0;
+ break;
+ case MYSQL_TYPE_TINY:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_YEAR:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 2;
+ break;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 4;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_DOUBLE:
+ *stmt->bind[i].length= stmt->bind[i].length_value= 8;
+ break;
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ *stmt->bind[i].length= stmt->bind[i].length_value= sizeof(MYSQL_TIME);
+ break;
+ default:
+ break;
+ }
+ }
+ stmt->bind_result_done= 1;
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ return(0);
+}
+
+static my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove)
+{
+ char stmt_id[STMT_ID_LENGTH];
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+
+ /* clear memory */
+ ma_free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */
+ ma_free_root(&stmt->mem_root,MYF(0));
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+
+ if (stmt->mysql)
+ {
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+
+ /* remove from stmt list */
+ if (remove)
+ stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list);
+
+ /* check if all data are fetched */
+ if (stmt->mysql->status != MYSQL_STATUS_READY)
+ {
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ int4store(stmt_id, stmt->stmt_id);
+ if (stmt->mysql->methods->db_command(stmt->mysql,COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ {
+ UPDATE_STMT_ERROR(stmt);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
+{
+ my_bool rc= 1;
+
+ if (stmt)
+ {
+ if (stmt->mysql && stmt->mysql->net.pvio)
+ mysql_stmt_internal_reset(stmt, 1);
+
+ rc= net_stmt_close(stmt, 1);
+
+ free(stmt->extension);
+ free(stmt);
+ }
+ return(rc);
+}
+
+void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, unsigned long long offset)
+{
+ unsigned long long i= offset;
+ MYSQL_ROWS *ptr= stmt->result.data;
+
+ while(i-- && ptr)
+ ptr= ptr->next;
+
+ stmt->result_cursor= ptr;
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+
+ return;
+}
+
+unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt)
+{
+ return stmt->last_errno;
+}
+
+const char * STDCALL mysql_stmt_error(MYSQL_STMT *stmt)
+{
+ return (const char *)stmt->last_error;
+}
+
+int mthd_stmt_fetch_row(MYSQL_STMT *stmt, unsigned char **row)
+{
+ return stmt->fetch_row_func(stmt, row);
+}
+
+int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
+{
+ unsigned char *row;
+ int rc;
+
+ if (stmt->state <= MYSQL_STMT_EXECUTED)
+ {
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_WAITING_USE_OR_STORE || !stmt->field_count)
+ {
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ } else if (stmt->state== MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ }
+
+ if (stmt->state == MYSQL_STMT_FETCH_DONE)
+ return(MYSQL_NO_DATA);
+
+ if ((rc= stmt->mysql->methods->db_stmt_fetch(stmt, &row)))
+ {
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ /* to fetch data again, stmt must be executed again */
+ return(rc);
+ }
+
+ rc= stmt->mysql->methods->db_stmt_fetch_to_bind(stmt, row);
+
+ stmt->state= MYSQL_STMT_USER_FETCHING;
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ return(rc);
+}
+
+int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset)
+{
+ if (stmt->state < MYSQL_STMT_USER_FETCHING || column >= stmt->field_count ||
+ stmt->state == MYSQL_STMT_FETCH_DONE) {
+ stmt_set_error(stmt, CR_NO_DATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->bind[column].u.row_ptr)
+ {
+ /* we set row_ptr only for columns which contain data, so this must be a NULL column */
+ if (bind[0].is_null)
+ *bind[0].is_null= 1;
+ }
+ else
+ {
+ unsigned char *save_ptr;
+ if (bind[0].length)
+ *bind[0].length= *stmt->bind[column].length;
+ else
+ bind[0].length= &stmt->bind[column].length_value;
+ if (bind[0].is_null)
+ *bind[0].is_null= 0;
+ else
+ bind[0].is_null= &bind[0].is_null_value;
+ if (!bind[0].error)
+ bind[0].error= &bind[0].error_value;
+ *bind[0].error= 0;
+ bind[0].offset= offset;
+ save_ptr= stmt->bind[column].u.row_ptr;
+ mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].u.row_ptr);
+ stmt->bind[column].u.row_ptr= save_ptr;
+ }
+ return(0);
+}
+
+unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt)
+{
+ return stmt->field_count;
+}
+
+my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
+{
+ return madb_reset_stmt(stmt, MADB_RESET_LONGDATA | MADB_RESET_STORED |
+ MADB_RESET_BUFFER | MADB_RESET_ERROR);
+}
+
+MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql)
+{
+
+ MYSQL_STMT *stmt= NULL;
+
+ if (!(stmt= (MYSQL_STMT *)calloc(1, sizeof(MYSQL_STMT))) ||
+ !(stmt->extension= (MADB_STMT_EXTENSION *)calloc(1, sizeof(MADB_STMT_EXTENSION))))
+ {
+ free(stmt);
+ SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+
+ /* fill mysql's stmt list */
+ stmt->list.data= stmt;
+ stmt->mysql= mysql;
+ stmt->stmt_id= 0;
+ mysql->stmts= list_add(mysql->stmts, &stmt->list);
+
+
+ /* clear flags */
+ strcpy(stmt->sqlstate, "00000");
+
+ stmt->state= MYSQL_STMT_INITTED;
+
+ /* set default */
+ stmt->prefetch_rows= 1;
+
+ ma_init_alloc_root(&stmt->mem_root, 2048, 2048);
+ ma_init_alloc_root(&stmt->result.alloc, 4096, 4096);
+ ma_init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, 2048, 2048);
+
+ return(stmt);
+}
+
+my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt)
+{
+ ulong packet_length;
+ uchar *p;
+
+ if ((packet_length= ma_net_safe_read(stmt->mysql)) == packet_error)
+ return(1);
+
+ p= (uchar *)stmt->mysql->net.read_pos;
+
+ if (0xFF == p[0]) /* Error occurred */
+ {
+ return(1);
+ }
+
+ p++;
+ stmt->stmt_id= uint4korr(p);
+ p+= 4;
+ stmt->field_count= uint2korr(p);
+ p+= 2;
+ stmt->param_count= uint2korr(p);
+ p+= 2;
+
+ /* filler */
+ p++;
+ /* for backward compatibility we also update mysql->warning_count */
+ stmt->mysql->warning_count= stmt->upsert_status.warning_count= uint2korr(p);
+
+/* metadata not supported yet */
+
+ if (stmt->param_count &&
+ stmt->mysql->methods->db_stmt_get_param_metadata(stmt))
+ {
+ return 1;
+ }
+
+ /* allocated bind buffer for parameters */
+ if (stmt->field_count &&
+ stmt->mysql->methods->db_stmt_get_result_metadata(stmt))
+ {
+ return 1;
+ }
+ if (stmt->param_count)
+ {
+ if (stmt->prebind_params)
+ {
+ if (stmt->prebind_params != stmt->param_count)
+ {
+ stmt_set_error(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ stmt->param_count= stmt->prebind_params;
+ return 1;
+ }
+ } else {
+ if (!(stmt->params= (MYSQL_BIND *)ma_alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+ memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND));
+ }
+ }
+ /* allocated bind buffer for result */
+ if (stmt->field_count)
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+ memset(stmt->bind, 0, sizeof(MYSQL_BIND) * stmt->field_count);
+ }
+ stmt->state = MYSQL_STMT_PREPARED;
+
+ return(0);
+}
+
+my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result;
+
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0,
+ 7 + ma_extended_type_info_rows(stmt->mysql))))
+ return(1);
+
+ free_rows(result);
+ return(0);
+}
+
+my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_DATA *result;
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+
+ if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0,
+ 7 + ma_extended_type_info_rows(stmt->mysql))))
+ return(1);
+ if (!(stmt->fields= unpack_fields(stmt->mysql, result, fields_ma_alloc_root,
+ stmt->field_count, 0)))
+ return(1);
+ return(0);
+}
+
+int STDCALL mysql_stmt_warning_count(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.warning_count;
+}
+
+int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length)
+{
+ MYSQL *mysql= stmt->mysql;
+ int rc= 1;
+ my_bool is_multi= 0;
+
+ if (!stmt->mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (length == (unsigned long) -1)
+ length= (unsigned long)strlen(query);
+
+ /* clear flags */
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0;
+
+ /* check if we have to clear results */
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ char stmt_id[STMT_ID_LENGTH];
+ is_multi= (mysql->net.extension->multi_status > COM_MULTI_OFF);
+ /* We need to semi-close the prepared statement:
+ reset stmt and free all buffers and close the statement
+ on server side. Statement handle will get a new stmt_id */
+
+ if (!is_multi)
+ ma_multi_command(mysql, COM_MULTI_ENABLED);
+
+ if (mysql_stmt_internal_reset(stmt, 1))
+ goto fail;
+
+ ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
+ ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0));
+
+ stmt->param_count= 0;
+ stmt->field_count= 0;
+ stmt->fields= NULL;
+ stmt->params= NULL;
+
+ int4store(stmt_id, stmt->stmt_id);
+ if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ goto fail;
+ }
+ if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, query, length, 1, stmt))
+ goto fail;
+
+ if (!is_multi && mysql->net.extension->multi_status == COM_MULTI_ENABLED)
+ if (ma_multi_command(mysql, COM_MULTI_END))
+ goto fail;
+
+ if (mysql->net.extension->multi_status > COM_MULTI_OFF ||
+ mysql->options.extension->skip_read_response)
+ return 0;
+
+ if (mysql->methods->db_read_prepare_response &&
+ mysql->methods->db_read_prepare_response(stmt))
+ goto fail;
+
+ return(0);
+
+fail:
+ stmt->state= MYSQL_STMT_INITTED;
+ UPDATE_STMT_ERROR(stmt);
+ return(rc);
+}
+
+int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
+{
+ unsigned int last_server_status;
+
+ if (!stmt->mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!stmt->field_count)
+ return(0);
+
+ /* test_pure_coverage requires checking of error_no */
+ if (stmt->last_errno)
+ return(1);
+
+ if (stmt->state < MYSQL_STMT_EXECUTED)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ last_server_status= stmt->mysql->server_status;
+
+ /* if stmt is a cursor, we need to tell server to send all rows */
+ if (stmt->cursor_exists && stmt->mysql->status == MYSQL_STATUS_READY)
+ {
+ char buff[STMT_ID_LENGTH + 4];
+ int4store(buff, stmt->stmt_id);
+ int4store(buff + STMT_ID_LENGTH, (int)~0);
+
+ if (stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_FETCH,
+ buff, sizeof(buff), 1, stmt))
+ {
+ UPDATE_STMT_ERROR(stmt);
+ return(1);
+ }
+ }
+ else if (stmt->mysql->status != MYSQL_STATUS_STMT_RESULT)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->mysql->methods->db_stmt_read_all_rows(stmt))
+ {
+ /* error during read - reset stmt->data */
+ ma_free_root(&stmt->result.alloc, 0);
+ stmt->result.data= NULL;
+ stmt->result.rows= 0;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ return(1);
+ }
+
+ /* workaround for MDEV 6304:
+ more results not set if the resultset has
+ SERVER_PS_OUT_PARAMS set
+ */
+ if (last_server_status & SERVER_PS_OUT_PARAMS &&
+ !(stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST))
+ stmt->mysql->server_status|= SERVER_MORE_RESULTS_EXIST;
+
+ stmt->result_cursor= stmt->result.data;
+ stmt->fetch_row_func= stmt_buffered_fetch;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+
+ if (!stmt->result.rows)
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ else
+ stmt->state= MYSQL_STMT_USE_OR_STORE_CALLED;
+
+ /* set affected rows: see bug 2247 */
+ stmt->upsert_status.affected_rows= stmt->result.rows;
+ stmt->mysql->affected_rows= stmt->result.rows;
+
+ return(0);
+}
+
+static int madb_alloc_stmt_fields(MYSQL_STMT *stmt)
+{
+ MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ MYSQL *mysql= stmt->mysql;
+ if (!mysql->field_count)
+ return 0;
+
+ stmt->field_count= mysql->field_count;
+ if (mysql->fields)
+ {
+ /* Column info was sent by server */
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+ if (!(stmt->fields= ma_duplicate_resultset_metadata(
+ mysql->fields, mysql->field_count,
+ fields_ma_alloc_root)))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ if (!(stmt->bind= (MYSQL_BIND *) ma_alloc_root(
+ fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return (1);
+ }
+ }
+ memset(stmt->bind, 0, stmt->field_count * sizeof(MYSQL_BIND));
+ stmt->bind_result_done= 0;
+ return(0);
+}
+
+int mthd_stmt_read_execute_response(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+ int ret;
+ unsigned int last_status= mysql->server_status;
+
+ if (!mysql)
+ return(1);
+
+ /* if a reconnect occurred, our connection handle is invalid */
+ if (!stmt->mysql)
+ return (1);
+
+ ret= test((mysql->methods->db_read_stmt_result &&
+ mysql->methods->db_read_stmt_result(mysql)));
+
+ if (!ret && mysql->field_count && !mysql->fields)
+ {
+ /*
+ Column info was not sent by server, copy
+ from stmt->fields
+ */
+ assert(stmt->fields);
+ /*
+ Too bad, C/C resets stmt->field_count to 0
+ before reading SP output variables result sets.
+ */
+ if(!stmt->field_count)
+ stmt->field_count = mysql->field_count;
+ else
+ assert(mysql->field_count == stmt->field_count);
+ mysql->fields= ma_duplicate_resultset_metadata(
+ stmt->fields, stmt->field_count, &mysql->field_alloc);
+ if (!mysql->fields)
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return (1);
+ }
+ }
+
+ /* update affected rows, also if an error occurred */
+ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows;
+
+ if (ret)
+ {
+ stmt_set_error(stmt, mysql->net.last_errno, mysql->net.sqlstate,
+ mysql->net.last_error);
+ /* if mariadb_stmt_execute_direct was used, we need to send the number
+ of parameters to the specified prebinded value to prevent possible
+ memory overrun */
+ if (stmt->prebind_params)
+ {
+ stmt->param_count= stmt->prebind_params;
+ }
+ stmt->state= MYSQL_STMT_PREPARED;
+ return(1);
+ }
+ stmt->upsert_status.last_insert_id= mysql->insert_id;
+ stmt->upsert_status.server_status= mysql->server_status;
+ ma_status_callback(stmt->mysql, last_status);
+ stmt->upsert_status.warning_count= mysql->warning_count;
+
+ CLEAR_CLIENT_ERROR(mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ stmt->execute_count++;
+ stmt->send_types_to_server= 0;
+
+ stmt->state= MYSQL_STMT_EXECUTED;
+
+ if (mysql->field_count)
+ {
+ if (!stmt->field_count ||
+ mysql->server_status & SERVER_MORE_RESULTS_EXIST) /* fix for ps_bug: test_misc */
+ {
+ MA_MEM_ROOT *fields_ma_alloc_root=
+ &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root;
+ uint i;
+
+ ma_free_root(fields_ma_alloc_root, MYF(0));
+ if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root,
+ sizeof(MYSQL_BIND) * mysql->field_count)) ||
+ !(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root,
+ sizeof(MYSQL_FIELD) * mysql->field_count)))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ memset(stmt->bind, 0, sizeof(MYSQL_BIND) * mysql->field_count);
+ stmt->field_count= mysql->field_count;
+
+ for (i=0; i < stmt->field_count; i++)
+ {
+ memcpy(&stmt->fields[i], &mysql->fields[i], sizeof(MYSQL_FIELD));
+
+ /* since all pointers will be incorrect if another statement will
+ be executed, so we need to allocate memory and copy the
+ information */
+ if (mysql->fields[i].db)
+ stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db);
+ if (mysql->fields[i].table)
+ stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].table);
+ if (mysql->fields[i].org_table)
+ stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_table);
+ if (mysql->fields[i].name)
+ stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].name);
+ if (mysql->fields[i].org_name)
+ stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].org_name);
+ if (mysql->fields[i].catalog)
+ stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog);
+ if (mysql->fields[i].def)
+ stmt->fields[i].def= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def);
+ stmt->fields[i].extension=
+ mysql->fields[i].extension ?
+ ma_field_extension_deep_dup(fields_ma_alloc_root,
+ mysql->fields[i].extension) :
+ NULL;
+ }
+ }
+
+ if ((stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) &&
+ (stmt->flags & CURSOR_TYPE_READ_ONLY))
+ {
+ stmt->cursor_exists = TRUE;
+ mysql->status = MYSQL_STATUS_READY;
+
+ /* Only cursor read */
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+
+ } else if (stmt->flags & CURSOR_TYPE_READ_ONLY &&
+ !(stmt->upsert_status.server_status & SERVER_MORE_RESULTS_EXIST))
+ {
+ /*
+ We have asked for CURSOR but got no cursor, because the condition
+ above is not fulfilled. Then...
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+
+ /* preferred is buffered read */
+ if (mysql_stmt_store_result(stmt))
+ return 1;
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+ } else
+ {
+ /* preferred is unbuffered read */
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+ }
+ stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE;
+ /* in certain cases parameter types can change: For example see bug
+ 4026 (SELECT ?), so we need to update field information */
+ if (mysql->field_count == stmt->field_count)
+ {
+ uint i;
+ for (i=0; i < stmt->field_count; i++)
+ {
+ stmt->fields[i].type= mysql->fields[i].type;
+ stmt->fields[i].length= mysql->fields[i].length;
+ stmt->fields[i].flags= mysql->fields[i].flags;
+ stmt->fields[i].decimals= mysql->fields[i].decimals;
+ stmt->fields[i].charsetnr= mysql->fields[i].charsetnr;
+ stmt->fields[i].max_length= mysql->fields[i].max_length;
+ }
+ } else
+ {
+ /* table was altered, see test_wl4166_2 */
+ stmt_set_error(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+ char *request;
+ int ret;
+ size_t request_len= 0;
+
+ if (!stmt->mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_PREPARED)
+ {
+ SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->param_count && !stmt->bind_param_done)
+ {
+ stmt_set_error(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler = _mysql_stmt_use_result;
+ stmt->default_rset_handler(stmt);
+ }
+ if (stmt->state > MYSQL_STMT_WAITING_USE_OR_STORE && stmt->state < MYSQL_STMT_FETCH_DONE && !stmt->result.data)
+ {
+ if (!stmt->cursor_exists)
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ stmt->state= MYSQL_STMT_PREPARED;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+
+ /* clear data, in case mysql_stmt_store_result was called */
+ if (stmt->result.data)
+ {
+ ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC));
+ stmt->result_cursor= stmt->result.data= 0;
+ }
+ /* CONC-344: set row count to zero */
+ stmt->result.rows= 0;
+
+ request= (char *)ma_stmt_execute_generate_request(stmt, &request_len, 0);
+ if (!request)
+ return 1;
+
+ ret= stmt->mysql->methods->db_command(mysql,
+ stmt->array_size > 0 ? COM_STMT_BULK_EXECUTE : COM_STMT_EXECUTE,
+ request, request_len, 1, stmt);
+ if (request)
+ free(request);
+
+ if (ret)
+ {
+ UPDATE_STMT_ERROR(stmt);
+ return(1);
+ }
+
+ if (mysql->net.extension->multi_status > COM_MULTI_OFF ||
+ mysql->options.extension->skip_read_response)
+ return(0);
+
+ return(mthd_stmt_read_execute_response(stmt));
+}
+
+static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags)
+{
+ MYSQL *mysql= stmt->mysql;
+ my_bool ret= 0;
+
+ if (!stmt->mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ /* clear error */
+ if (flags & MADB_RESET_ERROR)
+ {
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ }
+
+ if (stmt->stmt_id)
+ {
+ /* free buffered resultset, previously allocated
+ * by mysql_stmt_store_result
+ */
+ if (flags & MADB_RESET_STORED &&
+ stmt->result_cursor)
+ {
+ ma_free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC));
+ stmt->result.data= NULL;
+ stmt->result.rows= 0;
+ stmt->result_cursor= NULL;
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ }
+
+ /* if there is a pending result set, we will flush it */
+ if (flags & MADB_RESET_BUFFER)
+ {
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ stmt->state = MYSQL_STMT_USER_FETCHING;
+ }
+
+ if (stmt->mysql->status!= MYSQL_STATUS_READY && stmt->field_count)
+ {
+ mysql->methods->db_stmt_flush_unbuffered(stmt);
+ mysql->status= MYSQL_STATUS_READY;
+ }
+ }
+
+ if (flags & MADB_RESET_SERVER)
+ {
+ /* reset statement on server side */
+ if (stmt->mysql && stmt->mysql->status == MYSQL_STATUS_READY &&
+ stmt->mysql->net.pvio)
+ {
+ unsigned char cmd_buf[STMT_ID_LENGTH];
+ int4store(cmd_buf, stmt->stmt_id);
+ if ((ret= stmt->mysql->methods->db_command(mysql,COM_STMT_RESET, (char *)cmd_buf,
+ sizeof(cmd_buf), 0, stmt)))
+ {
+ UPDATE_STMT_ERROR(stmt);
+ return(ret);
+ }
+ }
+ }
+
+ if (flags & MADB_RESET_LONGDATA)
+ {
+ if (stmt->params)
+ {
+ ulonglong i;
+ for (i=0; i < stmt->param_count; i++)
+ if (stmt->params[i].long_data_used)
+ stmt->params[i].long_data_used= 0;
+ }
+ }
+
+ }
+ return(ret);
+}
+
+static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close)
+{
+ MYSQL *mysql= stmt->mysql;
+ my_bool ret= 1;
+ unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR;
+ unsigned int last_status;
+
+ if (!mysql)
+ {
+ /* connection could be invalid, e.g. after mysql_stmt_close or failed reconnect
+ attempt (see bug CONC-97) */
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ last_status= mysql->server_status;
+
+ if (stmt->state >= MYSQL_STMT_USER_FETCHING &&
+ stmt->fetch_row_func == stmt_unbuffered_fetch)
+ flags|= MADB_RESET_BUFFER;
+
+ ret= madb_reset_stmt(stmt, flags);
+
+ if (stmt->stmt_id)
+ {
+ if ((stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->mysql->status != MYSQL_STATUS_READY) ||
+ stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST)
+ {
+ /* flush any pending (multiple) result sets */
+ if (stmt->state == MYSQL_STMT_WAITING_USE_OR_STORE)
+ {
+ stmt->default_rset_handler(stmt);
+ stmt->state = MYSQL_STMT_USER_FETCHING;
+ }
+
+ if (stmt->field_count)
+ {
+ while (mysql_stmt_next_result(stmt) == 0);
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ }
+ }
+ if (!is_close)
+ ret= madb_reset_stmt(stmt, MADB_RESET_SERVER);
+ stmt->state= MYSQL_STMT_PREPARED;
+ }
+ else
+ stmt->state= MYSQL_STMT_INITTED;
+
+ stmt->upsert_status.affected_rows= mysql->affected_rows;
+ stmt->upsert_status.last_insert_id= mysql->insert_id;
+ stmt->upsert_status.server_status= mysql->server_status;
+ ma_status_callback(stmt->mysql, last_status);
+ stmt->upsert_status.warning_count= mysql->warning_count;
+ mysql->status= MYSQL_STATUS_READY;
+
+ return(ret);
+}
+
+MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt)
+{
+ MYSQL_RES *res;
+
+ if (!stmt->field_count)
+ return(NULL);
+
+ /* allocate result set structure and copy stmt information */
+ if (!(res= (MYSQL_RES *)calloc(1, sizeof(MYSQL_RES))))
+ {
+ stmt_set_error(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
+ return(NULL);
+ }
+
+ res->eof= 1;
+ res->fields= stmt->fields;
+ res->field_count= stmt->field_count;
+ return(res);
+}
+
+my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
+{
+ if (stmt->stmt_id > 0 &&
+ stmt->stmt_id != (unsigned long) -1)
+ return mysql_stmt_internal_reset(stmt, 0);
+ return 0;
+}
+
+const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt)
+{
+ return stmt->sqlstate;
+}
+
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt)
+{
+ return(stmt->result_cursor);
+}
+
+unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt)
+{
+ return stmt->param_count;
+}
+
+MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET new_row)
+{
+ MYSQL_ROW_OFFSET old_row; /* for returning old position */
+
+ old_row= stmt->result_cursor;
+ stmt->result_cursor= new_row;
+
+ return(old_row);
+}
+
+my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
+ const char *data, unsigned long length)
+{
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+
+ if (stmt->state < MYSQL_STMT_PREPARED || !stmt->params)
+ {
+ stmt_set_error(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (param_number >= stmt->param_count)
+ {
+ stmt_set_error(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (length || !stmt->params[param_number].long_data_used)
+ {
+ int ret;
+ size_t packet_len= STMT_ID_LENGTH + 2 + length;
+ uchar *cmd_buff= (uchar *)calloc(1, packet_len);
+ int4store(cmd_buff, stmt->stmt_id);
+ int2store(cmd_buff + STMT_ID_LENGTH, param_number);
+ memcpy(cmd_buff + STMT_ID_LENGTH + 2, data, length);
+ stmt->params[param_number].long_data_used= 1;
+ ret= stmt->mysql->methods->db_command(stmt->mysql, COM_STMT_SEND_LONG_DATA,
+ (char *)cmd_buff, packet_len, 1, stmt);
+ if (ret)
+ UPDATE_STMT_ERROR(stmt);
+ free(cmd_buff);
+ return(ret);
+ }
+ return(0);
+}
+
+unsigned long long STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt)
+{
+ return stmt->upsert_status.last_insert_id;
+}
+
+unsigned long long STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
+{
+ return stmt->result.rows;
+}
+
+MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt __attribute__((unused)))
+{
+ /* server doesn't deliver any information yet,
+ so we just return NULL
+ */
+ return(NULL);
+}
+
+my_bool STDCALL mysql_stmt_more_results(MYSQL_STMT *stmt)
+{
+ /* MDEV 4604: Server doesn't set MORE_RESULT flag for
+ OutParam result set, so we need to check
+ for SERVER_MORE_RESULTS_EXIST and for
+ SERVER_PS_OUT_PARAMS)
+ */
+ return (stmt &&
+ stmt->mysql &&
+ ((stmt->mysql->server_status & SERVER_MORE_RESULTS_EXIST) ||
+ (stmt->mysql->server_status & SERVER_PS_OUT_PARAMS)));
+}
+
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
+{
+ int rc= 0;
+
+ if (!stmt->mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (stmt->state < MYSQL_STMT_EXECUTED)
+ {
+ SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return(1);
+ }
+
+ if (!mysql_stmt_more_results(stmt))
+ return(-1);
+
+ if (stmt->state > MYSQL_STMT_EXECUTED &&
+ stmt->state < MYSQL_STMT_FETCH_DONE)
+ madb_reset_stmt(stmt, MADB_RESET_ERROR | MADB_RESET_BUFFER | MADB_RESET_LONGDATA);
+ stmt->state= MYSQL_STMT_WAITING_USE_OR_STORE;
+
+ if (mysql_next_result(stmt->mysql))
+ {
+ stmt->state= MYSQL_STMT_FETCH_DONE;
+ stmt_set_error(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate,
+ stmt->mysql->net.last_error);
+ return(1);
+ }
+
+ if (stmt->mysql->status == MYSQL_STATUS_GET_RESULT)
+ stmt->mysql->status= MYSQL_STATUS_STMT_RESULT;
+
+ if (stmt->mysql->field_count)
+ rc= madb_alloc_stmt_fields(stmt);
+ else
+ {
+ unsigned int last_status= stmt->mysql->server_status;
+ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows;
+ stmt->upsert_status.last_insert_id= stmt->mysql->insert_id;
+ stmt->upsert_status.server_status= stmt->mysql->server_status;
+ ma_status_callback(stmt->mysql, last_status);
+ stmt->upsert_status.warning_count= stmt->mysql->warning_count;
+ }
+
+ stmt->field_count= stmt->mysql->field_count;
+ stmt->result.rows= 0;
+
+ return(rc);
+}
+
+int STDCALL mariadb_stmt_execute_direct(MYSQL_STMT *stmt,
+ const char *stmt_str,
+ size_t length)
+{
+ MYSQL *mysql;
+ my_bool emulate_cmd;
+ my_bool clear_result= 0;
+
+ if (!stmt)
+ return 1;
+
+ mysql= stmt->mysql;
+ if (!mysql)
+ {
+ stmt_set_error(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+
+ emulate_cmd= !(!(stmt->mysql->server_capabilities & CLIENT_MYSQL) &&
+ (stmt->mysql->extension->mariadb_server_capabilities &
+ (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))) || mysql->net.compress;
+
+ /* Server versions < 10.2 don't support execute_direct, so we need to
+ emulate it */
+ if (emulate_cmd)
+ {
+ int rc;
+
+ /* avoid sending close + prepare in 2 packets */
+ if ((rc= mysql_stmt_prepare(stmt, stmt_str, (unsigned long)length)))
+ return rc;
+ return mysql_stmt_execute(stmt);
+ }
+
+ if (ma_multi_command(mysql, COM_MULTI_ENABLED))
+ {
+ stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0);
+ return 1;
+ }
+
+ if (length == (size_t) -1)
+ length= strlen(stmt_str);
+
+ /* clear flags */
+ CLEAR_CLIENT_STMT_ERROR(stmt);
+ CLEAR_CLIENT_ERROR(stmt->mysql);
+ stmt->upsert_status.affected_rows= mysql->affected_rows= (unsigned long long) ~0;
+
+ /* check if we have to clear results */
+ if (stmt->state > MYSQL_STMT_INITTED)
+ {
+ /* We need to semi-close the prepared statement:
+ reset stmt and free all buffers and close the statement
+ on server side. Statement handle will get a new stmt_id */
+ char stmt_id[STMT_ID_LENGTH];
+
+ if (mysql_stmt_internal_reset(stmt, 1))
+ goto fail;
+
+ ma_free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
+ ma_free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root, MYF(0));
+ stmt->field_count= 0;
+ stmt->param_count= 0;
+ stmt->params= 0;
+
+ int4store(stmt_id, stmt->stmt_id);
+ if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id,
+ sizeof(stmt_id), 1, stmt))
+ goto fail;
+ }
+ stmt->stmt_id= -1;
+ if (mysql->methods->db_command(mysql, COM_STMT_PREPARE, stmt_str, length, 1, stmt))
+ goto fail;
+
+ /* in case prepare fails, we need to clear the result package from execute, which
+ is always an error packet (invalid statement id) */
+ clear_result= 1;
+
+ stmt->state= MYSQL_STMT_PREPARED;
+ /* Since we can't determine stmt_id here, we need to set it to -1, so server will know that the
+ * execute command belongs to previous prepare */
+ stmt->stmt_id= -1;
+ if (mysql_stmt_execute(stmt))
+ goto fail;
+
+ /* flush multi buffer */
+ if (ma_multi_command(mysql, COM_MULTI_END))
+ goto fail;
+
+ if (!mysql->options.extension->skip_read_response)
+ {
+ /* read prepare response */
+ if (mysql->methods->db_read_prepare_response &&
+ mysql->methods->db_read_prepare_response(stmt))
+ goto fail;
+
+ clear_result= 0;
+
+ /* read execute response packet */
+ return mthd_stmt_read_execute_response(stmt);
+ }
+fail:
+ /* check if we need to set error message */
+ if (!mysql_stmt_errno(stmt))
+ UPDATE_STMT_ERROR(stmt);
+ if (clear_result) {
+ do {
+ stmt->mysql->methods->db_stmt_flush_unbuffered(stmt);
+ } while(mysql_stmt_more_results(stmt));
+ }
+
+ /* CONC-633: If prepare returned an error, we ignore error from execute */
+ if (mysql_stmt_errno(stmt))
+ {
+ my_set_error(mysql, mysql_stmt_errno(stmt), mysql_stmt_sqlstate(stmt),
+ mysql_stmt_error(stmt));
+ stmt->state= MYSQL_STMT_INITTED;
+ }
+ return 1;
+}
+
+MYSQL_FIELD * STDCALL mariadb_stmt_fetch_fields(MYSQL_STMT *stmt)
+{
+ if (stmt)
+ return stmt->fields;
+ return NULL;
+}
diff --git a/libmariadb/libmariadb/secure/gnutls.c b/libmariadb/libmariadb/secure/gnutls.c
new file mode 100644
index 00000000..4782be62
--- /dev/null
+++ b/libmariadb/libmariadb/secure/gnutls.c
@@ -0,0 +1,1438 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+#ifdef HAVE_GNUTLS
+
+#include <ma_global.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <errmsg.h>
+#include <ma_pthread.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <ma_tls.h>
+#include <mariadb_async.h>
+#include <ma_context.h>
+
+pthread_mutex_t LOCK_gnutls_config;
+
+extern my_bool ma_tls_initialized;
+extern unsigned int mariadb_deinitialize_ssl;
+
+enum ma_pem_type {
+ MA_TLS_PEM_CERT= 0,
+ MA_TLS_PEM_KEY,
+ MA_TLS_PEM_CA,
+ MA_TLS_PEM_CRL
+};
+
+static int my_verify_callback(gnutls_session_t ssl);
+
+char tls_library_version[TLS_VERSION_LENGTH];
+
+struct st_cipher_map {
+ unsigned char sid[2];
+ const char *iana_name;
+ const char *openssl_name;
+ const char *gnutls_name;
+};
+
+const struct st_cipher_map tls_ciphers[]=
+{
+ { {0x00, 0x01},
+ "TLS_RSA_WITH_NULL_MD5",
+ NULL,
+ "TLS_RSA_NULL_MD5"},
+ { {0x00, 0x02},
+ "TLS_RSA_WITH_NULL_SHA",
+ NULL,
+ "TLS_RSA_NULL_SHA1"},
+ { {0x00, 0x3B},
+ "TLS_RSA_WITH_NULL_SHA256",
+ NULL,
+ "TLS_RSA_NULL_SHA256"},
+ { {0x00, 0x05},
+ "TLS_RSA_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_RSA_ARCFOUR_128_SHA1"},
+ { {0x00, 0x04},
+ "TLS_RSA_WITH_RC4_128_MD5",
+ NULL,
+ "TLS_RSA_ARCFOUR_128_MD5"},
+ { {0x00, 0x0A},
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+ "DES-CBC3-SHA",
+ "TLS_RSA_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x2F},
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "AES128-SHA",
+ "TLS_RSA_AES_128_CBC_SHA1"},
+ { {0x00, 0x35},
+ "TLS_RSA_WITH_AES_256_CBC_SHA",
+ "AES256-SHA",
+ "TLS_RSA_AES_256_CBC_SHA1"},
+ { {0x00, 0xBA},
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ "CAMELLIA128-SHA256",
+ "TLS_RSA_CAMELLIA_128_CBC_SHA256"},
+ { {0x00, 0xC0},
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ NULL,
+ "TLS_RSA_CAMELLIA_256_CBC_SHA256"},
+ { {0x00, 0x41},
+ "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "CAMELLIA128-SHA",
+ "TLS_RSA_CAMELLIA_128_CBC_SHA1"},
+ { {0x00, 0x84},
+ "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "CAMELLIA256-SHA",
+ "TLS_RSA_CAMELLIA_256_CBC_SHA1"},
+ { {0x00, 0x3C},
+ "TLS_RSA_WITH_AES_128_CBC_SHA256",
+ "AES128-SHA256",
+ "TLS_RSA_AES_128_CBC_SHA256"},
+ { {0x00, 0x3D},
+ "TLS_RSA_WITH_AES_256_CBC_SHA256",
+ "AES256-SHA256",
+ "TLS_RSA_AES_256_CBC_SHA256"},
+ { {0x00, 0x9C},
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "AES128-GCM-SHA256",
+ "TLS_RSA_AES_128_GCM_SHA256"},
+ { {0x00, 0x9D},
+ "TLS_RSA_WITH_AES_256_GCM_SHA384",
+ "AES256-GCM-SHA384",
+ "TLS_RSA_AES_256_GCM_SHA384"},
+ { {0xC0, 0x7A},
+ "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_RSA_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x7B},
+ "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_RSA_CAMELLIA_256_GCM_SHA384"},
+ { {0xC0, 0x9C},
+ "TLS_RSA_WITH_AES_128_CCM",
+ NULL,
+ "TLS_RSA_AES_128_CCM"},
+ { {0xC0, 0x9D},
+ "TLS_RSA_WITH_AES_256_CCM",
+ NULL,
+ "TLS_RSA_AES_256_CCM"},
+ { {0xC0, 0xA0},
+ "TLS_RSA_WITH_AES_128_CCM_8",
+ NULL,
+ "TLS_RSA_AES_128_CCM_8"},
+ { {0xC0, 0xA1},
+ "TLS_RSA_WITH_AES_256_CCM_8",
+ NULL,
+ "TLS_RSA_AES_256_CCM_8"},
+ { {0x00, 0x66},
+ "TLS_DHE_DSS_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_DHE_DSS_ARCFOUR_128_SHA1"},
+ { {0x00, 0x13},
+ "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
+ NULL,
+ "TLS_DHE_DSS_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x32},
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+ NULL,
+ "TLS_DHE_DSS_AES_128_CBC_SHA1"},
+ { {0x00, 0x38},
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
+ NULL,
+ "TLS_DHE_DSS_AES_256_CBC_SHA1"},
+ { {0x00, 0xBD},
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA256"},
+ { {0x00, 0xC3},
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA256"},
+ { {0x00, 0x44},
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_128_CBC_SHA1"},
+ { {0x00, 0x87},
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_256_CBC_SHA1"},
+ { {0x00, 0x40},
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
+ NULL,
+ "TLS_DHE_DSS_AES_128_CBC_SHA256"},
+ { {0x00, 0x6A},
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
+ NULL,
+ "TLS_DHE_DSS_AES_256_CBC_SHA256"},
+ { {0x00, 0xA2},
+ "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
+ NULL,
+ "TLS_DHE_DSS_AES_128_GCM_SHA256"},
+ { {0x00, 0xA3},
+ "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
+ NULL,
+ "TLS_DHE_DSS_AES_256_GCM_SHA384"},
+ { {0xC0, 0x80},
+ "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x81},
+ "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_DHE_DSS_CAMELLIA_256_GCM_SHA384"},
+ { {0x00, 0x16},
+ "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "EDH-RSA-DES-CBC3-SHA",
+ "TLS_DHE_RSA_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x33},
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
+ "DHE-RSA-AES128-SHA",
+ "TLS_DHE_RSA_AES_128_CBC_SHA1"},
+ { {0x00, 0x39},
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
+ "DHE-RSA-AES256-SHA",
+ "TLS_DHE_RSA_AES_256_CBC_SHA1"},
+ { {0x00, 0xBE},
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA256"},
+ { {0x00, 0xC4},
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
+ NULL,
+ "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA256"},
+ { {0x00, 0x45},
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
+ "DHE-RSA-CAMELLIA128-SHA",
+ "TLS_DHE_RSA_CAMELLIA_128_CBC_SHA1"},
+ { {0x00, 0x88},
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
+ "DHE-RSA-CAMELLIA256-SHA",
+ "TLS_DHE_RSA_CAMELLIA_256_CBC_SHA1"},
+ { {0x00, 0x67},
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
+ "DHE-RSA-AES128-SHA256",
+ "TLS_DHE_RSA_AES_128_CBC_SHA256"},
+ { {0x00, 0x6B},
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+ "DHE-RSA-AES256-SHA256",
+ "TLS_DHE_RSA_AES_256_CBC_SHA256"},
+ { {0x00, 0x9E},
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
+ "DHE-RSA-AES128-GCM-SHA256",
+ "TLS_DHE_RSA_AES_128_GCM_SHA256"},
+ { {0x00, 0x9F},
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
+ "DHE-RSA-AES256-GCM-SHA384",
+ "TLS_DHE_RSA_AES_256_GCM_SHA384"},
+ { {0xC0, 0x7C},
+ "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_DHE_RSA_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x7D},
+ "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_DHE_RSA_CAMELLIA_256_GCM_SHA384"},
+ { {0xCC, 0xAA},
+ "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ "DHE-RSA-CHACHA20-POLY1305",
+ "TLS_DHE_RSA_CHACHA20_POLY1305"},
+ { {0xC0, 0x9E},
+ "TLS_DHE_RSA_WITH_AES_128_CCM",
+ NULL,
+ "TLS_DHE_RSA_AES_128_CCM"},
+ { {0xC0, 0x9F},
+ "TLS_DHE_RSA_WITH_AES_256_CCM",
+ NULL,
+ "TLS_DHE_RSA_AES_256_CCM"},
+ { {0xC0, 0xA2},
+ "TLS_DHE_RSA_WITH_AES_128_CCM_8",
+ NULL,
+ "TLS_DHE_RSA_AES_128_CCM_8"},
+ { {0xC0, 0xA3},
+ "TLS_DHE_RSA_WITH_AES_256_CCM_8",
+ NULL,
+ "TLS_DHE_RSA_AES_256_CCM_8"},
+ { {0xC0, 0x10},
+ "TLS_ECDHE_RSA_WITH_",
+ NULL,
+ "TLS_ECDHE_RSA_NULL_SHA1"},
+ { {0xC0, 0x12},
+ "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ "ECDHE-RSA-DES-CBC3-SHA",
+ "TLS_ECDHE_RSA_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x13},
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ "ECDHE-RSA-AES128-SHA",
+ "TLS_ECDHE_RSA_AES_128_CBC_SHA1"},
+ { {0xC0, 0x14},
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ "ECDHE-RSA-AES256-SHA",
+ "TLS_ECDHE_RSA_AES_256_CBC_SHA1"},
+ { {0xC0, 0x28},
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ "ECDHE-RSA-AES256-SHA384",
+ "TLS_ECDHE_RSA_AES_256_CBC_SHA384"},
+ { {0xC0, 0x11},
+ "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_ECDHE_RSA_ARCFOUR_128_SHA1"},
+ { {0xC0, 0x76},
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_ECDHE_RSA_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x77},
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_ECDHE_RSA_CAMELLIA_256_CBC_SHA384"},
+ { {0xC0, 0x06},
+ "TLS_ECDHE_ECDSA_WITH_",
+ NULL,
+ "TLS_ECDHE_ECDSA_NULL_SHA1"},
+ { {0xC0, 0x08},
+ "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ "ECDHE-ECDSA-DES-CBC3-SHA",
+ "TLS_ECDHE_ECDSA_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x09},
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ "ECDHE-ECDSA-AES128-SHA",
+ "TLS_ECDHE_ECDSA_AES_128_CBC_SHA1"},
+ { {0xC0, 0x0A},
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ "ECDHE-ECDSA-AES256-SHA",
+ "TLS_ECDHE_ECDSA_AES_256_CBC_SHA1"},
+ { {0xC0, 0x07},
+ "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_ECDHE_ECDSA_ARCFOUR_128_SHA1"},
+ { {0xC0, 0x72},
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_ECDHE_ECDSA_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x73},
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_ECDHE_ECDSA_CAMELLIA_256_CBC_SHA384"},
+ { {0xC0, 0x23},
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ "ECDHE-ECDSA-AES128-SHA256",
+ "TLS_ECDHE_ECDSA_AES_128_CBC_SHA256"},
+ { {0xC0, 0x27},
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ "ECDHE-RSA-AES128-SHA256",
+ "TLS_ECDHE_RSA_AES_128_CBC_SHA256"},
+ { {0xC0, 0x86},
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_ECDHE_ECDSA_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x87},
+ "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_ECDHE_ECDSA_CAMELLIA_256_GCM_SHA384"},
+ { {0xC0, 0x2B},
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ "ECDHE-ECDSA-AES128-GCM-SHA256",
+ "TLS_ECDHE_ECDSA_AES_128_GCM_SHA256"},
+ { {0xC0, 0x2C},
+ "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ "ECDHE-ECDSA-AES256-GCM-SHA384",
+ "TLS_ECDHE_ECDSA_AES_256_GCM_SHA384"},
+ { {0xC0, 0x2F},
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ "ECDHE-RSA-AES128-GCM-SHA256",
+ "TLS_ECDHE_RSA_AES_128_GCM_SHA256"},
+ { {0xC0, 0x30},
+ "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ "ECDHE-RSA-AES256-GCM-SHA384",
+ "TLS_ECDHE_RSA_AES_256_GCM_SHA384"},
+ { {0xC0, 0x24},
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ "ECDHE-ECDSA-AES256-SHA384",
+ "TLS_ECDHE_ECDSA_AES_256_CBC_SHA384"},
+ { {0xC0, 0x8A},
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_ECDHE_RSA_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x8B},
+ "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_ECDHE_RSA_CAMELLIA_256_GCM_SHA384"},
+ { {0xCC, 0xA8},
+ "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ "ECDHE-RSA-CHACHA20-POLY1305",
+ "TLS_ECDHE_RSA_CHACHA20_POLY1305"},
+ { {0xCC, 0xA9},
+ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+ "ECDHE-ECDSA-CHACHA20-POLY1305",
+ "TLS_ECDHE_ECDSA_CHACHA20_POLY1305"},
+ { {0xC0, 0xAC},
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
+ NULL,
+ "TLS_ECDHE_ECDSA_AES_128_CCM"},
+ { {0xC0, 0xAD},
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CCM",
+ NULL,
+ "TLS_ECDHE_ECDSA_AES_256_CCM"},
+ { {0xC0, 0xAE},
+ "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",
+ NULL,
+ "TLS_ECDHE_ECDSA_AES_128_CCM_8"},
+ { {0xC0, 0xAF},
+ "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",
+ NULL,
+ "TLS_ECDHE_ECDSA_AES_256_CCM_8"},
+ { {0xC0, 0x34},
+ "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "ECDHE-PSK-3DES-EDE-CBC-SHA",
+ "TLS_ECDHE_PSK_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x35},
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+ "ECDHE-PSK-AES128-CBC-SHA",
+ "TLS_ECDHE_PSK_AES_128_CBC_SHA1"},
+ { {0xC0, 0x36},
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
+ "ECDHE-PSK-AES256-CBC-SHA",
+ "TLS_ECDHE_PSK_AES_256_CBC_SHA1"},
+ { {0xC0, 0x37},
+ "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
+ "ECDHE-PSK-AES128-CBC-SHA256",
+ "TLS_ECDHE_PSK_AES_128_CBC_SHA256"},
+ { {0xC0, 0x38},
+ "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
+ "ECDHE-PSK-AES256-CBC-SHA384",
+ "TLS_ECDHE_PSK_AES_256_CBC_SHA384"},
+ { {0xC0, 0x33},
+ "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_ECDHE_PSK_ARCFOUR_128_SHA1"},
+ { {0xC0, 0x39},
+ "TLS_ECDHE_PSK_WITH_NULL_SHA",
+ NULL,
+ "TLS_ECDHE_PSK_NULL_SHA1"},
+ { {0xC0, 0x3A},
+ "TLS_ECDHE_PSK_WITH_NULL_SHA256",
+ NULL,
+ "TLS_ECDHE_PSK_NULL_SHA256"},
+ { {0xC0, 0x3B},
+ "TLS_ECDHE_PSK_WITH_NULL_SHA384",
+ NULL,
+ "TLS_ECDHE_PSK_NULL_SHA384"},
+ { {0xC0, 0x9A},
+ "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_ECDHE_PSK_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x9B},
+ "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_ECDHE_PSK_CAMELLIA_256_CBC_SHA384"},
+ { {0x00, 0x8A},
+ "TLS_PSK_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_PSK_ARCFOUR_128_SHA1"},
+ { {0x00, 0x8B},
+ "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
+ "PSK-3DES-EDE-CBC-SHA",
+ "TLS_PSK_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x8C},
+ "TLS_PSK_WITH_AES_128_CBC_SHA",
+ "PSK-AES128-CBC-SHA",
+ "TLS_PSK_AES_128_CBC_SHA1"},
+ { {0x00, 0x8D},
+ "TLS_PSK_WITH_AES_256_CBC_SHA",
+ "PSK-AES256-CBC-SHA",
+ "TLS_PSK_AES_256_CBC_SHA1"},
+ { {0x00, 0xAE},
+ "TLS_PSK_WITH_AES_128_CBC_SHA256",
+ "PSK-AES128-CBC-SHA256",
+ "TLS_PSK_AES_128_CBC_SHA256"},
+ { {0x00, 0xA9},
+ "TLS_PSK_WITH_AES_256_GCM_SHA384",
+ "PSK-AES256-GCM-SHA384",
+ "TLS_PSK_AES_256_GCM_SHA384"},
+ { {0xC0, 0x8E},
+ "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_PSK_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x8F},
+ "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_PSK_CAMELLIA_256_GCM_SHA384"},
+ { {0x00, 0xA8},
+ "TLS_PSK_WITH_AES_128_GCM_SHA256",
+ "PSK-AES128-GCM-SHA256",
+ "TLS_PSK_AES_128_GCM_SHA256"},
+ { {0x00, 0x2C},
+ "TLS_PSK_WITH_",
+ NULL,
+ "TLS_PSK_NULL_SHA1"},
+ { {0x00, 0xB0},
+ "TLS_PSK_WITH_",
+ NULL,
+ "TLS_PSK_NULL_SHA256"},
+ { {0xC0, 0x94},
+ "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_PSK_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x95},
+ "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_PSK_CAMELLIA_256_CBC_SHA384"},
+ { {0x00, 0xAF},
+ "TLS_PSK_WITH_AES_256_CBC_SHA384",
+ "PSK-AES256-CBC-SHA384",
+ "TLS_PSK_AES_256_CBC_SHA384"},
+ { {0x00, 0xB1},
+ "TLS_PSK_WITH_",
+ NULL,
+ "TLS_PSK_NULL_SHA384"},
+ { {0x00, 0x92},
+ "TLS_RSA_PSK_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_RSA_PSK_ARCFOUR_128_SHA1"},
+ { {0x00, 0x93},
+ "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
+ "RSA-PSK-3DES-EDE-CBC-SHA",
+ "TLS_RSA_PSK_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x94},
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
+ "RSA-PSK-AES128-CBC-SHA",
+ "TLS_RSA_PSK_AES_128_CBC_SHA1"},
+ { {0x00, 0x95},
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
+ "RSA-PSK-AES256-CBC-SHA",
+ "TLS_RSA_PSK_AES_256_CBC_SHA1"},
+ { {0xC0, 0x92},
+ "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_RSA_PSK_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x93},
+ "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_RSA_PSK_CAMELLIA_256_GCM_SHA384"},
+ { {0x00, 0xAC},
+ "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
+ "RSA-PSK-AES128-GCM-SHA256",
+ "TLS_RSA_PSK_AES_128_GCM_SHA256"},
+ { {0x00, 0xB6},
+ "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
+ "RSA-PSK-AES128-CBC-SHA256",
+ "TLS_RSA_PSK_AES_128_CBC_SHA256"},
+ { {0x00, 0x2E},
+ "TLS_RSA_PSK_WITH_NULL_SHA",
+ NULL,
+ "TLS_RSA_PSK_NULL_SHA1"},
+ { {0x00, 0xB8},
+ "TLS_RSA_PSK_WITH_",
+ NULL,
+ "TLS_RSA_PSK_NULL_SHA256"},
+ { {0x00, 0xAD},
+ "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
+ "RSA-PSK-AES256-GCM-SHA384",
+ "TLS_RSA_PSK_AES_256_GCM_SHA384"},
+ { {0x00, 0xB7},
+ "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
+ "RSA-PSK-AES256-CBC-SHA384",
+ "TLS_RSA_PSK_AES_256_CBC_SHA384"},
+ { {0x00, 0xB9},
+ "TLS_RSA_PSK_WITH_",
+ NULL,
+ "TLS_RSA_PSK_NULL_SHA384"},
+ { {0xC0, 0x98},
+ "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_RSA_PSK_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x99},
+ "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_RSA_PSK_CAMELLIA_256_CBC_SHA384"},
+ { {0x00, 0x8E},
+ "TLS_DHE_PSK_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_DHE_PSK_ARCFOUR_128_SHA1"},
+ { {0x00, 0x8F},
+ "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
+ "DHE-PSK-3DES-EDE-CBC-SHA",
+ "TLS_DHE_PSK_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x90},
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
+ "DHE-PSK-AES128-CBC-SHA",
+ "TLS_DHE_PSK_AES_128_CBC_SHA1"},
+ { {0x00, 0x91},
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
+ "DHE-PSK-AES256-CBC-SHA",
+ "TLS_DHE_PSK_AES_256_CBC_SHA1"},
+ { {0x00, 0xB2},
+ "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
+ "DHE-PSK-AES128-CBC-SHA256",
+ "TLS_DHE_PSK_AES_128_CBC_SHA256"},
+ { {0x00, 0xAA},
+ "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
+ "DHE-PSK-AES128-GCM-SHA256",
+ "TLS_DHE_PSK_AES_128_GCM_SHA256"},
+ { {0x00, 0x2D},
+ "TLS_DHE_PSK_WITH_",
+ NULL,
+ "TLS_DHE_PSK_NULL_SHA1"},
+ { {0x00, 0xB4},
+ "TLS_DHE_PSK_WITH_",
+ NULL,
+ "TLS_DHE_PSK_NULL_SHA256"},
+ { {0x00, 0xB5},
+ "TLS_DHE_PSK_WITH_",
+ NULL,
+ "TLS_DHE_PSK_NULL_SHA384"},
+ { {0x00, 0xB3},
+ "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
+ "DHE-PSK-AES256-CBC-SHA384",
+ "TLS_DHE_PSK_AES_256_CBC_SHA384"},
+ { {0x00, 0xAB},
+ "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
+ "DHE-PSK-AES256-GCM-SHA384",
+ "TLS_DHE_PSK_AES_256_GCM_SHA384"},
+ { {0xC0, 0x96},
+ "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_DHE_PSK_CAMELLIA_128_CBC_SHA256"},
+ { {0xC0, 0x97},
+ "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
+ NULL,
+ "TLS_DHE_PSK_CAMELLIA_256_CBC_SHA384"},
+ { {0xC0, 0x90},
+ "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_DHE_PSK_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x91},
+ "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_DHE_PSK_CAMELLIA_256_GCM_SHA384"},
+ { {0xC0, 0xA4},
+ "TLS_PSK_WITH_AES_128_CCM",
+ NULL,
+ "TLS_PSK_AES_128_CCM"},
+ { {0xC0, 0xA5},
+ "TLS_PSK_WITH_AES_256_CCM",
+ NULL,
+ "TLS_PSK_AES_256_CCM"},
+ { {0xC0, 0xA6},
+ "TLS_DHE_PSK_WITH_AES_128_CCM",
+ NULL,
+ "TLS_DHE_PSK_AES_128_CCM"},
+ { {0xC0, 0xA7},
+ "TLS_DHE_PSK_WITH_AES_256_CCM",
+ NULL,
+ "TLS_DHE_PSK_AES_256_CCM"},
+ { {0xC0, 0xA8},
+ "TLS_PSK_WITH_AES_128_CCM_8",
+ NULL,
+ "TLS_PSK_AES_128_CCM_8"},
+ { {0xC0, 0xA9},
+ "TLS_PSK_WITH_AES_256_CCM_8",
+ NULL,
+ "TLS_PSK_AES_256_CCM_8"},
+ { {0xC0, 0xAA},
+ "TLS_PSK_DHE_WITH_AES_128_CCM_8",
+ NULL,
+ "TLS_DHE_PSK_AES_128_CCM_8"},
+ { {0xC0, 0xAB},
+ "TLS_PSK_DHE_WITH_AES_256_CCM_8",
+ NULL,
+ "TLS_DHE_PSK_AES_256_CCM_8"},
+ { {0xCC, 0xAD},
+ "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "DHE-PSK-CHACHA20-POLY1305",
+ "TLS_DHE_PSK_CHACHA20_POLY1305"},
+ { {0xCC, 0xAC},
+ "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "ECDHE-PSK-CHACHA20-POLY1305",
+ "TLS_ECDHE_PSK_CHACHA20_POLY1305"},
+ { {0xCC, 0xAE},
+ "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "RSA-PSK-CHACHA20-POLY1305",
+ "TLS_RSA_PSK_CHACHA20_POLY1305"},
+ { {0xCC, 0xAB},
+ "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256",
+ "PSK-CHACHA20-POLY1305",
+ "TLS_PSK_CHACHA20_POLY1305"},
+ { {0x00, 0x18},
+ "TLS_DH_anon_WITH_RC4_128_MD5",
+ NULL,
+ "TLS_DH_ANON_ARCFOUR_128_MD5"},
+ { {0x00, 0x1B},
+ "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
+ NULL,
+ "TLS_DH_ANON_3DES_EDE_CBC_SHA1"},
+ { {0x00, 0x34},
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+ NULL,
+ "TLS_DH_ANON_AES_128_CBC_SHA1"},
+ { {0x00, 0x3A},
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA",
+ NULL,
+ "TLS_DH_ANON_AES_256_CBC_SHA1"},
+ { {0x00, 0xBF},
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_128_CBC_SHA256"},
+ { {0x00, 0xC5},
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_256_CBC_SHA256"},
+ { {0x00, 0x46},
+ "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_128_CBC_SHA1"},
+ { {0x00, 0x89},
+ "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_256_CBC_SHA1"},
+ { {0x00, 0x6C},
+ "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
+ NULL,
+ "TLS_DH_ANON_AES_128_CBC_SHA256"},
+ { {0x00, 0x6D},
+ "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
+ NULL,
+ "TLS_DH_ANON_AES_256_CBC_SHA256"},
+ { {0x00, 0xA6},
+ "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
+ NULL,
+ "TLS_DH_ANON_AES_128_GCM_SHA256"},
+ { {0x00, 0xA7},
+ "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
+ NULL,
+ "TLS_DH_ANON_AES_256_GCM_SHA384"},
+ { {0xC0, 0x84},
+ "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_128_GCM_SHA256"},
+ { {0xC0, 0x85},
+ "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
+ NULL,
+ "TLS_DH_ANON_CAMELLIA_256_GCM_SHA384"},
+ { {0xC0, 0x15},
+ "TLS_ECDH_anon_WITH_",
+ NULL,
+ "TLS_ECDH_ANON_NULL_SHA1"},
+ { {0xC0, 0x17},
+ "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
+ NULL,
+ "TLS_ECDH_ANON_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x18},
+ "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
+ NULL,
+ "TLS_ECDH_ANON_AES_128_CBC_SHA1"},
+ { {0xC0, 0x19},
+ "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
+ NULL,
+ "TLS_ECDH_ANON_AES_256_CBC_SHA1"},
+ { {0xC0, 0x16},
+ "TLS_ECDH_anon_WITH_RC4_128_SHA",
+ NULL,
+ "TLS_ECDH_ANON_ARCFOUR_128_SHA1"},
+ { {0xC0, 0x1A},
+ "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
+ "SRP-3DES-EDE-CBC-SHA",
+ "TLS_SRP_SHA_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x1D},
+ "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
+ "SRP-AES-128-CBC-SHA",
+ "TLS_SRP_SHA_AES_128_CBC_SHA1"},
+ { {0xC0, 0x20},
+ "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
+ "SRP-AES-256-CBC-SHA",
+ "TLS_SRP_SHA_AES_256_CBC_SHA1"},
+ { {0xC0, 0x1C},
+ "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
+ NULL,
+ "TLS_SRP_SHA_DSS_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x1B},
+ "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
+ "SRP-RSA-3DES-EDE-CBC-SHA",
+ "TLS_SRP_SHA_RSA_3DES_EDE_CBC_SHA1"},
+ { {0xC0, 0x1F},
+ "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
+ NULL,
+ "TLS_SRP_SHA_DSS_AES_128_CBC_SHA1"},
+ { {0xC0, 0x1E},
+ "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
+ "SRP-RSA-AES-128-CBC-SHA",
+ "TLS_SRP_SHA_RSA_AES_128_CBC_SHA1"},
+ { {0xC0, 0x22},
+ "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
+ NULL,
+ "TLS_SRP_SHA_DSS_AES_256_CBC_SHA1"},
+ { {0xC0, 0x21},
+ "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
+ "SRP-RSA-AES-256-CBC-SHA",
+ "TLS_SRP_SHA_RSA_AES_256_CBC_SHA1"},
+ { {0x00, 0x00},
+ NULL,
+ NULL,
+ NULL}
+};
+
+/* map the gnutls cipher suite (defined by key exchange algorithm, cipher
+ and mac algorithm) to the corresponding OpenSSL cipher name */
+static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx,
+ gnutls_cipher_algorithm_t cipher,
+ gnutls_mac_algorithm_t mac)
+{
+ unsigned int i=0;
+ const char *name= 0;
+ unsigned char sid[2];
+ gnutls_kx_algorithm_t lkx;
+ gnutls_cipher_algorithm_t lcipher;
+ gnutls_mac_algorithm_t lmac;
+
+ while ((name= gnutls_cipher_suite_info(i++, (unsigned char *)&sid, &lkx, &lcipher, &lmac, NULL)))
+ {
+ if (lkx == kx &&
+ lcipher == cipher &&
+ lmac == mac)
+ {
+ i=0;
+ while (tls_ciphers[i].iana_name)
+ {
+ if (!memcmp(tls_ciphers[i].sid, &sid, 2))
+ {
+ if (tls_ciphers[i].openssl_name)
+ return tls_ciphers[i].openssl_name;
+ if (tls_ciphers[i].gnutls_name)
+ return tls_ciphers[i].gnutls_name;
+ return tls_ciphers[i].iana_name;
+ }
+ i++;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* get priority string for a given openssl cipher name */
+static char *get_priority(const char *cipher_name, char *priority, size_t len)
+{
+ unsigned int i= 0;
+ while (tls_ciphers[i].iana_name)
+ {
+ if (strcmp(tls_ciphers[i].iana_name, cipher_name) == 0 ||
+ (tls_ciphers[i].openssl_name &&
+ strcmp(tls_ciphers[i].openssl_name, cipher_name) == 0) ||
+ (tls_ciphers[i].gnutls_name &&
+ strcmp(tls_ciphers[i].gnutls_name, cipher_name) == 0))
+ {
+ const char *name;
+ gnutls_kx_algorithm_t kx;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_mac_algorithm_t mac;
+ gnutls_protocol_t min_version;
+ unsigned j= 0;
+
+ if (!tls_ciphers[i].gnutls_name)
+ return NULL;
+
+ while ((name= gnutls_cipher_suite_info(j++, NULL, &kx, &cipher,
+ &mac, &min_version)))
+ {
+ if (!strcmp(name, tls_ciphers[i].gnutls_name))
+ {
+ snprintf(priority, len - 1, ":+%s:+%s:+%s",
+ gnutls_cipher_get_name(cipher),
+ gnutls_mac_get_name(mac),
+ gnutls_kx_get_name(kx));
+ return priority;
+ }
+ }
+ return NULL;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+#define MAX_SSL_ERR_LEN 100
+
+static void ma_tls_set_error(MYSQL *mysql, void *ssl, int ssl_errno)
+{
+ char ssl_error[MAX_SSL_ERR_LEN];
+ const char *ssl_error_reason;
+ MARIADB_PVIO *pvio= mysql->net.pvio;
+ int save_errno= errno;
+
+ /* give a more descriptive error message for alerts */
+ if (ssl_errno == GNUTLS_E_FATAL_ALERT_RECEIVED)
+ {
+ gnutls_alert_description_t alert_desc;
+ const char *alert_name;
+ alert_desc= gnutls_alert_get((gnutls_session_t)ssl);
+ alert_name= gnutls_alert_get_name(alert_desc);
+ snprintf(ssl_error, MAX_SSL_ERR_LEN, "fatal alert received: %s",
+ alert_name);
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error);
+ return;
+ }
+
+ if (ssl_errno && (ssl_error_reason= gnutls_strerror(ssl_errno)))
+ {
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
+ ssl_error_reason);
+ return;
+ }
+
+ strerror_r(save_errno, ssl_error, MAX_SSL_ERR_LEN);
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "TLS/SSL error: %s (%d)",
+ ssl_error, save_errno);
+}
+
+
+static void ma_tls_get_error(char *errmsg, size_t length, int ssl_errno)
+{
+ const char *ssl_error_reason;
+
+ if (!ssl_errno)
+ {
+ strncpy(errmsg, "Unknown SSL error", length);
+ return;
+ }
+ if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
+ {
+ strncpy(errmsg, ssl_error_reason, length);
+ return;
+ }
+ snprintf(errmsg, length, "SSL errno=%d", ssl_errno);
+}
+
+/*
+ Initializes SSL and allocate global
+ context SSL_context
+
+ SYNOPSIS
+ my_gnutls_start
+ mysql connection handle
+
+ RETURN VALUES
+ 0 success
+ 1 error
+*/
+int ma_tls_start(char *errmsg, size_t errmsg_len)
+{
+ int rc= 0;
+
+ if (ma_tls_initialized)
+ return 0;
+
+ pthread_mutex_init(&LOCK_gnutls_config,NULL);
+ pthread_mutex_lock(&LOCK_gnutls_config);
+
+ if ((rc= gnutls_global_init()) != GNUTLS_E_SUCCESS)
+ {
+ ma_tls_get_error(errmsg, errmsg_len, rc);
+ goto end;
+ }
+ snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "GnuTLS %s",
+ gnutls_check_version(NULL));
+
+ ma_tls_initialized= TRUE;
+end:
+ pthread_mutex_unlock(&LOCK_gnutls_config);
+ return rc;
+}
+
+/*
+ Release SSL and free resources
+ Will be automatically executed by
+ mysql_server_end() function
+
+ SYNOPSIS
+ my_gnutls_end()
+ void
+
+ RETURN VALUES
+ void
+*/
+void ma_tls_end()
+{
+ if (ma_tls_initialized)
+ {
+ pthread_mutex_lock(&LOCK_gnutls_config);
+ if (mariadb_deinitialize_ssl)
+ gnutls_global_deinit();
+ ma_tls_initialized= FALSE;
+ pthread_mutex_unlock(&LOCK_gnutls_config);
+ pthread_mutex_destroy(&LOCK_gnutls_config);
+ }
+ return;
+}
+
+static size_t ma_gnutls_get_protocol_version(const char *tls_version_option,
+ char *priority_string,
+ size_t prio_len)
+{
+ char tls_versions[128];
+
+ tls_versions[0]= 0;
+ if (!tls_version_option || !tls_version_option[0])
+ goto end;
+
+
+ if (strstr(tls_version_option, "TLSv1.0"))
+ strcat(tls_versions, ":+VERS-TLS1.0");
+ if (strstr(tls_version_option, "TLSv1.1"))
+ strcat(tls_versions, ":+VERS-TLS1.1");
+ if (strstr(tls_version_option, "TLSv1.2"))
+ strcat(tls_versions, ":+VERS-TLS1.2");
+#if GNUTLS_VERSION_NUMBER > 0x030605
+ if (strstr(tls_version_option, "TLSv1.3"))
+ strcat(tls_versions, ":+VERS-TLS1.3");
+#endif
+end:
+ if (tls_versions[0])
+ snprintf(priority_string, prio_len - 1, "-VERS-TLS-ALL%s:NORMAL", tls_versions);
+ else
+ strncpy(priority_string, "NORMAL:+VERS-ALL", prio_len - 1);
+ return strlen(priority_string);
+}
+
+static int ma_gnutls_set_ciphers(gnutls_session_t ssl,
+ const char *cipher_str,
+ const char *tls_version)
+{
+ const char *err;
+ char *token;
+#define PRIO_SIZE 1024
+ char prio[PRIO_SIZE];
+
+ ma_gnutls_get_protocol_version(tls_version, prio, PRIO_SIZE);
+
+ if (!cipher_str)
+ return gnutls_priority_set_direct(ssl, prio, &err);
+
+ token= strtok((char *)cipher_str, ":");
+
+ strcpy(prio, "NONE:+VERS-TLS-ALL:+SIGN-ALL:+COMP-NULL:+CURVE-ALL");
+
+ while (token)
+ {
+ char priority[1024];
+ char *p= get_priority(token, priority, 1024);
+ if (p)
+ strncat(prio, p, PRIO_SIZE - strlen(prio));
+ token = strtok(NULL, ":");
+ }
+ return gnutls_priority_set_direct(ssl, prio , &err);
+}
+
+static int ma_tls_set_certs(MYSQL *mysql,
+ gnutls_certificate_credentials_t ctx)
+{
+ int ssl_error= 0;
+
+ if (mysql->options.ssl_ca)
+ {
+
+ ssl_error= gnutls_certificate_set_x509_trust_file(ctx,
+ mysql->options.ssl_ca,
+ GNUTLS_X509_FMT_PEM);
+ if (ssl_error < 0)
+ goto error;
+ }
+
+ if (mysql->options.ssl_capath)
+ {
+ ssl_error= gnutls_certificate_set_x509_trust_dir(ctx,
+ mysql->options.ssl_capath,
+ GNUTLS_X509_FMT_PEM);
+ if (ssl_error < 0)
+ goto error;
+ }
+
+ if (mysql->options.extension && mysql->options.extension->ssl_crl)
+ {
+ ssl_error= gnutls_certificate_set_x509_crl_file(ctx,
+ mysql->options.extension->ssl_crl, GNUTLS_X509_FMT_PEM);
+ if (ssl_error < 0)
+ goto error;
+ }
+
+ if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
+ {
+ ssl_error= gnutls_certificate_set_x509_system_trust(ctx);
+ if (ssl_error < 0)
+ goto error;
+ }
+
+ gnutls_certificate_set_verify_function(ctx,
+ my_verify_callback);
+
+ if (mysql->options.ssl_key || mysql->options.ssl_cert)
+ {
+ char *keyfile= mysql->options.ssl_key;
+ char *certfile= mysql->options.ssl_cert;
+
+ if (!certfile)
+ certfile= keyfile;
+ else if (!keyfile)
+ keyfile= certfile;
+
+ /* load cert/key into context */
+ if ((ssl_error= gnutls_certificate_set_x509_key_file2(ctx,
+ certfile, keyfile, GNUTLS_X509_FMT_PEM,
+ mysql->options.extension ? mysql->options.extension->tls_pw : NULL, 0)) < 0)
+ goto error;
+ }
+
+error:
+ return ssl_error;
+}
+
+void *ma_tls_init(MYSQL *mysql)
+{
+ gnutls_session_t ssl= NULL;
+ gnutls_certificate_credentials_t ctx;
+ int ssl_error= 0;
+
+ pthread_mutex_lock(&LOCK_gnutls_config);
+
+ if (gnutls_certificate_allocate_credentials(&ctx) != GNUTLS_E_SUCCESS)
+ goto error;
+
+ if ((ssl_error= ma_tls_set_certs(mysql, ctx)) < 0)
+ goto error;
+
+ if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT | GNUTLS_NONBLOCK | GNUTLS_NO_SIGNAL)) < 0)
+ goto error;
+
+ gnutls_session_set_ptr(ssl, (void *)mysql);
+ /*
+ gnutls_certificate_set_retrieve_function2(GNUTLS_xcred, client_cert_callback);
+ */
+ ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher, mysql->options.extension ? mysql->options.extension->tls_version : NULL);
+ if (ssl_error < 0)
+ goto error;
+
+ /* we don't load private key and cert by default - if the server requests
+ a client certificate we will send it via callback function */
+ if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, ctx)) < 0)
+ goto error;
+
+ pthread_mutex_unlock(&LOCK_gnutls_config);
+ return (void *)ssl;
+error:
+ ma_tls_set_error(mysql, ssl, ssl_error);
+ gnutls_certificate_free_credentials(ctx);
+ if (ssl)
+ gnutls_deinit(ssl);
+ pthread_mutex_unlock(&LOCK_gnutls_config);
+ return NULL;
+}
+
+#ifdef GNUTLS_EXTERNAL_TRANSPORT
+ssize_t ma_tls_push(gnutls_transport_ptr_t ptr, const void* data, size_t len)
+{
+ MARIADB_PVIO *pvio= (MARIADB_PVIO *)ptr;
+ ssize_t rc= pvio->methods->write(pvio, data, len);
+ return rc;
+}
+
+ssize_t ma_tls_pull(gnutls_transport_ptr_t ptr, void* data, size_t len)
+{
+ MARIADB_PVIO *pvio= (MARIADB_PVIO *)ptr;
+ ssize_t rc= pvio->methods->read(pvio, data, len);
+ return rc;
+}
+
+static int ma_tls_pull_timeout(gnutls_transport_ptr_t ptr, unsigned int ms)
+{
+ MARIADB_PVIO *pvio= (MARIADB_PVIO *)ptr;
+ return pvio->methods->wait_io_or_timeout(pvio, 0, ms);
+}
+#endif
+
+my_bool ma_tls_connect(MARIADB_TLS *ctls)
+{
+ gnutls_session_t ssl = (gnutls_session_t)ctls->ssl;
+ my_bool blocking;
+ MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
+ MARIADB_PVIO *pvio;
+ int ret;
+
+ if (!mysql)
+ return 1;
+
+ pvio= mysql->net.pvio;
+
+ /* Set socket to blocking if not already set */
+ if (!(blocking= pvio->methods->is_blocking(pvio)))
+ pvio->methods->blocking(pvio, TRUE, 0);
+
+
+#ifdef GNUTLS_EXTERNAL_TRANSPORT
+ /* we don't use GnuTLS read/write functions */
+ gnutls_transport_set_ptr(ssl, pvio);
+ gnutls_transport_set_push_function(ssl, ma_tls_push);
+ gnutls_transport_set_pull_function(ssl, ma_tls_pull);
+ gnutls_transport_set_pull_timeout_function(ssl, ma_tls_pull_timeout);
+ gnutls_handshake_set_timeout(ssl, pvio->timeout[PVIO_CONNECT_TIMEOUT]);
+#else
+ gnutls_transport_set_int(ssl, mysql_get_socket(mysql));
+#endif
+
+ do {
+ ret = gnutls_handshake(ssl);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0)
+ {
+ /* If error message was not set while calling certification callback function,
+ use default error message (which is not very descriptive */
+ if (!mysql_errno(mysql))
+ ma_tls_set_error(mysql, ssl, ret);
+
+ ma_tls_close(ctls);
+
+ /* restore blocking mode */
+ if (!blocking)
+ pvio->methods->blocking(pvio, FALSE, 0);
+ return 1;
+ }
+ ctls->ssl= (void *)ssl;
+ return 0;
+}
+
+ssize_t ma_tls_write_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ MARIADB_TLS *ctls= pvio->ctls;
+
+ for (;;)
+ {
+ b->events_to_wait_for= 0;
+ res= gnutls_record_send((gnutls_session_t)ctls->ssl, (void *)buffer, length);
+ if (res > 0)
+ return res;
+ if (res == GNUTLS_E_AGAIN)
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ else
+ return res;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ }
+}
+
+
+ssize_t ma_tls_read_async(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
+{
+ ssize_t res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ MARIADB_TLS *ctls= pvio->ctls;
+
+ for (;;)
+ {
+ b->events_to_wait_for= 0;
+ res= gnutls_record_recv((gnutls_session_t)ctls->ssl, (void *)buffer, length);
+ if (res > 0)
+ return res;
+ if (res == GNUTLS_E_AGAIN)
+ b->events_to_wait_for|= MYSQL_WAIT_READ;
+ else
+ return res;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ }
+}
+
+ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ ssize_t rc;
+ MARIADB_PVIO *pvio= ctls->pvio;
+
+ while ((rc= gnutls_record_recv((gnutls_session_t)ctls->ssl, (void *)buffer, length)) <= 0)
+ {
+ if (rc != GNUTLS_E_AGAIN && rc != GNUTLS_E_INTERRUPTED)
+ break;
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1)
+ break;
+ }
+ if (rc <= 0) {
+ MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl);
+ ma_tls_set_error(mysql, ctls->ssl, rc);
+ }
+ return rc;
+}
+
+ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ ssize_t rc;
+ MARIADB_PVIO *pvio= ctls->pvio;
+
+ while ((rc= gnutls_record_send((gnutls_session_t)ctls->ssl, (void *)buffer, length)) <= 0)
+ {
+ if (rc != GNUTLS_E_AGAIN && rc != GNUTLS_E_INTERRUPTED)
+ break;
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1)
+ break;
+ }
+ if (rc <= 0) {
+ MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl);
+ ma_tls_set_error(mysql, ctls->ssl, rc);
+ }
+ return rc;
+}
+
+my_bool ma_tls_close(MARIADB_TLS *ctls)
+{
+ if (ctls->ssl)
+ {
+ gnutls_certificate_credentials_t ctx;
+ /* this would be the correct way, however can't detect afterwards
+ if the socket is closed or not, so we don't send encrypted
+ finish alert.
+ rc= gnutls_bye((gnutls_session_t )ctls->ssl, GNUTLS_SHUT_WR);
+ */
+ gnutls_credentials_get(ctls->ssl, GNUTLS_CRD_CERTIFICATE, (void **)&ctx);
+ gnutls_certificate_free_keys(ctx);
+ gnutls_certificate_free_cas(ctx);
+ gnutls_certificate_free_crls(ctx);
+ gnutls_certificate_free_ca_names(ctx);
+ gnutls_certificate_free_credentials(ctx);
+ gnutls_deinit((gnutls_session_t )ctls->ssl);
+ ctls->ssl= NULL;
+ }
+ return 0;
+}
+
+int ma_tls_verify_server_cert(MARIADB_TLS *ctls __attribute__((unused)))
+{
+ /* server verification is already handled before during handshake */
+ return 0;
+}
+
+const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
+{
+ gnutls_kx_algorithm_t kx;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_mac_algorithm_t mac;
+
+ if (!ctls || !ctls->ssl)
+ return NULL;
+
+ mac= gnutls_mac_get((gnutls_session_t)ctls->ssl);
+ cipher= gnutls_cipher_get((gnutls_session_t)ctls->ssl);
+ kx= gnutls_kx_get((gnutls_session_t)ctls->ssl);
+ return openssl_cipher_name(kx, cipher, mac);
+}
+
+static int my_verify_callback(gnutls_session_t ssl)
+{
+ unsigned int status= 0;
+ MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl);
+
+ CLEAR_CLIENT_ERROR(mysql);
+
+ if ((mysql->options.extension->tls_verify_server_cert))
+ {
+ const char *hostname= mysql->host;
+
+ if (gnutls_certificate_verify_peers3 (ssl, hostname, &status) < 0)
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ } else {
+ if (gnutls_certificate_verify_peers2 (ssl, &status) < 0)
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+ if (status & GNUTLS_CERT_INVALID)
+ {
+ gnutls_datum_t out;
+ int type;
+ /* accept self signed certificates if we don't have to verify server cert */
+ if (!(mysql->options.extension->tls_verify_server_cert) &&
+ (status & GNUTLS_CERT_SIGNER_NOT_FOUND))
+ return 0;
+
+ /* gnutls default error message "certificate validation failed" isn't very
+ descriptive, so we provide more information about the error here */
+ type= gnutls_certificate_type_get(ssl);
+ gnutls_certificate_verification_status_print(status, type, &out, 0);
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR), out.data);
+ gnutls_free(out.data);
+
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+
+ return 0;
+}
+
+unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
+{
+ MYSQL *mysql;
+ size_t fp_len= len;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+
+ if (!ctls || !ctls->ssl)
+ return 0;
+
+ mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl);
+
+ cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size);
+ if (cert_list == NULL)
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Unable to get server certificate");
+ return 0;
+ }
+
+ if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0)
+ return fp_len;
+ else
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Finger print buffer too small");
+ return 0;
+ }
+}
+
+int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ if (!ctls || !ctls->ssl)
+ return 1;
+
+ return gnutls_protocol_get_version(ctls->ssl) - 1;
+}
+
+void ma_tls_set_connection(MYSQL *mysql)
+{
+ (void)gnutls_session_set_ptr(mysql->net.pvio->ctls->ssl, (void *)mysql);
+}
+#endif /* HAVE_GNUTLS */
diff --git a/libmariadb/libmariadb/secure/gnutls_crypt.c b/libmariadb/libmariadb/secure/gnutls_crypt.c
new file mode 100644
index 00000000..a669e88e
--- /dev/null
+++ b/libmariadb/libmariadb/secure/gnutls_crypt.c
@@ -0,0 +1,77 @@
+/*
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*/
+#include <ma_crypt.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static gnutls_digest_algorithm_t ma_hash_get_algorithm(unsigned int alg)
+{
+ switch(alg)
+ {
+ case MA_HASH_MD5:
+ return GNUTLS_DIG_MD5;
+ case MA_HASH_SHA1:
+ return GNUTLS_DIG_SHA1;
+ case MA_HASH_SHA256:
+ return GNUTLS_DIG_SHA256;
+ case MA_HASH_SHA384:
+ return GNUTLS_DIG_SHA384;
+ case MA_HASH_SHA512:
+ return GNUTLS_DIG_SHA512;
+ case MA_HASH_RIPEMD160:
+ return GNUTLS_DIG_RMD160;
+ default:
+ return GNUTLS_DIG_UNKNOWN;
+ }
+}
+
+MA_HASH_CTX *ma_hash_new(unsigned int algorithm)
+{
+ gnutls_hash_hd_t ctx= NULL;
+ gnutls_digest_algorithm_t hash_alg= ma_hash_get_algorithm(algorithm);
+
+ /* unknown or unsupported hash algorithm */
+ if (hash_alg == GNUTLS_DIG_UNKNOWN)
+ return NULL;
+
+ if (gnutls_hash_init(&ctx, hash_alg) < 0)
+ return NULL;
+
+ return (MA_HASH_CTX *)ctx;
+}
+
+void ma_hash_free(MA_HASH_CTX *ctx)
+{
+ if (ctx)
+ gnutls_hash_deinit((gnutls_hash_hd_t)ctx, NULL);
+}
+
+void ma_hash_input(MA_HASH_CTX *ctx,
+ const unsigned char *buffer,
+ size_t len)
+{
+ gnutls_hash((gnutls_hash_hd_t)ctx, (const void *)buffer, len);
+}
+
+void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
+{
+ gnutls_hash_output((gnutls_hash_hd_t)ctx, digest);
+}
+
+
diff --git a/libmariadb/libmariadb/secure/ma_schannel.c b/libmariadb/libmariadb/secure/ma_schannel.c
new file mode 100644
index 00000000..be4148c7
--- /dev/null
+++ b/libmariadb/libmariadb/secure/ma_schannel.c
@@ -0,0 +1,629 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ Author: Georg Richter
+
+ *************************************************************************************/
+#include "ma_schannel.h"
+#include "schannel_certs.h"
+#include <assert.h>
+
+#define SC_IO_BUFFER_SIZE 0x4000
+#define MAX_SSL_ERR_LEN 100
+
+#define SCHANNEL_PAYLOAD(A) ((A).cbMaximumMessage + (A).cbHeader + (A).cbTrailer)
+void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
+
+
+
+
+/* {{{ void ma_schannel_set_sec_error */
+void ma_schannel_set_sec_error(MARIADB_PVIO* pvio, DWORD ErrorNo)
+{
+ MYSQL* mysql = pvio->mysql;
+ if (ErrorNo != SEC_E_OK)
+ mysql->net.extension->extended_errno = ErrorNo;
+ if (ErrorNo == SEC_E_INTERNAL_ERROR && GetLastError())
+ {
+ ma_schannel_set_win_error(pvio, GetLastError());
+ return;
+ }
+ ma_schannel_set_win_error(pvio, ErrorNo);
+}
+/* }}} */
+
+#include "win32_errmsg.h"
+/* {{{ void ma_schnnel_set_win_error */
+void ma_schannel_set_win_error(MARIADB_PVIO *pvio, DWORD ErrorNo)
+{
+ char buffer[256];
+ ma_format_win32_error(buffer, sizeof(buffer), ErrorNo, "TLS/SSL error: ");
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, buffer);
+ return;
+}
+/* }}} */
+
+
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData) */
+/*
+ perform handshake loop
+
+ SYNOPSIS
+ ma_schannel_handshake_loop()
+ pvio Pointer to an Communication/IO structure
+ InitialRead TRUE if it's the very first read
+ ExtraData Pointer to an SecBuffer which contains extra data (sent by application)
+
+
+*/
+
+SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData)
+{
+ SecBufferDesc OutBuffer, InBuffer;
+ SecBuffer InBuffers[2], OutBuffers;
+ DWORD dwSSPIFlags, dwSSPIOutFlags, cbData, cbIoBuffer;
+ TimeStamp tsExpiry;
+ SECURITY_STATUS rc;
+ PUCHAR IoBuffer;
+ BOOL fDoRead;
+ MARIADB_TLS *ctls= pvio->ctls;
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+
+
+ dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+
+ /* Allocate data buffer */
+ if (!(IoBuffer = malloc(SC_IO_BUFFER_SIZE)))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ cbIoBuffer = 0;
+ fDoRead = InitialRead;
+
+ /* handshake loop: We will leave if handshake is finished
+ or an error occurs */
+
+ rc = SEC_I_CONTINUE_NEEDED;
+
+ while (rc == SEC_I_CONTINUE_NEEDED ||
+ rc == SEC_E_INCOMPLETE_MESSAGE ||
+ rc == SEC_I_INCOMPLETE_CREDENTIALS )
+ {
+ /* Read data */
+ if (rc == SEC_E_INCOMPLETE_MESSAGE ||
+ !cbIoBuffer)
+ {
+ if(fDoRead)
+ {
+ ssize_t nbytes = pvio->methods->read(pvio, IoBuffer + cbIoBuffer, (size_t)(SC_IO_BUFFER_SIZE - cbIoBuffer));
+ if (nbytes <= 0)
+ {
+ rc = SEC_E_INTERNAL_ERROR;
+ break;
+ }
+ cbData = (DWORD)nbytes;
+ cbIoBuffer += cbData;
+ }
+ else
+ fDoRead = TRUE;
+ }
+
+ /* input buffers
+ First buffer stores data received from server. leftover data
+ will be stored in second buffer with BufferType SECBUFFER_EXTRA */
+
+ InBuffers[0].pvBuffer = IoBuffer;
+ InBuffers[0].cbBuffer = cbIoBuffer;
+ InBuffers[0].BufferType = SECBUFFER_TOKEN;
+
+ InBuffers[1].pvBuffer = NULL;
+ InBuffers[1].cbBuffer = 0;
+ InBuffers[1].BufferType = SECBUFFER_EMPTY;
+
+ InBuffer.cBuffers = 2;
+ InBuffer.pBuffers = InBuffers;
+ InBuffer.ulVersion = SECBUFFER_VERSION;
+
+
+ /* output buffer */
+ OutBuffers.pvBuffer = NULL;
+ OutBuffers.BufferType= SECBUFFER_TOKEN;
+ OutBuffers.cbBuffer = 0;
+
+ OutBuffer.cBuffers = 1;
+ OutBuffer.pBuffers = &OutBuffers;
+ OutBuffer.ulVersion = SECBUFFER_VERSION;
+
+
+ rc = InitializeSecurityContextA(&sctx->CredHdl,
+ &sctx->hCtxt,
+ NULL,
+ dwSSPIFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &InBuffer,
+ 0,
+ NULL,
+ &OutBuffer,
+ &dwSSPIOutFlags,
+ &tsExpiry );
+
+
+ if (rc == SEC_E_OK ||
+ rc == SEC_I_CONTINUE_NEEDED ||
+ (FAILED(rc) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)))
+ {
+ if(OutBuffers.cbBuffer && OutBuffers.pvBuffer)
+ {
+ ssize_t nbytes = pvio->methods->write(pvio, (uchar *)OutBuffers.pvBuffer, (size_t)OutBuffers.cbBuffer);
+ if(nbytes <= 0)
+ {
+ FreeContextBuffer(OutBuffers.pvBuffer);
+ DeleteSecurityContext(&sctx->hCtxt);
+ return SEC_E_INTERNAL_ERROR;
+ }
+ cbData= (DWORD)nbytes;
+ /* Free output context buffer */
+ FreeContextBuffer(OutBuffers.pvBuffer);
+ OutBuffers.pvBuffer = NULL;
+ }
+ }
+ /* check if we need to read more data */
+ switch (rc) {
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* we didn't receive all data, so just continue loop */
+ continue;
+ break;
+ case SEC_E_OK:
+ /* handshake completed, but we need to check if extra
+ data was sent (which contains encrypted application data) */
+ if (InBuffers[1].BufferType == SECBUFFER_EXTRA)
+ {
+ if (!(pExtraData->pvBuffer= LocalAlloc(0, InBuffers[1].cbBuffer)))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ MoveMemory(pExtraData->pvBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
+ pExtraData->BufferType = SECBUFFER_TOKEN;
+ pExtraData->cbBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ {
+ pExtraData->BufferType= SECBUFFER_EMPTY;
+ pExtraData->pvBuffer= NULL;
+ pExtraData->cbBuffer= 0;
+ }
+ break;
+
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ /* Provided credentials didn't contain a valid client certificate.
+ We will try to connect anonymously, using current credentials */
+ fDoRead= FALSE;
+ rc= SEC_I_CONTINUE_NEEDED;
+ continue;
+ break;
+ default:
+ if (FAILED(rc))
+ {
+ goto loopend;
+ }
+ break;
+ }
+
+ if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
+ {
+ MoveMemory( IoBuffer, IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), InBuffers[1].cbBuffer );
+ cbIoBuffer = InBuffers[1].cbBuffer;
+ }
+ else
+ cbIoBuffer = 0;
+ }
+loopend:
+ if (FAILED(rc))
+ {
+ ma_schannel_set_sec_error(pvio, rc);
+ DeleteSecurityContext(&sctx->hCtxt);
+ }
+ free(IoBuffer);
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls) */
+/*
+ performs client side handshake
+
+ SYNOPSIS
+ ma_schannel_client_handshake()
+ ctls Pointer to a MARIADB_TLS structure
+
+ DESCRIPTION
+ initiates a client/server handshake. This function can be used
+ by clients only
+
+ RETURN
+ SEC_E_OK on success
+*/
+
+SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls)
+{
+ MARIADB_PVIO *pvio;
+ SECURITY_STATUS sRet;
+ DWORD OutFlags;
+ SC_CTX *sctx;
+ SecBuffer ExtraData;
+ DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
+ ISC_REQ_USE_SUPPLIED_CREDS |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
+
+ SecBufferDesc BufferOut;
+ SecBuffer BuffersOut;
+
+ if (!ctls || !ctls->pvio)
+ return 1;
+
+ pvio= ctls->pvio;
+ sctx= (SC_CTX *)ctls->ssl;
+
+ /* Initialie securifty context */
+ BuffersOut.BufferType= SECBUFFER_TOKEN;
+ BuffersOut.cbBuffer= 0;
+ BuffersOut.pvBuffer= NULL;
+
+
+ BufferOut.cBuffers= 1;
+ BufferOut.pBuffers= &BuffersOut;
+ BufferOut.ulVersion= SECBUFFER_VERSION;
+
+ sRet = InitializeSecurityContext(&sctx->CredHdl,
+ NULL,
+ pvio->mysql->host,
+ SFlags,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &sctx->hCtxt,
+ &BufferOut,
+ &OutFlags,
+ NULL);
+
+ if(sRet != SEC_I_CONTINUE_NEEDED)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ return sRet;
+ }
+
+ /* send client hello */
+ if(BuffersOut.cbBuffer != 0 && BuffersOut.pvBuffer != NULL)
+ {
+ ssize_t nbytes = (DWORD)pvio->methods->write(pvio, (uchar *)BuffersOut.pvBuffer, (size_t)BuffersOut.cbBuffer);
+
+ if (nbytes <= 0)
+ {
+ sRet= SEC_E_INTERNAL_ERROR;
+ goto end;
+ }
+ }
+ sRet= ma_schannel_handshake_loop(pvio, TRUE, &ExtraData);
+
+ /* allocate IO-Buffer for write operations: After handshake
+ was successful, we are able now to calculate payload */
+ if ((sRet = QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes )))
+ goto end;
+
+ sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
+ if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, sctx->IoBufferSize)))
+ {
+ sRet= SEC_E_INSUFFICIENT_MEMORY;
+ goto end;
+ }
+
+ return sRet;
+end:
+ if (BuffersOut.pvBuffer)
+ FreeContextBuffer(BuffersOut.pvBuffer);
+ return sRet;
+}
+/* }}} */
+
+/* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext,
+ DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
+/*
+ Reads encrypted data from a SSL stream and decrypts it.
+
+ SYNOPSIS
+ ma_schannel_read
+ pvio pointer to Communication IO structure
+ phContext a context handle
+ DecryptLength size of decrypted buffer
+ ReadBuffer Buffer for decrypted data
+ ReadBufferSize size of ReadBuffer
+
+
+ DESCRIPTION
+ Reads decrypted data from a SSL stream and encrypts it.
+
+ RETURN
+ SEC_E_OK on success
+ SEC_E_* if an error occurred
+*/
+
+SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
+ CtxtHandle * phContext,
+ DWORD *DecryptLength,
+ uchar *ReadBuffer,
+ DWORD ReadBufferSize)
+{
+ ssize_t nbytes = 0;
+ DWORD dwOffset = 0;
+ SC_CTX *sctx;
+ SECURITY_STATUS sRet = 0;
+ SecBufferDesc Msg;
+ SecBuffer Buffers[4];
+ int i;
+
+ if (!pvio || !pvio->methods || !pvio->methods->read || !pvio->ctls || !DecryptLength)
+ return SEC_E_INTERNAL_ERROR;
+
+ sctx = (SC_CTX *)pvio->ctls->ssl;
+ *DecryptLength = 0;
+
+ if (sctx->dataBuf.cbBuffer)
+ {
+ /* Have unread decrypted data from the last time, copy. */
+ nbytes = MIN(ReadBufferSize, sctx->dataBuf.cbBuffer);
+ memcpy(ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
+ sctx->dataBuf.pvBuffer = (char *)(sctx->dataBuf.pvBuffer) + nbytes;
+ sctx->dataBuf.cbBuffer -= (DWORD)nbytes;
+ *DecryptLength = (DWORD)nbytes;
+ return SEC_E_OK;
+ }
+
+
+ while (1)
+ {
+ /* Check for any encrypted data returned by last DecryptMessage() in SECBUFFER_EXTRA buffer. */
+ if (sctx->extraBuf.cbBuffer)
+ {
+ memmove(sctx->IoBuffer, sctx->extraBuf.pvBuffer, sctx->extraBuf.cbBuffer);
+ dwOffset = sctx->extraBuf.cbBuffer;
+ sctx->extraBuf.cbBuffer = 0;
+ }
+
+ do {
+ assert(sctx->IoBufferSize > dwOffset);
+ if (dwOffset == 0 || sRet == SEC_E_INCOMPLETE_MESSAGE)
+ {
+ nbytes = pvio->methods->read(pvio, sctx->IoBuffer + dwOffset, (size_t)(sctx->IoBufferSize - dwOffset));
+ if (nbytes <= 0)
+ {
+ /* server closed connection, or an error */
+ // todo: error
+ return SEC_E_INVALID_HANDLE;
+ }
+ dwOffset += (DWORD)nbytes;
+ }
+ ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
+ Buffers[0].pvBuffer = sctx->IoBuffer;
+ Buffers[0].cbBuffer = dwOffset;
+
+ Buffers[0].BufferType = SECBUFFER_DATA;
+ Buffers[1].BufferType = SECBUFFER_EMPTY;
+ Buffers[2].BufferType = SECBUFFER_EMPTY;
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Msg.ulVersion = SECBUFFER_VERSION; // Version number
+ Msg.cBuffers = 4;
+ Msg.pBuffers = Buffers;
+
+ sRet = DecryptMessage(phContext, &Msg, 0, NULL);
+
+ } while (sRet == SEC_E_INCOMPLETE_MESSAGE); /* Continue reading until full message arrives */
+
+
+ if (sRet != SEC_E_OK)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ return sRet;
+ }
+
+ sctx->extraBuf.cbBuffer = 0;
+ sctx->dataBuf.cbBuffer = 0;
+ for (i = 0; i < 4; i++)
+ {
+ if (Buffers[i].BufferType == SECBUFFER_DATA)
+ sctx->dataBuf = Buffers[i];
+ if (Buffers[i].BufferType == SECBUFFER_EXTRA)
+ sctx->extraBuf = Buffers[i];
+ }
+
+
+ if (sctx->dataBuf.cbBuffer)
+ {
+ assert(sctx->dataBuf.pvBuffer);
+ /*
+ Copy at most ReadBufferSize bytes to output.
+ Store the rest (if any) to be processed next time.
+ */
+ nbytes = MIN(sctx->dataBuf.cbBuffer, ReadBufferSize);
+ memcpy((char *)ReadBuffer, sctx->dataBuf.pvBuffer, nbytes);
+ sctx->dataBuf.cbBuffer -= (unsigned long)nbytes;
+ sctx->dataBuf.pvBuffer = (char *)sctx->dataBuf.pvBuffer + nbytes;
+
+ *DecryptLength = (DWORD)nbytes;
+ return SEC_E_OK;
+ }
+ // No data buffer, loop
+ }
+}
+/* }}} */
+#include "win32_errmsg.h"
+my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name)
+{
+ SECURITY_STATUS status;
+
+ MARIADB_PVIO *pvio= ctls->pvio;
+ MYSQL *mysql= pvio->mysql;
+ SC_CTX *sctx = (SC_CTX *)ctls->ssl;
+ const char *ca_file= mysql->options.ssl_ca;
+ const char* ca_path = mysql->options.ssl_capath;
+ const char *crl_file= mysql->options.extension ? mysql->options.extension->ssl_crl : NULL;
+ const char* crl_path = mysql->options.extension ? mysql->options.extension->ssl_crlpath : NULL;
+ PCCERT_CONTEXT pServerCert= NULL;
+ char errmsg[256];
+ HCERTSTORE store= NULL;
+ int ret= 0;
+
+ status = schannel_create_store(ca_file, ca_path, crl_file, crl_path, &store, errmsg, sizeof(errmsg));
+ if(status)
+ goto end;
+
+ status = QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert);
+ if (status)
+ {
+ ma_format_win32_error(errmsg, sizeof(errmsg), GetLastError(),
+ "QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed.");
+ goto end;
+ }
+
+ status = schannel_verify_server_certificate(
+ pServerCert,
+ store,
+ crl_file != 0 || crl_path != 0,
+ mysql->host,
+ verify_server_name,
+ errmsg, sizeof(errmsg));
+
+ if (status)
+ goto end;
+
+ ret= 1;
+
+end:
+ if (!ret)
+ {
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, errmsg);
+ }
+ if (pServerCert)
+ CertFreeCertificateContext(pServerCert);
+ if(store)
+ schannel_free_store(store);
+ return ret;
+}
+
+
+/* {{{ size_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio, PCredHandle phCreds, CtxtHandle * phContext) */
+/*
+ Decrypts data and write to SSL stream
+ SYNOPSIS
+ ma_schannel_write_decrypt
+ pvio pointer to Communication IO structure
+ phContext a context handle
+ DecryptLength size of decrypted buffer
+ ReadBuffer Buffer for decrypted data
+ ReadBufferSize size of ReadBuffer
+
+ DESCRIPTION
+ Write encrypted data to SSL stream.
+
+ RETURN
+ SEC_E_OK on success
+ SEC_E_* if an error occurred
+*/
+ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
+ uchar *WriteBuffer,
+ size_t WriteBufferSize)
+{
+ SECURITY_STATUS scRet;
+ SecBufferDesc Message;
+ SecBuffer Buffers[4];
+ SC_CTX *sctx= (SC_CTX *)pvio->ctls->ssl;
+ size_t payload;
+ ssize_t nbytes;
+ DWORD write_size;
+
+ payload= MIN(WriteBufferSize, sctx->Sizes.cbMaximumMessage);
+
+ memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
+
+ Buffers[0].pvBuffer = sctx->IoBuffer;
+ Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
+ Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
+
+ Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
+ Buffers[1].cbBuffer = (DWORD)payload;
+ Buffers[1].BufferType = SECBUFFER_DATA;
+
+ Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
+ Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
+ Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+ Buffers[3].pvBuffer = SECBUFFER_EMPTY;
+ Buffers[3].cbBuffer = SECBUFFER_EMPTY;
+ Buffers[3].BufferType = SECBUFFER_EMPTY;
+
+ Message.ulVersion = SECBUFFER_VERSION;
+ Message.cBuffers = 4;
+ Message.pBuffers = Buffers;
+ if ((scRet = EncryptMessage(&sctx->hCtxt, 0, &Message, 0))!= SEC_E_OK)
+ return -1;
+ write_size = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
+ nbytes = pvio->methods->write(pvio, sctx->IoBuffer, write_size);
+ return nbytes == write_size ? payload : -1;
+}
+/* }}} */
+
+extern char *ssl_protocol_version[5];
+
+/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
+int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ SC_CTX *sctx;
+ SecPkgContext_ConnectionInfo ConnectionInfo;
+ if (!ctls->ssl)
+ return 1;
+
+ sctx= (SC_CTX *)ctls->ssl;
+
+ if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
+ return -1;
+
+ switch(ConnectionInfo.dwProtocol)
+ {
+ case SP_PROT_SSL3_CLIENT:
+ return PROTOCOL_SSLV3;
+ case SP_PROT_TLS1_CLIENT:
+ return PROTOCOL_TLS_1_0;
+ case SP_PROT_TLS1_1_CLIENT:
+ return PROTOCOL_TLS_1_1;
+ case SP_PROT_TLS1_2_CLIENT:
+ return PROTOCOL_TLS_1_2;
+ default:
+ return -1;
+ }
+}
+/* }}} */
diff --git a/libmariadb/libmariadb/secure/ma_schannel.h b/libmariadb/libmariadb/secure/ma_schannel.h
new file mode 100644
index 00000000..af7fc602
--- /dev/null
+++ b/libmariadb/libmariadb/secure/ma_schannel.h
@@ -0,0 +1,87 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ Author: Georg Richter
+
+ *************************************************************************************/
+#ifndef _ma_schannel_h_
+#define _ma_schannel_h_
+
+#define SECURITY_WIN32
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <errmsg.h>
+
+
+#include <wincrypt.h>
+#include <wintrust.h>
+
+
+#include <security.h>
+
+#include <schnlsp.h>
+#undef SECURITY_WIN32
+#include <windows.h>
+#include <sspi.h>
+
+#define SC_IO_BUFFER_SIZE 0x4000
+
+
+#include <ma_pthread.h>
+
+struct st_DER {
+ char* der_buffer;
+ DWORD der_length;
+};
+
+struct st_schannel {
+ CredHandle CredHdl;
+ PUCHAR IoBuffer;
+ DWORD IoBufferSize;
+ SecPkgContext_StreamSizes Sizes;
+ CtxtHandle hCtxt;
+
+ /* Cached data from the last read/decrypt call.*/
+ SecBuffer extraBuf; /* encrypted data read from server. */
+ SecBuffer dataBuf; /* decrypted but still unread data from server.*/
+
+};
+
+typedef struct st_schannel SC_CTX;
+
+extern HCERTSTORE ca_CertStore, crl_CertStore;
+extern my_bool ca_Check, crl_Check;
+
+;
+SECURITY_STATUS ma_schannel_client_handshake(MARIADB_TLS *ctls);
+SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_PVIO *pvio, my_bool InitialRead, SecBuffer *pExtraData);
+
+my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls, BOOL verify_server_name);
+ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
+ uchar *WriteBuffer,
+ size_t WriteBufferSize);
+SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_PVIO *pvio,
+ CtxtHandle* phContext,
+ DWORD *DecryptLength,
+ uchar *ReadBuffer,
+ DWORD ReadBufferSize);
+
+
+#endif /* _ma_schannel_h_ */
diff --git a/libmariadb/libmariadb/secure/openssl.c b/libmariadb/libmariadb/secure/openssl.c
new file mode 100644
index 00000000..2a272504
--- /dev/null
+++ b/libmariadb/libmariadb/secure/openssl.c
@@ -0,0 +1,786 @@
+/************************************************************************************
+ Copyright (C) 2012 Monty Program 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <ma_common.h>
+#include <ma_pvio.h>
+#include <errmsg.h>
+#include <string.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <openssl/ssl.h> /* SSL and SSL_CTX */
+#include <openssl/err.h> /* error reporting */
+#include <openssl/conf.h>
+#include <openssl/md4.h>
+
+#if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C)
+#include <openssl/applink.c>
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+#include <openssl/x509v3.h>
+#define HAVE_OPENSSL_CHECK_HOST 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#define HAVE_OPENSSL_1_1_API
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+#define SSL_OP_NO_TLSv1_1 0L
+#define SSL_OP_NO_TLSv1_2 0L
+#define CRYPTO_THREADID_set_callback CRYPTO_set_id_callback
+#define CRYPTO_THREADID_get_callback CRYPTO_get_id_callback
+#endif
+
+#if defined(OPENSSL_USE_BIOMETHOD)
+#undef OPENSSL_USE_BIOMETHOD
+#endif
+#ifndef HAVE_OPENSSL_DEFAULT
+#include <memory.h>
+#define ma_malloc(A,B) malloc((A))
+#undef ma_free
+#define ma_free(A) free((A))
+#define ma_snprintf snprintf
+#define ma_vsnprintf vsnprintf
+#undef SAFE_MUTEX
+#endif
+#include <ma_pthread.h>
+
+#include <mariadb_async.h>
+#include <ma_context.h>
+
+extern my_bool ma_tls_initialized;
+extern unsigned int mariadb_deinitialize_ssl;
+
+#define MAX_SSL_ERR_LEN 100
+char tls_library_version[TLS_VERSION_LENGTH];
+
+static pthread_mutex_t LOCK_openssl_config;
+#ifndef HAVE_OPENSSL_1_1_API
+static pthread_mutex_t *LOCK_crypto= NULL;
+#endif
+#if defined(OPENSSL_USE_BIOMETHOD)
+static int ma_bio_read(BIO *h, char *buf, int size);
+static int ma_bio_write(BIO *h, const char *buf, int size);
+static BIO_METHOD ma_BIO_method;
+#endif
+
+
+static long ma_tls_version_options(const char *version)
+{
+ long protocol_options,
+ disable_all_protocols;
+
+ protocol_options= disable_all_protocols=
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 |
+ SSL_OP_NO_TLSv1_1 |
+ SSL_OP_NO_TLSv1_2
+#ifdef TLS1_3_VERSION
+ | SSL_OP_NO_TLSv1_3
+#endif
+ ;
+
+ if (!version)
+ return 0;
+
+ if (strstr(version, "TLSv1.0"))
+ protocol_options&= ~SSL_OP_NO_TLSv1;
+ if (strstr(version, "TLSv1.1"))
+ protocol_options&= ~SSL_OP_NO_TLSv1_1;
+ if (strstr(version, "TLSv1.2"))
+ protocol_options&= ~SSL_OP_NO_TLSv1_2;
+#ifdef TLS1_3_VERSION
+ if (strstr(version, "TLSv1.3"))
+ protocol_options&= ~SSL_OP_NO_TLSv1_3;
+#endif
+
+ if (protocol_options != disable_all_protocols)
+ return protocol_options;
+ return 0;
+}
+
+static void ma_tls_set_error(MYSQL *mysql)
+{
+ ulong ssl_errno= ERR_get_error();
+ char ssl_error[MAX_SSL_ERR_LEN];
+ const char *ssl_error_reason;
+ MARIADB_PVIO *pvio= mysql->net.pvio;
+ int save_errno= errno;
+
+ if (ssl_errno && (ssl_error_reason= ERR_reason_error_string(ssl_errno)))
+ {
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ 0, ssl_error_reason);
+ return;
+ } else if (!save_errno) {
+ pvio->set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN,
+ ER(CR_SERVER_LOST));
+ return;
+ }
+
+ strerror_r(save_errno, ssl_error, MAX_SSL_ERR_LEN);
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "TLS/SSL error: %s (%d)",
+ ssl_error, save_errno);
+ return;
+}
+
+#ifndef HAVE_OPENSSL_1_1_API
+static void my_cb_locking(int mode, int n,
+ const char *file __attribute__((unused)),
+ int line __attribute__((unused)))
+{
+ if (mode & CRYPTO_LOCK)
+ pthread_mutex_lock(&LOCK_crypto[n]);
+ else
+ pthread_mutex_unlock(&LOCK_crypto[n]);
+}
+
+static int ssl_thread_init()
+{
+ if (LOCK_crypto == NULL)
+ {
+ int i, max= CRYPTO_num_locks();
+
+ if (!(LOCK_crypto=
+ (pthread_mutex_t *)ma_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
+ return 1;
+
+ for (i=0; i < max; i++)
+ pthread_mutex_init(&LOCK_crypto[i], NULL);
+
+ CRYPTO_set_locking_callback(my_cb_locking);
+ }
+ return 0;
+}
+#endif
+
+#if defined(_WIN32) || !defined(DISABLE_SIGPIPE)
+#define disable_sigpipe()
+#else
+#include <signal.h>
+static void ma_sigpipe_handler()
+{
+}
+
+static void disable_sigpipe()
+{
+ struct sigaction old_handler, new_handler={NULL};
+ if (!sigaction (SIGPIPE, NULL, &old_handler) &&
+ !old_handler.sa_handler)
+ {
+ new_handler.sa_handler= ma_sigpipe_handler;
+ new_handler.sa_flags= 0;
+ if (!sigemptyset(&new_handler.sa_mask))
+ sigaction(SIGPIPE, &new_handler, NULL);
+ }
+}
+#endif
+
+/*
+ Initializes SSL
+
+ SYNOPSIS
+ my_ssl_start
+ mysql connection handle
+
+ RETURN VALUES
+ 0 success
+ 1 error
+*/
+int ma_tls_start(char *errmsg __attribute__((unused)), size_t errmsg_len __attribute__((unused)))
+{
+ int rc= 1;
+ char *p;
+ if (ma_tls_initialized)
+ return 0;
+
+ /* lock mutex to prevent multiple initialization */
+ pthread_mutex_init(&LOCK_openssl_config, NULL);
+ pthread_mutex_lock(&LOCK_openssl_config);
+#ifdef HAVE_OPENSSL_1_1_API
+ if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL))
+ goto end;
+#else
+ if (ssl_thread_init())
+ {
+ strncpy(errmsg, "Not enough memory", errmsg_len);
+ goto end;
+ }
+ SSL_library_init();
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+ OPENSSL_config(NULL);
+#endif
+#endif
+#ifndef HAVE_OPENSSL_1_1_API
+ /* load errors */
+ SSL_load_error_strings();
+ /* digests and ciphers */
+ OpenSSL_add_all_algorithms();
+#endif
+ disable_sigpipe();
+#ifdef OPENSSL_USE_BIOMETHOD
+ memcpy(&ma_BIO_method, BIO_s_socket(), sizeof(BIO_METHOD));
+ ma_BIO_method.bread= ma_bio_read;
+ ma_BIO_method.bwrite= ma_bio_write;
+#endif
+ snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "%s",
+#if defined(LIBRESSL_VERSION_NUMBER) || !defined(HAVE_OPENSSL_1_1_API)
+ SSLeay_version(SSLEAY_VERSION));
+#else
+ OpenSSL_version(OPENSSL_VERSION));
+#endif
+ /* remove date from version */
+ if ((p= strstr(tls_library_version, " ")))
+ *p= 0;
+ rc= 0;
+ ma_tls_initialized= TRUE;
+end:
+ pthread_mutex_unlock(&LOCK_openssl_config);
+ return rc;
+}
+
+/*
+ Release SSL and free resources
+ Will be automatically executed by
+ mysql_server_end() function
+
+ SYNOPSIS
+ my_ssl_end()
+ void
+
+ RETURN VALUES
+ void
+*/
+void ma_tls_end()
+{
+ if (ma_tls_initialized)
+ {
+ pthread_mutex_lock(&LOCK_openssl_config);
+#ifndef HAVE_OPENSSL_1_1_API
+ if (LOCK_crypto)
+ {
+ int i;
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_THREADID_set_callback(NULL);
+
+ for (i=0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_destroy(&LOCK_crypto[i]);
+ ma_free((gptr)LOCK_crypto);
+ LOCK_crypto= NULL;
+ }
+#endif
+ if (mariadb_deinitialize_ssl)
+ {
+#ifndef HAVE_OPENSSL_1_1_API
+ ERR_remove_thread_state(NULL);
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_free_strings();
+ CONF_modules_free();
+ CONF_modules_unload(1);
+#endif
+ }
+ ma_tls_initialized= FALSE;
+ pthread_mutex_unlock(&LOCK_openssl_config);
+ pthread_mutex_destroy(&LOCK_openssl_config);
+ }
+ return;
+}
+
+int ma_tls_get_password(char *buf, int size,
+ int rwflag __attribute__((unused)),
+ void *userdata)
+{
+ memset(buf, 0, size);
+ if (userdata)
+ strncpy(buf, (char *)userdata, size);
+ return (int)strlen(buf);
+}
+
+
+static int ma_tls_set_certs(MYSQL *mysql, SSL_CTX *ctx)
+{
+ char *certfile= mysql->options.ssl_cert,
+ *keyfile= mysql->options.ssl_key;
+ char *pw= (mysql->options.extension) ?
+ mysql->options.extension->tls_pw : NULL;
+
+ /* add cipher */
+ if ((mysql->options.ssl_cipher &&
+ mysql->options.ssl_cipher[0] != 0))
+ {
+ if(
+#ifdef TLS1_3_VERSION
+ SSL_CTX_set_ciphersuites(ctx, mysql->options.ssl_cipher) == 0 &&
+#endif
+ SSL_CTX_set_cipher_list(ctx, mysql->options.ssl_cipher) == 0)
+ goto error;
+ }
+
+ /* ca_file and ca_path */
+ if (!SSL_CTX_load_verify_locations(ctx,
+ mysql->options.ssl_ca,
+ mysql->options.ssl_capath))
+ {
+ if (mysql->options.ssl_ca || mysql->options.ssl_capath)
+ goto error;
+ if (SSL_CTX_set_default_verify_paths(ctx) == 0)
+ goto error;
+ }
+
+ if (mysql->options.extension &&
+ (mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
+ {
+ X509_STORE *certstore;
+
+ if ((certstore= SSL_CTX_get_cert_store(ctx)))
+ {
+ if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
+ mysql->options.extension->ssl_crlpath) == 0)
+ goto error;
+
+ if (X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0)
+ goto error;
+ }
+ }
+
+ if (keyfile && !certfile)
+ certfile= keyfile;
+ if (certfile && !keyfile)
+ keyfile= certfile;
+
+ /* set cert */
+ if (certfile && certfile[0] != 0)
+ {
+ if (SSL_CTX_use_certificate_chain_file(ctx, certfile) != 1)
+ {
+ goto error;
+ }
+ }
+
+ if (keyfile && keyfile[0])
+ {
+ FILE *fp;
+ if ((fp= fopen(keyfile, "rb")))
+ {
+ EVP_PKEY *key= EVP_PKEY_new();
+ PEM_read_PrivateKey(fp, &key, NULL, pw);
+ fclose(fp);
+ if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
+ {
+ unsigned long err= ERR_peek_error();
+ EVP_PKEY_free(key);
+ if (!(ERR_GET_LIB(err) == ERR_LIB_X509 &&
+ ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE))
+ goto error;
+ }
+ EVP_PKEY_free(key);
+ } else {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ CER(CR_FILE_NOT_FOUND), keyfile);
+ return 1;
+ }
+ }
+ /* verify key */
+ if (certfile && SSL_CTX_check_private_key(ctx) != 1)
+ goto error;
+
+ SSL_CTX_set_verify(ctx, (mysql->options.ssl_ca || mysql->options.ssl_capath) ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+ return 0;
+
+error:
+ ma_tls_set_error(mysql);
+ return 1;
+}
+
+void *ma_tls_init(MYSQL *mysql)
+{
+ SSL *ssl= NULL;
+ SSL_CTX *ctx= NULL;
+ long default_options= SSL_OP_ALL |
+ SSL_OP_NO_SSLv2 |
+ SSL_OP_NO_SSLv3;
+ long options= 0;
+ pthread_mutex_lock(&LOCK_openssl_config);
+
+ #if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (!(ctx= SSL_CTX_new(TLS_client_method())))
+#else
+ if (!(ctx= SSL_CTX_new(SSLv23_client_method())))
+#endif
+ goto error;
+ if (mysql->options.extension)
+ options= ma_tls_version_options(mysql->options.extension->tls_version);
+ SSL_CTX_set_options(ctx, options ? options : default_options);
+
+ if (ma_tls_set_certs(mysql, ctx))
+ {
+ goto error;
+ }
+
+ if (!(ssl= SSL_new(ctx)))
+ goto error;
+
+ if (!SSL_set_app_data(ssl, mysql))
+ goto error;
+
+ pthread_mutex_unlock(&LOCK_openssl_config);
+ return (void *)ssl;
+error:
+ pthread_mutex_unlock(&LOCK_openssl_config);
+ if (ctx)
+ SSL_CTX_free(ctx);
+ if (ssl)
+ SSL_free(ssl);
+ return NULL;
+}
+
+my_bool ma_tls_connect(MARIADB_TLS *ctls)
+{
+ SSL *ssl = (SSL *)ctls->ssl;
+ my_bool blocking, try_connect= 1;
+ MYSQL *mysql;
+ MARIADB_PVIO *pvio;
+ int rc;
+#ifdef OPENSSL_USE_BIOMETHOD
+ BIO_METHOD *bio_method= NULL;
+ BIO *bio;
+#endif
+
+ mysql= (MYSQL *)SSL_get_app_data(ssl);
+ pvio= mysql->net.pvio;
+
+ /* Set socket to non blocking if not already set */
+ if (!(blocking= pvio->methods->is_blocking(pvio)))
+ pvio->methods->blocking(pvio, FALSE, 0);
+
+ SSL_clear(ssl);
+
+#ifdef OPENSSL_USE_BIOMETHOD
+ bio= BIO_new(&ma_BIO_method);
+ bio->ptr= pvio;
+ SSL_set_bio(ssl, bio, bio);
+ BIO_set_fd(bio, mysql_get_socket(mysql), BIO_NOCLOSE);
+#else
+ SSL_set_fd(ssl, (int)mysql_get_socket(mysql));
+#endif
+
+ while (try_connect && (rc= SSL_connect(ssl)) == -1)
+ {
+ switch((SSL_get_error(ssl, rc))) {
+ case SSL_ERROR_WANT_READ:
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
+ try_connect= 0;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1)
+ try_connect= 0;
+ break;
+ default:
+ try_connect= 0;
+ }
+ }
+
+ /* In case handshake failed or if a root certificate (ca) was specified,
+ we need to check the result code of X509 verification. A detailed check
+ of the peer certificate (hostname checking will follow later) */
+ if (rc != 1 || mysql->options.extension->tls_verify_server_cert ||
+ mysql->options.ssl_ca || mysql->options.ssl_capath)
+ {
+ long x509_err= SSL_get_verify_result(ssl);
+ if (x509_err != X509_V_OK)
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err));
+ /* restore blocking mode */
+ if (!blocking)
+ pvio->methods->blocking(pvio, FALSE, 0);
+
+ return 1;
+ } else if (rc != 1) {
+ ma_tls_set_error(mysql);
+ return 1;
+ }
+ }
+ pvio->ctls->ssl= ctls->ssl= (void *)ssl;
+
+ return 0;
+}
+
+static my_bool
+ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl)
+{
+ int ssl_err;
+ b->events_to_wait_for= 0;
+ if (res >= 0)
+ return 1;
+ ssl_err= SSL_get_error(ssl, res);
+ if (ssl_err == SSL_ERROR_WANT_READ)
+ b->events_to_wait_for|= MYSQL_WAIT_READ;
+ else if (ssl_err == SSL_ERROR_WANT_WRITE)
+ b->events_to_wait_for|= MYSQL_WAIT_WRITE;
+ else
+ return 1;
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data);
+ my_context_yield(&b->async_context);
+ if (b->suspend_resume_hook)
+ (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data);
+ return 0;
+}
+
+ssize_t ma_tls_read_async(MARIADB_PVIO *pvio,
+ const unsigned char *buffer,
+ size_t length)
+{
+ int res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ MARIADB_TLS *ctls= pvio->ctls;
+
+ for (;;)
+ {
+ res= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length);
+ if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
+ return res;
+ }
+}
+
+ssize_t ma_tls_write_async(MARIADB_PVIO *pvio,
+ const unsigned char *buffer,
+ size_t length)
+{
+ int res;
+ struct mysql_async_context *b= pvio->mysql->options.extension->async_context;
+ MARIADB_TLS *ctls= pvio->ctls;
+
+ for (;;)
+ {
+ res= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length);
+ if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl))
+ return res;
+ }
+}
+
+
+ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ int rc;
+ MARIADB_PVIO *pvio= ctls->pvio;
+
+ while ((rc= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length)) <= 0)
+ {
+ int error= SSL_get_error((SSL *)ctls->ssl, rc);
+ if (error != SSL_ERROR_WANT_READ)
+ break;
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1)
+ break;
+ }
+ if (rc <= 0)
+ {
+ MYSQL *mysql= SSL_get_app_data(ctls->ssl);
+ ma_tls_set_error(mysql);
+ }
+ return rc;
+}
+
+ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ int rc;
+ MARIADB_PVIO *pvio= ctls->pvio;
+
+ while ((rc= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length)) <= 0)
+ {
+ int error= SSL_get_error((SSL *)ctls->ssl, rc);
+ if (error != SSL_ERROR_WANT_WRITE)
+ break;
+ if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1)
+ break;
+ }
+ if (rc <= 0)
+ {
+ MYSQL *mysql= SSL_get_app_data(ctls->ssl);
+ ma_tls_set_error(mysql);
+ }
+ return rc;
+}
+
+my_bool ma_tls_close(MARIADB_TLS *ctls)
+{
+ int i, rc;
+ SSL *ssl;
+ SSL_CTX *ctx= NULL;
+
+ if (!ctls || !ctls->ssl)
+ return 1;
+ ssl= (SSL *)ctls->ssl;
+ ctx= SSL_get_SSL_CTX(ssl);
+ if (ctx)
+ SSL_CTX_free(ctx);
+
+ SSL_set_quiet_shutdown(ssl, 1);
+ /* 2 x pending + 2 * data = 4 */
+ for (i=0; i < 4; i++)
+ if ((rc= SSL_shutdown(ssl)))
+ break;
+
+ /* Since we transferred ownership of BIO to ssl, BIO will
+ automatically freed - no need for an explicit BIO_free_all */
+
+ SSL_free(ssl);
+ ctls->ssl= NULL;
+
+ return rc;
+}
+
+int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
+{
+ X509 *cert;
+ MYSQL *mysql;
+ SSL *ssl;
+ MARIADB_PVIO *pvio;
+#if !defined(HAVE_OPENSSL_CHECK_HOST)
+ X509_NAME *x509sn;
+ int cn_pos;
+ X509_NAME_ENTRY *cn_entry;
+ ASN1_STRING *cn_asn1;
+ const char *cn_str;
+#endif
+ if (!ctls || !ctls->ssl)
+ return 1;
+ ssl= (SSL *)ctls->ssl;
+
+ mysql= (MYSQL *)SSL_get_app_data(ssl);
+ pvio= mysql->net.pvio;
+
+ if (!mysql->host)
+ {
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname");
+ return 1;
+ }
+
+ if (!(cert= SSL_get_peer_certificate(ssl)))
+ {
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate");
+ return 1;
+ }
+#ifdef HAVE_OPENSSL_CHECK_HOST
+ if (X509_check_host(cert, mysql->host, strlen(mysql->host), 0, 0) != 1
+ && X509_check_ip_asc(cert, mysql->host, 0) != 1)
+ goto error;
+#else
+ x509sn= X509_get_subject_name(cert);
+
+ if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0)
+ goto error;
+
+ if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos)))
+ goto error;
+
+ if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry)))
+ goto error;
+
+ cn_str = (char *)ASN1_STRING_data(cn_asn1);
+
+ /* Make sure there is no embedded \0 in the CN */
+ if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str))
+ goto error;
+
+ if (strcmp(cn_str, mysql->host))
+ goto error;
+#endif
+ X509_free(cert);
+
+ return 0;
+error:
+ X509_free(cert);
+
+ pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed");
+ return 1;
+}
+
+const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
+{
+ if (!ctls || !ctls->ssl)
+ return NULL;
+ return SSL_get_cipher_name(ctls->ssl);
+}
+
+unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
+{
+ X509 *cert= NULL;
+ MYSQL *mysql;
+ unsigned int fp_len;
+
+ if (!ctls || !ctls->ssl)
+ return 0;
+
+ mysql= SSL_get_app_data(ctls->ssl);
+
+ if (!(cert= SSL_get_peer_certificate(ctls->ssl)))
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Unable to get server certificate");
+ goto end;
+ }
+
+ if (len < EVP_MAX_MD_SIZE)
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "Finger print buffer too small");
+ goto end;
+ }
+ if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len))
+ {
+ my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
+ ER(CR_SSL_CONNECTION_ERROR),
+ "invalid finger print of server certificate");
+ goto end;
+ }
+
+ X509_free(cert);
+ return (fp_len);
+end:
+ X509_free(cert);
+ return 0;
+}
+
+
+int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
+{
+ if (!ctls || !ctls->ssl)
+ return -1;
+
+ return SSL_version(ctls->ssl) & 0xFF;
+}
+
+void ma_tls_set_connection(MYSQL *mysql)
+{
+ (void)SSL_set_app_data(mysql->net.pvio->ctls->ssl, mysql);
+}
+
diff --git a/libmariadb/libmariadb/secure/openssl_crypt.c b/libmariadb/libmariadb/secure/openssl_crypt.c
new file mode 100644
index 00000000..faf755c9
--- /dev/null
+++ b/libmariadb/libmariadb/secure/openssl_crypt.c
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*/
+#include <ma_global.h>
+#include <ma_crypt.h>
+#include <openssl/evp.h>
+
+static const EVP_MD *ma_hash_get_algorithm(unsigned int alg)
+{
+ switch(alg)
+ {
+ case MA_HASH_MD5:
+ return EVP_md5();
+ case MA_HASH_SHA1:
+ return EVP_sha1();
+ case MA_HASH_SHA224:
+ return EVP_sha224();
+ case MA_HASH_SHA256:
+ return EVP_sha256();
+ case MA_HASH_SHA384:
+ return EVP_sha384();
+ case MA_HASH_SHA512:
+ return EVP_sha512();
+ case MA_HASH_RIPEMD160:
+ return EVP_ripemd160();
+ default:
+ return NULL;
+ }
+}
+
+MA_HASH_CTX *ma_hash_new(unsigned int algorithm)
+{
+ EVP_MD_CTX *ctx= NULL;
+ const EVP_MD *evp_md= ma_hash_get_algorithm(algorithm);
+
+ /* unknown or unsupported hash algorithm */
+ if (!evp_md)
+ return NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (!(ctx= EVP_MD_CTX_new()))
+#else
+ if (!(ctx= EVP_MD_CTX_create()))
+#endif
+ return NULL;
+ if (!EVP_DigestInit(ctx, evp_md))
+ {
+ ma_hash_free(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void ma_hash_free(MA_HASH_CTX *ctx)
+{
+ if (ctx)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX_destroy(ctx);
+#endif
+}
+
+void ma_hash_input(MA_HASH_CTX *ctx,
+ const unsigned char *buffer,
+ size_t len)
+{
+ EVP_DigestUpdate(ctx, buffer, len);
+}
+
+void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
+{
+ EVP_DigestFinal_ex(ctx, digest, NULL);
+}
diff --git a/libmariadb/libmariadb/secure/schannel.c b/libmariadb/libmariadb/secure/schannel.c
new file mode 100644
index 00000000..8069af53
--- /dev/null
+++ b/libmariadb/libmariadb/secure/schannel.c
@@ -0,0 +1,567 @@
+/************************************************************************************
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+#include "ma_schannel.h"
+#include "schannel_certs.h"
+#include <string.h>
+
+extern my_bool ma_tls_initialized;
+char tls_library_version[] = "Schannel";
+
+#define PROT_SSL3 1
+#define PROT_TLS1_0 2
+#define PROT_TLS1_2 4
+#define PROT_TLS1_3 8
+
+static struct
+{
+ DWORD cipher_id;
+ DWORD protocol;
+ const char *iana_name;
+ const char *openssl_name;
+ ALG_ID algs[4]; /* exchange, encryption, hash, signature */
+}
+cipher_map[] =
+{
+ {
+ 0x0002,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_NULL_SHA", "NULL-SHA",
+ { CALG_RSA_KEYX, 0, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0004,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_RC4_128_MD5", "RC4-MD5",
+ { CALG_RSA_KEYX, CALG_RC4, CALG_MD5, CALG_RSA_SIGN }
+ },
+ {
+ 0x0005,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_RSA_WITH_RC4_128_SHA", "RC4-SHA",
+ { CALG_RSA_KEYX, CALG_RC4, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x000A,
+ PROT_SSL3,
+ "TLS_RSA_WITH_3DES_EDE_CBC_SHA", "DES-CBC3-SHA",
+ {CALG_RSA_KEYX, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN}
+ },
+ {
+ 0x0013,
+ PROT_TLS1_0 | PROT_TLS1_2 | PROT_SSL3,
+ "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "EDH-DSS-DES-CBC3-SHA",
+ { CALG_DH_EPHEM, CALG_3DES, CALG_SHA1, CALG_DSS_SIGN }
+ },
+ {
+ 0x002F,
+ PROT_SSL3 | PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_CBC_SHA", "AES128-SHA",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA, CALG_RSA_SIGN}
+ },
+ {
+ 0x0032,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "DHE-DSS-AES128-SHA",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0033,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "DHE-RSA-AES128-SHA",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0035,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_CBC_SHA", "AES256-SHA",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x0038,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "DHE-DSS-AES256-SHA",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_DSS_SIGN }
+ },
+ {
+ 0x0039,
+ PROT_TLS1_0 | PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "DHE-RSA-AES256-SHA",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA1, CALG_RSA_SIGN }
+ },
+ {
+ 0x003B,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256",
+ { CALG_RSA_KEYX, 0, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x003C,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x003D,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x0040,
+ PROT_TLS1_2,
+ "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_DSS_SIGN }
+ },
+ {
+ 0x009C,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256",
+ { CALG_RSA_KEYX, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x009D,
+ PROT_TLS1_2,
+ "TLS_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384",
+ { CALG_RSA_KEYX, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
+ },
+ {
+ 0x009E,
+ PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256",
+ { CALG_DH_EPHEM, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0x009F,
+ PROT_TLS1_2,
+ "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384",
+ { CALG_DH_EPHEM, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
+ },
+ {
+ 0xC027,
+ PROT_TLS1_2,
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256",
+ { CALG_ECDH, CALG_AES_128, CALG_SHA_256, CALG_RSA_SIGN }
+ },
+ {
+ 0xC028,
+ PROT_TLS1_2,
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDHE-RSA-AES256-SHA384",
+ { CALG_ECDH, CALG_AES_256, CALG_SHA_384, CALG_RSA_SIGN }
+ }
+};
+
+#define MAX_ALG_ID 50
+
+extern void ma_schannel_set_sec_error(MARIADB_PVIO *pvio, DWORD ErrorNo);
+
+/*
+ Initializes SSL and allocate global
+ context SSL_context
+
+ SYNOPSIS
+ ma_tls_start
+
+ RETURN VALUES
+ 0 success
+ 1 error
+*/
+int ma_tls_start(char *errmsg, size_t errmsg_len)
+{
+ ma_tls_initialized = TRUE;
+ return 0;
+}
+
+/*
+ Release SSL and free resources
+ Will be automatically executed by
+ mysql_server_end() function
+
+ SYNOPSIS
+ ma_tls_end()
+ void
+
+ RETURN VALUES
+ void
+*/
+void ma_tls_end()
+{
+ return;
+}
+
+/* {{{ static int ma_tls_set_client_certs(MARIADB_TLS *ctls) */
+static int ma_tls_set_client_certs(MARIADB_TLS *ctls,const CERT_CONTEXT **cert_ctx)
+{
+ MYSQL *mysql= ctls->pvio->mysql;
+ char *certfile= mysql->options.ssl_cert,
+ *keyfile= mysql->options.ssl_key;
+ MARIADB_PVIO *pvio= ctls->pvio;
+ char errmsg[256];
+
+ if (!certfile && keyfile)
+ certfile= keyfile;
+ if (!keyfile && certfile)
+ keyfile= certfile;
+
+ if (!certfile)
+ return 0;
+
+ *cert_ctx = schannel_create_cert_context(certfile, keyfile, errmsg, sizeof(errmsg));
+ if (!*cert_ctx)
+ {
+ pvio->set_error(pvio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, errmsg);
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ void *ma_tls_init(MARIADB_TLS *ctls, MYSQL *mysql) */
+void *ma_tls_init(MYSQL *mysql)
+{
+ SC_CTX *sctx = (SC_CTX *)LocalAlloc(LMEM_ZEROINIT, sizeof(SC_CTX));
+ if (sctx)
+ {
+ SecInvalidateHandle(&sctx->CredHdl);
+ SecInvalidateHandle(&sctx->hCtxt);
+ }
+ return sctx;
+}
+/* }}} */
+
+
+/*
+ Maps between openssl suite names and schannel alg_ids.
+ Every suite has 4 algorithms (for exchange, encryption, hash and signing).
+
+ The input string is a set of suite names (openssl), separated
+ by ':'
+
+ The output is written into the array 'arr' of size 'arr_size'
+ The function returns number of elements written to the 'arr'.
+*/
+
+static struct _tls_version {
+ const char *tls_version;
+ DWORD protocol;
+} tls_version[]= {
+ {"TLSv1.0", PROT_TLS1_0},
+ {"TLSv1.2", PROT_TLS1_2},
+ {"TLSv1.3", PROT_TLS1_3},
+ {"SSLv3", PROT_SSL3}
+};
+
+/* The following list was produced with OpenSSL 1.1.1j
+ by executing `openssl ciphers -V`. */
+static struct {
+ DWORD dwCipherSuite;
+ const char *openssl_name;
+} openssl_ciphers[] = {
+ {0x002F, "AES128-SHA"},
+ {0x0033, "DHE-RSA-AES128-SHA"},
+ {0x0035, "AES256-SHA"},
+ {0x0039, "DHE-RSA-AES256-SHA"},
+ {0x003C, "AES128-SHA256"},
+ {0x003D, "AES256-SHA256"},
+ {0x0067, "DHE-RSA-AES128-SHA256"},
+ {0x006B, "DHE-RSA-AES256-SHA256"},
+ {0x008C, "PSK-AES128-CBC-SHA"},
+ {0x008D, "PSK-AES256-CBC-SHA"},
+ {0x0090, "DHE-PSK-AES128-CBC-SHA"},
+ {0x0091, "DHE-PSK-AES256-CBC-SHA"},
+ {0x0094, "RSA-PSK-AES128-CBC-SHA"},
+ {0x0095, "RSA-PSK-AES256-CBC-SHA"},
+ {0x009C, "AES128-GCM-SHA256"},
+ {0x009D, "AES256-GCM-SHA384"},
+ {0x009E, "DHE-RSA-AES128-GCM-SHA256"},
+ {0x009F, "DHE-RSA-AES256-GCM-SHA384"},
+ {0x00A8, "PSK-AES128-GCM-SHA256"},
+ {0x00A9, "PSK-AES256-GCM-SHA384"},
+ {0x00AA, "DHE-PSK-AES128-GCM-SHA256"},
+ {0x00AB, "DHE-PSK-AES256-GCM-SHA384"},
+ {0x00AC, "RSA-PSK-AES128-GCM-SHA256"},
+ {0x00AD, "RSA-PSK-AES256-GCM-SHA384"},
+ {0x00AE, "PSK-AES128-CBC-SHA256"},
+ {0x00AF, "PSK-AES256-CBC-SHA384"},
+ {0x00B2, "DHE-PSK-AES128-CBC-SHA256"},
+ {0x00B3, "DHE-PSK-AES256-CBC-SHA384"},
+ {0x00B6, "RSA-PSK-AES128-CBC-SHA256"},
+ {0x00B7, "RSA-PSK-AES256-CBC-SHA384"},
+ {0x1301, "TLS_AES_128_GCM_SHA256"},
+ {0x1302, "TLS_AES_256_GCM_SHA384"},
+ {0x1303, "TLS_CHACHA20_POLY1305_SHA256"},
+ {0xC009, "ECDHE-ECDSA-AES128-SHA"},
+ {0xC00A, "ECDHE-ECDSA-AES256-SHA"},
+ {0xC013, "ECDHE-RSA-AES128-SHA"},
+ {0xC014, "ECDHE-RSA-AES256-SHA"},
+ {0xC01D, "SRP-AES-128-CBC-SHA"},
+ {0xC01E, "SRP-RSA-AES-128-CBC-SHA"},
+ {0xC020, "SRP-AES-256-CBC-SHA"},
+ {0xC021, "SRP-RSA-AES-256-CBC-SHA"},
+ {0xC023, "ECDHE-ECDSA-AES128-SHA256"},
+ {0xC024, "ECDHE-ECDSA-AES256-SHA384"},
+ {0xC027, "ECDHE-RSA-AES128-SHA256"},
+ {0xC028, "ECDHE-RSA-AES256-SHA384"},
+ {0xC02B, "ECDHE-ECDSA-AES128-GCM-SHA256"},
+ {0xC02C, "ECDHE-ECDSA-AES256-GCM-SHA384"},
+ {0xC02F, "ECDHE-RSA-AES128-GCM-SHA256"},
+ {0xC030, "ECDHE-RSA-AES256-GCM-SHA384"},
+ {0xC035, "ECDHE-PSK-AES128-CBC-SHA"},
+ {0xC036, "ECDHE-PSK-AES256-CBC-SHA"},
+ {0xC037, "ECDHE-PSK-AES128-CBC-SHA256"},
+ {0xC038, "ECDHE-PSK-AES256-CBC-SHA384"},
+ {0xCCA8, "ECDHE-RSA-CHACHA20-POLY1305"},
+ {0xCCA9, "ECDHE-ECDSA-CHACHA20-POLY1305"},
+ {0xCCAA, "DHE-RSA-CHACHA20-POLY1305"},
+ {0xCCAB, "PSK-CHACHA20-POLY1305"},
+ {0xCCAC, "ECDHE-PSK-CHACHA20-POLY1305"},
+ {0xCCAD, "DHE-PSK-CHACHA20-POLY1305"},
+ {0xCCAE, "RSA-PSK-CHACHA20-POLY1305"}
+};
+
+static size_t set_cipher(char * cipher_str, DWORD protocol, ALG_ID *arr , size_t arr_size)
+{
+ char *token = strtok(cipher_str, ":");
+ size_t pos = 0;
+
+ while (token)
+ {
+ size_t i;
+
+ for(i = 0; i < sizeof(cipher_map)/sizeof(cipher_map[0]) ; i++)
+ {
+ if((pos + 4 < arr_size && strcmp(cipher_map[i].openssl_name, token) == 0) ||
+ (cipher_map[i].protocol <= protocol))
+ {
+ memcpy(arr + pos, cipher_map[i].algs, sizeof(ALG_ID)* 4);
+ pos += 4;
+ break;
+ }
+ }
+ token = strtok(NULL, ":");
+ }
+ return pos;
+}
+
+my_bool ma_tls_connect(MARIADB_TLS *ctls)
+{
+ MYSQL *mysql;
+ SCHANNEL_CRED Cred = {0};
+ MARIADB_PVIO *pvio;
+ my_bool rc= 1;
+ SC_CTX *sctx;
+ SECURITY_STATUS sRet;
+ ALG_ID AlgId[MAX_ALG_ID];
+ size_t i;
+ DWORD protocol = 0;
+ int verify_certs;
+ const CERT_CONTEXT* cert_context = NULL;
+
+ if (!ctls)
+ return 1;
+
+ pvio= ctls->pvio;
+ sctx= (SC_CTX *)ctls->ssl;
+ if (!pvio || !sctx)
+ return 1;
+
+ mysql= pvio->mysql;
+ if (!mysql)
+ return 1;
+
+ /* Set cipher */
+ if (mysql->options.ssl_cipher)
+ {
+
+ /* check if a protocol was specified as a cipher:
+ * In this case don't allow cipher suites which belong to newer protocols
+ * Please note: There are no cipher suites for TLS1.1
+ */
+ for (i = 0; i < sizeof(tls_version) / sizeof(tls_version[0]); i++)
+ {
+ if (!_stricmp(mysql->options.ssl_cipher, tls_version[i].tls_version))
+ protocol |= tls_version[i].protocol;
+ }
+ memset(AlgId, 0, sizeof(AlgId));
+ Cred.cSupportedAlgs = (DWORD)set_cipher(mysql->options.ssl_cipher, protocol, AlgId, MAX_ALG_ID);
+ if (Cred.cSupportedAlgs)
+ {
+ Cred.palgSupportedAlgs = AlgId;
+ }
+ else if (!protocol)
+ {
+ ma_schannel_set_sec_error(pvio, SEC_E_ALGORITHM_MISMATCH);
+ goto end;
+ }
+ }
+
+ Cred.dwVersion= SCHANNEL_CRED_VERSION;
+
+ Cred.dwFlags = SCH_CRED_NO_SERVERNAME_CHECK | SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
+
+ if (mysql->options.extension && mysql->options.extension->tls_version)
+ {
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.0"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0_CLIENT;
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.1"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1_CLIENT;
+ if (strstr(mysql->options.extension->tls_version, "TLSv1.2"))
+ Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2_CLIENT;
+ }
+ if (!Cred.grbitEnabledProtocols)
+ Cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
+
+
+ if (ma_tls_set_client_certs(ctls, &cert_context))
+ goto end;
+
+ if (cert_context)
+ {
+ Cred.cCreds = 1;
+ Cred.paCred = &cert_context;
+ }
+ sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
+ NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL);
+ if (sRet)
+ {
+ ma_schannel_set_sec_error(pvio, sRet);
+ goto end;
+ }
+ if (ma_schannel_client_handshake(ctls) != SEC_E_OK)
+ goto end;
+
+ verify_certs = mysql->options.ssl_ca || mysql->options.ssl_capath ||
+ (mysql->options.extension->tls_verify_server_cert);
+
+ if (verify_certs)
+ {
+ if (!ma_schannel_verify_certs(ctls, mysql->options.extension->tls_verify_server_cert))
+ goto end;
+ }
+
+ rc = 0;
+
+end:
+ if (cert_context)
+ schannel_free_cert_context(cert_context);
+ return rc;
+}
+
+ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ MARIADB_PVIO *pvio= ctls->pvio;
+ DWORD dlength= 0;
+ SECURITY_STATUS status = ma_schannel_read_decrypt(pvio, &sctx->hCtxt, &dlength, (uchar *)buffer, (DWORD)length);
+ if (status == SEC_I_CONTEXT_EXPIRED)
+ return 0; /* other side shut down the connection. */
+ if (status == SEC_I_RENEGOTIATE)
+ return -1; /* Do not handle renegotiate yet */
+
+ return (status == SEC_E_OK)? (ssize_t)dlength : -1;
+}
+
+ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length)
+{
+ MARIADB_PVIO *pvio= ctls->pvio;
+ ssize_t rc, wlength= 0;
+ ssize_t remain= length;
+
+ while (remain > 0)
+ {
+ if ((rc= ma_schannel_write_encrypt(pvio, (uchar *)buffer + wlength, remain)) <= 0)
+ return rc;
+ wlength+= rc;
+ remain-= rc;
+ }
+ return length;
+}
+
+/* {{{ my_bool ma_tls_close(MARIADB_PVIO *pvio) */
+my_bool ma_tls_close(MARIADB_TLS *ctls)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+
+ if (sctx)
+ {
+ LocalFree(sctx->IoBuffer);
+
+ if (SecIsValidHandle(&sctx->CredHdl))
+ FreeCredentialHandle(&sctx->CredHdl);
+
+ if (SecIsValidHandle(&sctx->hCtxt))
+ DeleteSecurityContext(&sctx->hCtxt);
+ }
+ LocalFree(sctx);
+ return 0;
+}
+/* }}} */
+
+int ma_tls_verify_server_cert(MARIADB_TLS *ctls)
+{
+ /* Done elsewhere */
+ return 0;
+}
+
+static const char *cipher_name(const SecPkgContext_CipherInfo *CipherInfo)
+{
+ size_t i;
+
+ for(i = 0; i < sizeof(openssl_ciphers)/sizeof(openssl_ciphers[0]) ; i++)
+ {
+ if (CipherInfo->dwCipherSuite == openssl_ciphers[i].dwCipherSuite)
+ return openssl_ciphers[i].openssl_name;
+ }
+ return "";
+};
+
+const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
+{
+ SecPkgContext_CipherInfo CipherInfo = { SECPKGCONTEXT_CIPHERINFO_V1 };
+ SECURITY_STATUS sRet;
+ SC_CTX *sctx;
+
+ if (!ctls || !ctls->ssl)
+ return NULL;
+
+ sctx= (SC_CTX *)ctls->ssl;
+ sRet= QueryContextAttributesA(&sctx->hCtxt, SECPKG_ATTR_CIPHER_INFO, (PVOID)&CipherInfo);
+
+ if (sRet != SEC_E_OK)
+ return NULL;
+
+ return cipher_name(&CipherInfo);
+}
+
+unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
+{
+ SC_CTX *sctx= (SC_CTX *)ctls->ssl;
+ PCCERT_CONTEXT pRemoteCertContext = NULL;
+ if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
+ return 0;
+ CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, (DWORD *)&len);
+ CertFreeCertificateContext(pRemoteCertContext);
+ return len;
+}
+
+void ma_tls_set_connection(MYSQL *mysql __attribute__((unused)))
+{
+ return;
+}
diff --git a/libmariadb/libmariadb/secure/schannel_certs.c b/libmariadb/libmariadb/secure/schannel_certs.c
new file mode 100644
index 00000000..c8ff3753
--- /dev/null
+++ b/libmariadb/libmariadb/secure/schannel_certs.c
@@ -0,0 +1,852 @@
+/************************************************************************************
+ Copyright (C) 2019 MariaDB
+
+ 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+
+ /*
+ This module contain X509 certificate handling on Windows.
+ PEM parsing, loading client certificate and key, server certificate validation
+ */
+
+ /*
+ CERT_CHAIN_ENGINE_CONFIG has additional members in Windows 8.1
+ To allow client to be work on pre-8.1 Windows, compile
+ with corresponding _WIN32_WINNT
+ */
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif
+#include "schannel_certs.h"
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+#include <winhttp.h>
+#include <limits.h>
+#include <assert.h>
+#include "win32_errmsg.h"
+
+ /*
+ Return GetLastError(), or, if this unexpectedly gives success,
+ return ERROR_INTERNAL_ERROR.
+
+ Background - in several cases in this module we return GetLastError()
+ after an Windows function fails. However, we do not want the function to
+ return success, even if GetLastError() was suddenly 0.
+ */
+static DWORD get_last_error()
+{
+ DWORD ret = GetLastError();
+ if (ret)
+ return ret;
+
+ // We generally expect last error to be set API fails.
+ // thus the debug assertion-
+ assert(0);
+ return ERROR_INTERNAL_ERROR;
+}
+
+#define FAIL(...) \
+ do{\
+ status = get_last_error();\
+ ma_format_win32_error(errmsg, errmsg_len, status, __VA_ARGS__);\
+ goto cleanup;\
+ } while (0)
+
+/*
+ Load file into memory. Add null terminator at the end, so it will be a valid C string.
+*/
+static char* pem_file_to_string(const char* file, char* errmsg, size_t errmsg_len)
+{
+ LARGE_INTEGER file_size;
+ size_t file_bufsize = 0;
+ size_t total_bytes_read = 0;
+ char* file_buffer = NULL;
+ SECURITY_STATUS status = SEC_E_OK;
+
+ HANDLE file_handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ {
+ FAIL("failed to open file '%s'", file);
+ }
+
+ if (!GetFileSizeEx(file_handle, &file_size))
+ {
+ FAIL("GetFileSizeEx failed on '%s'", file);
+ }
+
+ if (file_size.QuadPart > ULONG_MAX - 1)
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("file '%s' too large", file);
+ }
+
+ file_bufsize = (size_t)file_size.QuadPart;
+ file_buffer = (char*)LocalAlloc(0,file_bufsize + 1);
+ if (!file_buffer)
+ {
+ FAIL("LocalAlloc(0,%zu) failed", file_bufsize + 1);
+ }
+
+ while (total_bytes_read < file_bufsize)
+ {
+ DWORD bytes_to_read = (DWORD)(file_bufsize - total_bytes_read);
+ DWORD bytes_read = 0;
+
+ if (!ReadFile(file_handle, file_buffer + total_bytes_read,
+ bytes_to_read, &bytes_read, NULL))
+ {
+ FAIL("ReadFile() failed to read file '%s'", file);
+ }
+ if (bytes_read == 0)
+ {
+ /* Premature EOF -- adjust the bufsize to the new value */
+ file_bufsize = total_bytes_read;
+ }
+ else
+ {
+ total_bytes_read += bytes_read;
+ }
+ }
+
+ /* Null terminate the buffer */
+ file_buffer[file_bufsize] = '\0';
+
+cleanup:
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(file_handle);
+ }
+ if (status)
+ {
+ /* Some error happened. */
+ LocalFree(file_buffer);
+ file_buffer = NULL;
+ }
+ return file_buffer;
+}
+
+
+// Structure for parsing BEGIN/END sections inside pem.
+typedef struct _pem_type_desc
+{
+ const char* begin_tag;
+ size_t begin_tag_len;
+ const char* end_tag;
+ size_t end_tag_len;
+} pem_type_desc;
+
+#define BEGIN_TAG(x) "-----BEGIN " x "-----"
+#define END_TAG(x) "\n-----END " x "-----"
+#define PEM_SECTION(tag) {BEGIN_TAG(tag), sizeof(BEGIN_TAG(tag))-1, END_TAG(tag), sizeof(END_TAG(tag))-1}
+
+typedef enum {
+ PEM_TYPE_CERTIFICATE = 0,
+ PEM_TYPE_X509_CRL,
+ PEM_TYPE_RSA_PRIVATE_KEY,
+ PEM_TYPE_PRIVATE_KEY
+} PEM_TYPE;
+
+static const pem_type_desc pem_sections[] = {
+ PEM_SECTION("CERTIFICATE"),
+ PEM_SECTION("X509 CRL"),
+ PEM_SECTION("RSA PRIVATE KEY"),
+ PEM_SECTION("PRIVATE KEY")
+};
+
+/*
+ Locate a substring in pem for given type,
+ e.g section between BEGIN CERTIFICATE and END CERTIFICATE
+ in PEMs base64 format, with header and footer.
+
+ output parameters 'begin' and 'end' are set upon return.
+ it is possible that functions returns 'begin' != NULL but
+ 'end' = NULL. This is generally a format error, meaning that
+ the end tag was not found
+*/
+void pem_locate(char* pem_str,
+ PEM_TYPE type,
+ char** begin,
+ char** end)
+{
+ *begin = NULL;
+ *end = NULL;
+ char c;
+
+ const pem_type_desc* desc = &pem_sections[type];
+ *begin = strstr(pem_str, desc->begin_tag);
+ if (!(*begin))
+ return;
+
+ // We expect newline after the
+ // begin tag, LF or CRLF
+ c = (*begin)[desc->begin_tag_len];
+
+ if (c != '\r' && c != '\n')
+ {
+ *begin = NULL;
+ return;
+ }
+
+ *end = strstr(*begin + desc->begin_tag_len + 1, desc->end_tag);
+ if (!*end)
+ return; // error, end marker not found
+
+ (*end) += desc->end_tag_len;
+ return;
+}
+
+
+/*
+ Add certificates, or CRLs from a PEM file to Wincrypt store
+*/
+static SECURITY_STATUS add_certs_to_store(
+ HCERTSTORE trust_store,
+ const char* file,
+ PEM_TYPE type,
+ char* errmsg,
+ size_t errmsg_len)
+{
+ char* file_buffer = NULL;
+ char* cur = NULL;
+ SECURITY_STATUS status = SEC_E_OK;
+ CRL_CONTEXT* crl_context = NULL;
+ CERT_CONTEXT* cert_context = NULL;
+ char* begin;
+ char* end;
+
+ file_buffer = pem_file_to_string(file, errmsg, errmsg_len);
+ if (!file_buffer)
+ goto cleanup;
+
+ for (cur = file_buffer; ; cur = end)
+ {
+ pem_locate(cur, type, &begin, &end);
+
+ if (!begin)
+ break;
+
+ if (!end)
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("Invalid PEM file '%s', missing end marker corresponding to begin marker '%s' at offset %zu",
+ file, pem_sections[type].begin_tag, (size_t)(begin - file_buffer));
+ }
+ CERT_BLOB cert_blob;
+ void* context = NULL;
+ DWORD actual_content_type = 0;
+
+ cert_blob.pbData = (BYTE*)begin;
+ cert_blob.cbData = (DWORD)(end - begin);
+ if (!CryptQueryObject(
+ CERT_QUERY_OBJECT_BLOB, &cert_blob,
+ CERT_QUERY_CONTENT_FLAG_CERT | CERT_QUERY_CONTENT_FLAG_CRL,
+ CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
+ NULL, NULL, NULL, (const void**)&context))
+ {
+ FAIL("failed to extract certificate from PEM file '%s'",file);
+ }
+
+ if (!context)
+ {
+ SetLastError(SEC_E_INTERNAL_ERROR);
+ FAIL("unexpected result from CryptQueryObject(),cert_context is NULL"
+ " after successful completion, file '%s'",
+ file);
+ }
+
+ if (actual_content_type == CERT_QUERY_CONTENT_CERT)
+ {
+ CERT_CONTEXT* cert_context = (CERT_CONTEXT*)context;
+ if (!CertAddCertificateContextToStore(
+ trust_store, cert_context,
+ CERT_STORE_ADD_ALWAYS, NULL))
+ {
+ FAIL("CertAddCertificateContextToStore failed");
+ }
+ }
+ else if (actual_content_type == CERT_QUERY_CONTENT_CRL)
+ {
+ CRL_CONTEXT* crl_context = (CRL_CONTEXT*)context;
+ if (!CertAddCRLContextToStore(
+ trust_store, crl_context,
+ CERT_STORE_ADD_ALWAYS, NULL))
+ {
+ FAIL("CertAddCRLContextToStore() failed");
+ }
+ }
+ }
+cleanup:
+ LocalFree(file_buffer);
+ if (cert_context)
+ CertFreeCertificateContext(cert_context);
+ if (crl_context)
+ CertFreeCRLContext(crl_context);
+ return status;
+}
+
+/*
+Add a directory to store, i.e try to load all files.
+(extract certificates and add them to store)
+
+@return 0 on success, error only if directory is invalid.
+*/
+SECURITY_STATUS add_dir_to_store(HCERTSTORE trust_store, const char* dir,
+ PEM_TYPE type, char* errmsg, size_t errmsg_len)
+{
+ WIN32_FIND_DATAA ffd;
+ char path[MAX_PATH];
+ char pattern[MAX_PATH];
+ DWORD dwAttr;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ SECURITY_STATUS status = SEC_E_OK;
+
+ if ((dwAttr = GetFileAttributes(dir)) == INVALID_FILE_ATTRIBUTES)
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("directory '%s' does not exist", dir);
+ }
+ if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("'%s' is not a directory", dir);
+ }
+ sprintf_s(pattern, sizeof(pattern), "%s\\*", dir);
+ hFind = FindFirstFile(pattern, &ffd);
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ FAIL("FindFirstFile(%s) failed",pattern);
+ }
+ do
+ {
+ if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ continue;
+ sprintf_s(path, sizeof(path), "%s\\%s", dir, ffd.cFileName);
+
+ // ignore error from add_certs_to_store(), not all file
+ // maybe PEM.
+ add_certs_to_store(trust_store, path, type, errmsg,
+ errmsg_len);
+ } while (FindNextFile(hFind, &ffd) != 0);
+
+cleanup:
+ if (hFind != INVALID_HANDLE_VALUE)
+ FindClose(hFind);
+
+ return status;
+}
+
+/* Count certificates in store. */
+static int count_certificates(HCERTSTORE store)
+{
+ int num_certs = 0;
+ PCCERT_CONTEXT c = NULL;
+
+ while ((c = CertEnumCertificatesInStore(store, c)))
+ num_certs++;
+
+ return num_certs;
+}
+
+/**
+ Creates certificate store with user defined CA chain and/or CRL.
+ Loads PEM certificate from files or directories.
+
+ If only CRLFile/CRLPath is defined, the "system" store is duplicated,
+ and new CRLs are added to it.
+
+ If CAFile/CAPAth is defined, then new empty store is created, and CAs
+ (and CRLs, if defined), are added to it.
+
+ The function throws an error, if none of the files in CAFile/CAPath have a valid certificate.
+ It is also an error if CRLFile does not exist.
+*/
+SECURITY_STATUS schannel_create_store(
+ const char* CAFile,
+ const char* CAPath,
+ const char* CRLFile,
+ const char* CRLPath,
+ HCERTSTORE* out_store,
+ char* errmsg,
+ size_t errmsg_len)
+{
+
+ HCERTSTORE store = NULL;
+ HCERTSTORE system_store = NULL;
+ int status = SEC_E_OK;
+
+ *out_store = NULL;
+ if (!CAFile && !CAPath && !CRLFile && !CRLPath)
+ {
+ /* Nothing to do, caller will use default store*/
+ *out_store = NULL;
+ return SEC_E_OK;
+ }
+ if (CAFile || CAPath)
+ {
+ /* Open the certificate store */
+ store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, (HCRYPTPROV)NULL,
+ CERT_STORE_CREATE_NEW_FLAG, NULL);
+ if (!store)
+ {
+ FAIL("CertOpenStore failed for memory store");
+ }
+ }
+ else if (CRLFile || CRLPath)
+ {
+ /* Only CRL was provided, copy system store, add revocation list to
+ * it. */
+ system_store =
+ CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, (HCRYPTPROV_LEGACY)NULL,
+ CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
+ if (!system_store)
+ {
+ FAIL("CertOpenStore failed for system store");
+ }
+
+ store = CertDuplicateStore(system_store);
+ if (!store)
+ {
+ FAIL("CertDuplicateStore failed");
+ }
+ }
+
+ if (CAFile)
+ {
+ status = add_certs_to_store(store, CAFile,
+ PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
+ if (status)
+ goto cleanup;
+ }
+ if (CAPath)
+ {
+ status = add_dir_to_store(store, CAPath,
+ PEM_TYPE_CERTIFICATE, errmsg, errmsg_len);
+ if (status)
+ goto cleanup;
+ }
+
+ if ((CAFile || CAPath) && store && !count_certificates(store))
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("no valid certificates were found, CAFile='%s', CAPath='%s'",
+ CAFile ? CAFile : "<not set>", CAPath ? CAPath : "<not set>");
+ }
+
+ if (CRLFile)
+ {
+ status = add_certs_to_store(store, CRLFile, PEM_TYPE_X509_CRL,
+ errmsg, errmsg_len);
+ }
+ if (CRLPath)
+ {
+ status = add_dir_to_store(store, CRLPath, PEM_TYPE_X509_CRL,
+ errmsg, errmsg_len);
+ }
+
+cleanup:
+ if (system_store)
+ CertCloseStore(system_store, 0);
+ if (status && store)
+ {
+ CertCloseStore(store, 0);
+ store = NULL;
+ }
+ *out_store = store;
+ return status;
+}
+
+/*
+ The main verification logic.
+ Taken almost completely from Windows 2003 Platform SDK 2003
+ (Samples\Security\SSPI\SSL\WebClient.c)
+
+ The only difference here is is usage of custom store
+ and chain engine.
+*/
+static SECURITY_STATUS VerifyServerCertificate(
+ PCCERT_CONTEXT pServerCert,
+ HCERTSTORE hStore,
+ LPWSTR pwszServerName,
+ DWORD dwRevocationCheckFlags,
+ DWORD dwVerifyFlags,
+ LPSTR errmsg,
+ size_t errmsg_len)
+{
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA polExtra;
+ CERT_CHAIN_POLICY_PARA PolicyPara;
+ CERT_CHAIN_POLICY_STATUS PolicyStatus;
+ CERT_CHAIN_PARA ChainPara;
+ HCERTCHAINENGINE hChainEngine = NULL;
+ PCCERT_CHAIN_CONTEXT pChainContext = NULL;
+ LPSTR rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE };
+ DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);
+ SECURITY_STATUS status = SEC_E_OK;
+
+ if (pServerCert == NULL)
+ {
+ SetLastError(SEC_E_WRONG_PRINCIPAL);
+ FAIL("Invalid parameter pServerCert passed to VerifyServerCertificate");
+ }
+
+ ZeroMemory(&ChainPara, sizeof(ChainPara));
+ ChainPara.cbSize = sizeof(ChainPara);
+ ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
+ ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
+
+ if (hStore)
+ {
+ CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
+ EngineConfig.cbSize = sizeof(EngineConfig);
+ EngineConfig.hExclusiveRoot = hStore;
+ if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
+ {
+ FAIL("CertCreateCertificateChainEngine failed");
+ }
+ }
+
+ if (!CertGetCertificateChain(
+ hChainEngine,
+ pServerCert,
+ NULL,
+ pServerCert->hCertStore,
+ &ChainPara,
+ dwRevocationCheckFlags,
+ NULL,
+ &pChainContext))
+ {
+ FAIL("CertGetCertificateChain failed");
+ goto cleanup;
+ }
+
+ // Validate certificate chain.
+ ZeroMemory(&polExtra, sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ polExtra.cbStruct = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ polExtra.dwAuthType = AUTHTYPE_SERVER;
+ polExtra.fdwChecks = dwVerifyFlags;
+ polExtra.pwszServerName = pwszServerName;
+
+ memset(&PolicyPara, 0, sizeof(PolicyPara));
+ PolicyPara.cbSize = sizeof(PolicyPara);
+ PolicyPara.pvExtraPolicyPara = &polExtra;
+
+ memset(&PolicyStatus, 0, sizeof(PolicyStatus));
+ PolicyStatus.cbSize = sizeof(PolicyStatus);
+
+ if (!CertVerifyCertificateChainPolicy(
+ CERT_CHAIN_POLICY_SSL,
+ pChainContext,
+ &PolicyPara,
+ &PolicyStatus))
+ {
+ FAIL("CertVerifyCertificateChainPolicy failed");
+ }
+
+ if (PolicyStatus.dwError)
+ {
+ SetLastError(PolicyStatus.dwError);
+ FAIL("Server certificate validation failed");
+ }
+
+cleanup:
+ if (hChainEngine)
+ {
+ CertFreeCertificateChainEngine(hChainEngine);
+ }
+ if (pChainContext)
+ {
+ CertFreeCertificateChain(pChainContext);
+ }
+ return status;
+}
+
+
+void schannel_free_store(HCERTSTORE store)
+{
+ if (store)
+ CertCloseStore(store, 0);
+}
+
+
+/*
+Verify server certificate against a wincrypt store
+@return 0 - success, otherwise error occurred.
+*/
+SECURITY_STATUS schannel_verify_server_certificate(
+ const CERT_CONTEXT* cert,
+ HCERTSTORE store,
+ BOOL check_revocation,
+ const char* server_name,
+ BOOL check_server_name,
+ char* errmsg,
+ size_t errmsg_len)
+{
+ SECURITY_STATUS status = SEC_E_OK;
+ wchar_t* wserver_name = NULL;
+ DWORD dwVerifyFlags;
+ DWORD dwRevocationFlags;
+
+ if (check_server_name)
+ {
+ int cchServerName = (int)strlen(server_name) + 1;
+ wserver_name = (wchar_t*)LocalAlloc(0,sizeof(wchar_t) * cchServerName);
+ if (!wserver_name)
+ {
+ FAIL("LocalAlloc() failed");
+ }
+ if (MultiByteToWideChar(CP_UTF8, 0, server_name, cchServerName, wserver_name, cchServerName) < 0)
+ {
+ FAIL("MultiByteToWideChar() failed");
+ }
+ }
+
+ dwVerifyFlags = 0;
+ dwRevocationFlags = 0;
+ if (check_revocation)
+ dwRevocationFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
+ if (!check_server_name)
+ dwVerifyFlags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
+
+ status = VerifyServerCertificate(cert, store, wserver_name ? wserver_name : L"SERVER_NAME",
+ dwRevocationFlags, dwVerifyFlags, errmsg, errmsg_len);
+
+cleanup:
+ LocalFree(wserver_name);
+ return status;
+}
+
+
+/* Attach private key (in PEM format) to client certificate */
+static SECURITY_STATUS load_private_key(CERT_CONTEXT* cert, char* private_key_str, size_t len, char* errmsg, size_t errmsg_len)
+{
+ DWORD derlen = (DWORD)len;
+ BYTE* derbuf = NULL;
+ DWORD keyblob_len = 0;
+ BYTE* keyblob = NULL;
+ HCRYPTPROV hProv = 0;
+ HCRYPTKEY hKey = 0;
+ CERT_KEY_CONTEXT cert_key_context = { 0 };
+ PCRYPT_PRIVATE_KEY_INFO pki = NULL;
+ DWORD pki_len = 0;
+ SECURITY_STATUS status = SEC_E_OK;
+
+ derbuf = LocalAlloc(0, derlen);
+ if (!derbuf)
+ {
+ FAIL("LocalAlloc failed");
+ }
+
+ if (!CryptStringToBinaryA(private_key_str, (DWORD)len, CRYPT_STRING_BASE64HEADER, derbuf, &derlen, NULL, NULL))
+ {
+ FAIL("Failed to convert BASE64 private key");
+ }
+
+ /*
+ To accommodate for both "BEGIN PRIVATE KEY" vs "BEGIN RSA PRIVATE KEY"
+ sections in PEM, we try to decode with PKCS_PRIVATE_KEY_INFO first,
+ and, if it fails, with PKCS_RSA_PRIVATE_KEY flag.
+ */
+ if (CryptDecodeObjectEx(
+ X509_ASN_ENCODING,
+ PKCS_PRIVATE_KEY_INFO,
+ derbuf, derlen,
+ CRYPT_DECODE_ALLOC_FLAG,
+ NULL, &pki, &pki_len))
+ {
+ // convert private key info to RSA private key blob
+ if (!CryptDecodeObjectEx(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ pki->PrivateKey.pbData,
+ pki->PrivateKey.cbData,
+ CRYPT_DECODE_ALLOC_FLAG,
+ NULL, &keyblob, &keyblob_len))
+ {
+ FAIL("Failed to parse private key");
+ }
+ }
+ else if (!CryptDecodeObjectEx(
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ PKCS_RSA_PRIVATE_KEY,
+ derbuf, derlen,
+ CRYPT_DECODE_ALLOC_FLAG, NULL,
+ &keyblob, &keyblob_len))
+ {
+ FAIL("Failed to parse private key");
+ }
+
+ if (!CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ {
+ FAIL("CryptAcquireContext failed");
+ }
+
+ if (!CryptImportKey(hProv, keyblob, keyblob_len, 0, 0, (HCRYPTKEY*)&hKey))
+ {
+ FAIL("CryptImportKey failed");
+ }
+ cert_key_context.hCryptProv = hProv;
+ cert_key_context.dwKeySpec = AT_KEYEXCHANGE;
+ cert_key_context.cbSize = sizeof(cert_key_context);
+
+ /* assign private key to certificate context */
+ if (!CertSetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
+ CERT_STORE_NO_CRYPT_RELEASE_FLAG,
+ &cert_key_context))
+ {
+ FAIL("CertSetCertificateContextProperty failed");
+ }
+
+cleanup:
+ LocalFree(derbuf);
+ LocalFree(keyblob);
+ LocalFree(pki);
+ if (hKey)
+ CryptDestroyKey(hKey);
+ if (status)
+ {
+ if (hProv)
+ CryptReleaseContext(hProv, 0);
+ }
+ return status;
+}
+
+/*
+ Given PEM strings for certificate and private key,
+ create a client certificate*
+*/
+static CERT_CONTEXT* create_client_certificate_mem(
+ char* cert_file_content,
+ char* key_file_content,
+ char* errmsg,
+ size_t errmsg_len)
+{
+ CERT_CONTEXT* ctx = NULL;
+ char* begin;
+ char* end;
+ CERT_BLOB cert_blob;
+ DWORD actual_content_type = 0;
+ SECURITY_STATUS status = SEC_E_OK;
+
+ /* Parse certificate */
+ pem_locate(cert_file_content, PEM_TYPE_CERTIFICATE,
+ &begin, &end);
+
+ if (!begin || !end)
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("Client certificate not found in PEM file");
+ }
+
+ cert_blob.pbData = (BYTE*)begin;
+ cert_blob.cbData = (DWORD)(end - begin);
+ if (!CryptQueryObject(
+ CERT_QUERY_OBJECT_BLOB, &cert_blob,
+ CERT_QUERY_CONTENT_FLAG_CERT,
+ CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &actual_content_type,
+ NULL, NULL, NULL, (const void**)&ctx))
+ {
+ FAIL("Can't parse client certficate");
+ }
+
+ /* Parse key */
+ PEM_TYPE types[] = { PEM_TYPE_RSA_PRIVATE_KEY, PEM_TYPE_PRIVATE_KEY };
+ for (int i = 0; i < sizeof(types) / sizeof(types[0]); i++)
+ {
+ pem_locate(key_file_content, types[i], &begin, &end);
+ if (begin && end)
+ {
+ /* Assign key to certificate.*/
+ status = load_private_key(ctx, begin, (end - begin), errmsg, errmsg_len);
+ goto cleanup;
+ }
+ }
+
+ if (!begin || !end)
+ {
+ SetLastError(SEC_E_INVALID_PARAMETER);
+ FAIL("Client private key not found in PEM");
+ }
+
+cleanup:
+ if (status && ctx)
+ {
+ CertFreeCertificateContext(ctx);
+ ctx = NULL;
+ }
+ return ctx;
+}
+
+
+/* Given cert and key, as PEM file names, create a client certificate */
+CERT_CONTEXT* schannel_create_cert_context(char* cert_file, char* key_file, char* errmsg, size_t errmsg_len)
+{
+ CERT_CONTEXT* ctx = NULL;
+ char* key_file_content = NULL;
+ char* cert_file_content = NULL;
+
+ cert_file_content = pem_file_to_string(cert_file, errmsg, errmsg_len);
+
+ if (!cert_file_content)
+ goto cleanup;
+
+ if (cert_file == key_file)
+ {
+ key_file_content = cert_file_content;
+ }
+ else
+ {
+ key_file_content = pem_file_to_string(key_file, errmsg, errmsg_len);
+ if (!key_file_content)
+ goto cleanup;
+ }
+
+ ctx = create_client_certificate_mem(cert_file_content, key_file_content, errmsg, errmsg_len);
+
+cleanup:
+ LocalFree(cert_file_content);
+ if (cert_file != key_file)
+ LocalFree(key_file_content);
+
+ return ctx;
+}
+
+/*
+ Free certificate, and all resources, created by schannel_create_cert_context()
+*/
+void schannel_free_cert_context(const CERT_CONTEXT* cert)
+{
+ /* release provider handle which was acquires in load_private_key() */
+ CERT_KEY_CONTEXT cert_key_context = { 0 };
+ cert_key_context.cbSize = sizeof(cert_key_context);
+ DWORD cbData = sizeof(CERT_KEY_CONTEXT);
+ HCRYPTPROV hProv = 0;
+
+ if (CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &cert_key_context, &cbData))
+ {
+ hProv = cert_key_context.hCryptProv;
+ }
+ CertFreeCertificateContext(cert);
+ if (hProv)
+ {
+ CryptReleaseContext(cert_key_context.hCryptProv, 0);
+ }
+}
diff --git a/libmariadb/libmariadb/secure/schannel_certs.h b/libmariadb/libmariadb/secure/schannel_certs.h
new file mode 100644
index 00000000..67a8d11e
--- /dev/null
+++ b/libmariadb/libmariadb/secure/schannel_certs.h
@@ -0,0 +1,53 @@
+/************************************************************************************
+ 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+
+#pragma once
+#include <windows.h>
+#include <wincrypt.h>
+
+extern SECURITY_STATUS schannel_create_store(
+ const char* CAFile,
+ const char* CAPath,
+ const char* CRLFile,
+ const char* CRLPath,
+ HCERTSTORE* store,
+ char* errmsg,
+ size_t errmsg_len
+);
+
+extern SECURITY_STATUS schannel_verify_server_certificate(
+ const CERT_CONTEXT* cert,
+ HCERTSTORE store,
+ BOOL check_revocation,
+ const char* server_name,
+ BOOL check_server_name,
+ char* errmsg,
+ size_t errmsg_len);
+
+extern void schannel_free_store(HCERTSTORE store);
+
+extern CERT_CONTEXT* schannel_create_cert_context(
+ char* cert_file,
+ char* key_file,
+ char* errmsg,
+ size_t errmsg_len);
+
+extern void schannel_free_cert_context(const CERT_CONTEXT* cert);
+
diff --git a/libmariadb/libmariadb/secure/win_crypt.c b/libmariadb/libmariadb/secure/win_crypt.c
new file mode 100644
index 00000000..9d9fe495
--- /dev/null
+++ b/libmariadb/libmariadb/secure/win_crypt.c
@@ -0,0 +1,162 @@
+/*
+ Copyright (C) 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 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+*/
+#include <windows.h>
+#include <bcrypt.h>
+#include <ma_crypt.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ Error handling for bcrypt.
+ If we can't meaningfully return an error, dump error on stderr
+ and abort. Those errors are mostly likely programming errors
+ (invalid parameters and such)
+*/
+static inline void check_nt_status(int ret, const char *function,
+ const char *file, int line)
+{
+ if (ret)
+ {
+ fprintf(stderr,"invalid return %d from bcrypt, "
+ "function %s with file %s, line %d\n",
+ ret, function, file, line);
+ abort();
+ }
+}
+#define CHECK_NT_STATUS(ret) check_nt_status(ret, __func__, __FILE__, __LINE__)
+
+/*
+ Return Bcrypt algorithm ID (wchar string) for MariaDB numeric ID
+*/
+static LPCWSTR ma_hash_get_algorithm(unsigned int alg)
+{
+ switch (alg)
+ {
+ case MA_HASH_SHA1:
+ return BCRYPT_SHA1_ALGORITHM;
+ case MA_HASH_SHA256:
+ return BCRYPT_SHA256_ALGORITHM;
+ case MA_HASH_SHA384:
+ return BCRYPT_SHA384_ALGORITHM;
+ case MA_HASH_SHA512:
+ return BCRYPT_SHA512_ALGORITHM;
+ default:
+ return NULL;
+ }
+}
+
+/* Cached algorithm provides handles. */
+static BCRYPT_ALG_HANDLE cached_alg_handles[MA_HASH_MAX];
+
+/*
+ Cleanup cached algorithm handles. It runs either on process exit,
+ or when DLL is unloaded (see _onexit() documentation)
+*/
+static int win_crypt_onexit(void)
+{
+ int i;
+ for (i= 0; i < MA_HASH_MAX; i++)
+ {
+ if (cached_alg_handles[i])
+ BCryptCloseAlgorithmProvider(cached_alg_handles[i], 0);
+ }
+ return 0;
+}
+
+static void register_cleanup_onexit_once()
+{
+ static LONG onexit_called;
+ if (!InterlockedCompareExchange(&onexit_called, 1, 0))
+ _onexit(win_crypt_onexit);
+}
+
+/*
+ Given algorithm ID, return BCRYPT provider handle.
+ Uses or populates algorithm provider handle cache.
+*/
+static BCRYPT_ALG_HANDLE ma_hash_get_algorithm_handle(unsigned int alg)
+{
+ static SRWLOCK lock= SRWLOCK_INIT;
+ BCRYPT_ALG_HANDLE handle= NULL;
+ const wchar_t *name;
+
+ if ((handle= cached_alg_handles[alg]) != NULL)
+ return handle;
+
+ name= ma_hash_get_algorithm(alg);
+ if (!name)
+ return NULL;
+
+ AcquireSRWLockExclusive(&lock);
+ if ((handle= cached_alg_handles[alg]) == NULL)
+ {
+ if (BCryptOpenAlgorithmProvider(&handle, name, NULL, 0) == 0)
+ cached_alg_handles[alg]= handle;
+ else
+ handle= NULL;
+ }
+ ReleaseSRWLockExclusive(&lock);
+
+ if (handle)
+ register_cleanup_onexit_once();
+ return handle;
+}
+
+MA_HASH_CTX *ma_hash_new(unsigned int algorithm)
+{
+ BCRYPT_HASH_HANDLE hash_handle;
+ BCRYPT_ALG_HANDLE alg_handle= ma_hash_get_algorithm_handle(algorithm);
+
+ if (!alg_handle)
+ return NULL;
+
+ if (BCryptCreateHash(alg_handle, &hash_handle, NULL, 0, NULL, 0, 0))
+ return NULL;
+
+ return hash_handle;
+}
+
+void ma_hash_free(MA_HASH_CTX *ctx)
+{
+ NTSTATUS status;
+ if (!ctx)
+ return;
+ status= BCryptDestroyHash(ctx);
+ CHECK_NT_STATUS(status);
+}
+
+void ma_hash_input(MA_HASH_CTX *ctx, const unsigned char *buffer, size_t len)
+{
+ NTSTATUS status= BCryptHashData(ctx, (PUCHAR) buffer, (ULONG) len, 0);
+ CHECK_NT_STATUS(status);
+}
+
+void ma_hash_result(MA_HASH_CTX *ctx, unsigned char *digest)
+{
+ DWORD hash_length;
+ DWORD data_length;
+ NTSTATUS status=
+ BCryptGetProperty(ctx, BCRYPT_HASH_LENGTH, (PBYTE) &hash_length,
+ sizeof(DWORD), &data_length, 0);
+ CHECK_NT_STATUS(status);
+
+ status= BCryptFinishHash(ctx, digest, (ULONG) hash_length, 0);
+ CHECK_NT_STATUS(status);
+}
diff --git a/libmariadb/libmariadb/win32_errmsg.c b/libmariadb/libmariadb/win32_errmsg.c
new file mode 100644
index 00000000..f890c4f6
--- /dev/null
+++ b/libmariadb/libmariadb/win32_errmsg.c
@@ -0,0 +1,138 @@
+/************************************************************************************
+ Copyright (C) 2019 MariaDB
+
+ 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 <http://www.gnu.org/licenses>
+ or write to the Free Software Foundation, Inc.,
+ 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
+
+ *************************************************************************************/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ Format Windows error, with optional text.
+
+ For "known" errors we also output their symbolic error constant, e.g
+ CERT_E_CN_NO_MATCH in addition to numeric value.
+
+ We also try to output English text for all error messages, as not to mess up
+ with encodings.
+*/
+void ma_format_win32_error(char* buf, size_t buflen, DWORD code, _Printf_format_string_ const char* fmt, ...)
+{
+ char* cur = buf;
+ char* end = cur + buflen;
+ *cur = 0;
+ if (fmt)
+ {
+ va_list vargs;
+ va_start(vargs, fmt);
+ cur += vsnprintf_s(cur, end - cur, _TRUNCATE, fmt, vargs);
+ va_end(vargs);
+ }
+
+ if (code == 0)
+ return;
+
+ static struct map_entry
+ {
+ DWORD code;
+ const char* sym;
+ const char* msg;
+ }
+ map[] =
+ {
+#define ENTRY(x, y) {x,#x, y}
+ ENTRY(SEC_E_WRONG_PRINCIPAL, "The target principal name is incorrect"),
+ ENTRY(CERT_E_CN_NO_MATCH,"The certificate's CN name does not match the passed value"),
+ ENTRY(SEC_E_UNTRUSTED_ROOT,"The certificate chain was issued by an authority that is not trusted"),
+ ENTRY(TRUST_E_CERT_SIGNATURE,"The signature of the certificate cannot be verified"),
+ ENTRY(SEC_E_CERT_EXPIRED,"The received certificate has expired"),
+ ENTRY(CERT_E_EXPIRED,"A required certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file"),
+ ENTRY(CRYPT_E_NO_REVOCATION_CHECK, "The revocation function was unable to check revocation for the certificate"),
+ ENTRY(CRYPT_E_REVOCATION_OFFLINE,"The revocation function was unable to check revocation because the revocation server was offline"),
+ ENTRY(CRYPT_E_REVOKED,"The certificate is revoked"),
+ ENTRY(SEC_E_CERT_UNKNOWN,"An unknown error occurred while processing the certificate"),
+ ENTRY(CERT_E_ROLE," A certificate that can only be used as an end-entity is being used as a CA or vice versa"),
+ ENTRY(CERT_E_WRONG_USAGE,"The certificate is not valid for the requested usage"),
+ ENTRY(SEC_E_ILLEGAL_MESSAGE, "The message received was unexpected or badly formatted"),
+ ENTRY(CERT_E_VALIDITYPERIODNESTING,"The validity periods of the certification chain do not nest correctly"),
+ ENTRY(CERT_E_PATHLENCONST,"A path length constraint in the certification chain has been violated"),
+ ENTRY(CERT_E_CRITICAL,"A certificate contains an unknown extension that is marked 'critical'"),
+ ENTRY(CERT_E_PURPOSE,"A certificate being used for a purpose other than the ones specified by its CA"),
+ ENTRY(CERT_E_ISSUERCHAINING,"A parent of a given certificate in fact did not issue that child certificate"),
+ ENTRY(CERT_E_MALFORMED, "A certificate is missing or has an empty value for an important field, such as a subject or issuer name"),
+ ENTRY(CERT_E_CHAINING,"A certificate chain could not be built to a trusted root authority"),
+ ENTRY(TRUST_E_FAIL," Generic trust failure"),
+ ENTRY(CERT_E_UNTRUSTEDTESTROOT,"The certification path terminates with the test root which is not trusted with the current policy settings"),
+ ENTRY(CERT_E_UNTRUSTEDROOT,"A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider"),
+ ENTRY(CERT_E_REVOCATION_FAILURE,"The revocation process could not continue - the certificate(s) could not be checked"),
+ ENTRY(SEC_E_ILLEGAL_MESSAGE, "The message received was unexpected or badly formatted"),
+ ENTRY(SEC_E_UNTRUSTED_ROOT, "Untrusted root certificate"),
+ ENTRY(SEC_E_BUFFER_TOO_SMALL, "Buffer too small"),
+ ENTRY(SEC_E_CRYPTO_SYSTEM_INVALID, "Cipher is not supported"),
+ ENTRY(SEC_E_INSUFFICIENT_MEMORY, "Out of memory"),
+ ENTRY(SEC_E_OUT_OF_SEQUENCE, "Invalid message sequence"),
+ ENTRY(SEC_E_DECRYPT_FAILURE, "The specified data could not be decrypted"),
+ ENTRY(SEC_I_INCOMPLETE_CREDENTIALS, "Incomplete credentials"),
+ ENTRY(SEC_E_ENCRYPT_FAILURE, "The specified data could not be encrypted"),
+ ENTRY(SEC_I_CONTEXT_EXPIRED, "The context has expired and can no longer be used"),
+ ENTRY(SEC_E_ALGORITHM_MISMATCH, "no cipher match"),
+ ENTRY(SEC_E_NO_CREDENTIALS, "no credentials"),
+ ENTRY(SEC_E_INVALID_TOKEN, "The token supplied to function is invalid"),
+ ENTRY(SEC_E_UNSUPPORTED_FUNCTION,"The function requested is not supported")
+ };
+
+ struct map_entry* entry = NULL;
+
+ if (cur > buf && cur[-1] != ' ' && cur[-1] != '.')
+ {
+ strncpy_s(cur,end-cur, ". ", _TRUNCATE);
+ cur += 2;
+ }
+
+ for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); i++)
+ {
+ if (code == map[i].code)
+ {
+ entry = &map[i];
+ break;
+ }
+ }
+ if (cur > end - 20)
+ return;
+ if (entry)
+ {
+ snprintf(cur, end - cur, "%s. Error 0x%08lX(%s)", entry->msg, code, entry->sym);
+ }
+ else
+ {
+ cur += FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ cur, (DWORD)(end - cur), NULL);
+ while (cur > buf && (*cur == '\0' || *cur == '\n' || *cur == '\r' || *cur == '.'))
+ cur--;
+ if (*cur)
+ {
+ cur++;
+ *cur = 0;
+ }
+ snprintf(cur, end - cur, ". Error %lu/0x%08lX", code, code);
+ }
+ end[-1] = 0;
+}
+
diff --git a/libmariadb/libmariadb/win32_errmsg.h b/libmariadb/libmariadb/win32_errmsg.h
new file mode 100644
index 00000000..865d9928
--- /dev/null
+++ b/libmariadb/libmariadb/win32_errmsg.h
@@ -0,0 +1,2 @@
+#include <windows.h>
+void ma_format_win32_error(char* buf, size_t buflen, DWORD code, _Printf_format_string_ const char* fmt, ...);