From 35a201cc8ef0c3f5b2df88d2e528aabee1048348 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 18:47:09 +0200 Subject: Initial/Final commit --- .../libmariadb/CMakeLists.txt | 460 ++ .../libmariadb/acinclude.m4 | 91 + mariadb-connector-c-v_2.3.7/libmariadb/array.c | 175 + mariadb-connector-c-v_2.3.7/libmariadb/bchange.c | 39 + mariadb-connector-c-v_2.3.7/libmariadb/bmove.c | 80 + mariadb-connector-c-v_2.3.7/libmariadb/bmove_upp.c | 51 + mariadb-connector-c-v_2.3.7/libmariadb/charset.c | 78 + .../libmariadb/client_plugin.c | 466 +++ mariadb-connector-c-v_2.3.7/libmariadb/dbug.c | 2457 +++++++++++ mariadb-connector-c-v_2.3.7/libmariadb/default.c | 433 ++ mariadb-connector-c-v_2.3.7/libmariadb/errmsg.c | 152 + mariadb-connector-c-v_2.3.7/libmariadb/errors.c | 96 + .../libmariadb/get_password.c | 175 + mariadb-connector-c-v_2.3.7/libmariadb/getopt.c | 746 ++++ mariadb-connector-c-v_2.3.7/libmariadb/getopt1.c | 170 + mariadb-connector-c-v_2.3.7/libmariadb/hash.c | 644 +++ mariadb-connector-c-v_2.3.7/libmariadb/int2str.c | 153 + mariadb-connector-c-v_2.3.7/libmariadb/is_prefix.c | 34 + .../libmariadb/libmariadb.c | 3728 +++++++++++++++++ mariadb-connector-c-v_2.3.7/libmariadb/list.c | 116 + mariadb-connector-c-v_2.3.7/libmariadb/llstr.c | 36 + .../libmariadb/longlong2str.c | 143 + mariadb-connector-c-v_2.3.7/libmariadb/ma_dtoa.c | 1925 +++++++++ mariadb-connector-c-v_2.3.7/libmariadb/ma_dyncol.c | 4401 ++++++++++++++++++++ mariadb-connector-c-v_2.3.7/libmariadb/ma_secure.c | 700 ++++ mariadb-connector-c-v_2.3.7/libmariadb/ma_time.c | 65 + .../libmariadb/mf_dirname.c | 100 + mariadb-connector-c-v_2.3.7/libmariadb/mf_fn_ext.c | 46 + mariadb-connector-c-v_2.3.7/libmariadb/mf_format.c | 156 + .../libmariadb/mf_loadpath.c | 54 + mariadb-connector-c-v_2.3.7/libmariadb/mf_pack.c | 532 +++ mariadb-connector-c-v_2.3.7/libmariadb/mf_path.c | 120 + .../libmariadb/mf_unixpath.c | 33 + mariadb-connector-c-v_2.3.7/libmariadb/mf_wcomp.c | 68 + mariadb-connector-c-v_2.3.7/libmariadb/mulalloc.c | 53 + mariadb-connector-c-v_2.3.7/libmariadb/my_alloc.c | 169 + mariadb-connector-c-v_2.3.7/libmariadb/my_auth.c | 742 ++++ .../libmariadb/my_charset.c | 1466 +++++++ .../libmariadb/my_compress.c | 89 + .../libmariadb/my_context.c | 760 ++++ mariadb-connector-c-v_2.3.7/libmariadb/my_div.c | 31 + mariadb-connector-c-v_2.3.7/libmariadb/my_error.c | 124 + mariadb-connector-c-v_2.3.7/libmariadb/my_fopen.c | 178 + .../libmariadb/my_fstream.c | 171 + mariadb-connector-c-v_2.3.7/libmariadb/my_getwd.c | 202 + mariadb-connector-c-v_2.3.7/libmariadb/my_init.c | 273 ++ mariadb-connector-c-v_2.3.7/libmariadb/my_lib.c | 614 +++ .../libmariadb/my_loaddata.c | 304 ++ mariadb-connector-c-v_2.3.7/libmariadb/my_malloc.c | 101 + mariadb-connector-c-v_2.3.7/libmariadb/my_messnc.c | 36 + mariadb-connector-c-v_2.3.7/libmariadb/my_net.c | 44 + mariadb-connector-c-v_2.3.7/libmariadb/my_once.c | 88 + mariadb-connector-c-v_2.3.7/libmariadb/my_open.c | 126 + mariadb-connector-c-v_2.3.7/libmariadb/my_port.c | 40 + .../libmariadb/my_pthread.c | 555 +++ mariadb-connector-c-v_2.3.7/libmariadb/my_read.c | 66 + .../libmariadb/my_realloc.c | 65 + mariadb-connector-c-v_2.3.7/libmariadb/my_seek.c | 58 + mariadb-connector-c-v_2.3.7/libmariadb/my_static.c | 101 + mariadb-connector-c-v_2.3.7/libmariadb/my_static.h | 70 + mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c | 1921 +++++++++ .../libmariadb/my_stmt_codec.c | 1117 +++++ .../libmariadb/my_symlink.c | 138 + .../libmariadb/my_thr_init.c | 246 ++ .../libmariadb/my_vsnprintf.c | 119 + mariadb-connector-c-v_2.3.7/libmariadb/my_write.c | 90 + .../libmariadb/mysql_async.c | 2000 +++++++++ .../libmariadb/mysys_priv.h | 32 + mariadb-connector-c-v_2.3.7/libmariadb/net.c | 868 ++++ mariadb-connector-c-v_2.3.7/libmariadb/password.c | 234 ++ mariadb-connector-c-v_2.3.7/libmariadb/sha1.c | 325 ++ mariadb-connector-c-v_2.3.7/libmariadb/str2int.c | 202 + mariadb-connector-c-v_2.3.7/libmariadb/strcend.c | 48 + mariadb-connector-c-v_2.3.7/libmariadb/strcont.c | 46 + mariadb-connector-c-v_2.3.7/libmariadb/strend.c | 50 + mariadb-connector-c-v_2.3.7/libmariadb/strfill.c | 36 + mariadb-connector-c-v_2.3.7/libmariadb/string.c | 127 + mariadb-connector-c-v_2.3.7/libmariadb/strinstr.c | 50 + mariadb-connector-c-v_2.3.7/libmariadb/strmake.c | 54 + mariadb-connector-c-v_2.3.7/libmariadb/strmov.c | 59 + mariadb-connector-c-v_2.3.7/libmariadb/strnlen.c | 36 + mariadb-connector-c-v_2.3.7/libmariadb/strnmov.c | 36 + mariadb-connector-c-v_2.3.7/libmariadb/strto.c | 209 + mariadb-connector-c-v_2.3.7/libmariadb/strtoll.c | 198 + mariadb-connector-c-v_2.3.7/libmariadb/strtoull.c | 26 + mariadb-connector-c-v_2.3.7/libmariadb/strxmov.c | 50 + mariadb-connector-c-v_2.3.7/libmariadb/strxnmov.c | 48 + mariadb-connector-c-v_2.3.7/libmariadb/thr_mutex.c | 231 + mariadb-connector-c-v_2.3.7/libmariadb/typelib.c | 106 + mariadb-connector-c-v_2.3.7/libmariadb/violite.c | 709 ++++ 90 files changed, 34660 insertions(+) create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/CMakeLists.txt create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/acinclude.m4 create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/array.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/bchange.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/bmove.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/bmove_upp.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/charset.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/client_plugin.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/dbug.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/default.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/errmsg.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/errors.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/get_password.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/getopt.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/getopt1.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/hash.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/int2str.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/is_prefix.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/list.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/llstr.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/longlong2str.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/ma_dtoa.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/ma_dyncol.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/ma_secure.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/ma_time.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_dirname.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_fn_ext.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_format.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_loadpath.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_pack.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_path.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_unixpath.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mf_wcomp.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mulalloc.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_alloc.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_auth.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_charset.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_compress.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_context.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_div.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_error.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_fopen.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_fstream.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_getwd.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_init.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_lib.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_loaddata.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_malloc.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_messnc.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_net.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_once.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_open.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_port.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_pthread.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_read.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_realloc.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_seek.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_static.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_static.h create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_stmt_codec.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_symlink.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_thr_init.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_vsnprintf.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/my_write.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mysql_async.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/mysys_priv.h create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/net.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/password.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/sha1.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/str2int.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strcend.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strcont.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strend.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strfill.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/string.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strinstr.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strmake.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strmov.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strnlen.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strnmov.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strto.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strtoll.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strtoull.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strxmov.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/strxnmov.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/thr_mutex.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/typelib.c create mode 100644 mariadb-connector-c-v_2.3.7/libmariadb/violite.c (limited to 'mariadb-connector-c-v_2.3.7/libmariadb') diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/CMakeLists.txt b/mariadb-connector-c-v_2.3.7/libmariadb/CMakeLists.txt new file mode 100644 index 0000000..03b20c9 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/CMakeLists.txt @@ -0,0 +1,460 @@ +INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include + ${ZLIB_INC} + ${CC_SOURCE_DIR}/libmariadb) + +ADD_DEFINITIONS(-D ENABLED_LOCAL_INFILE) +ADD_DEFINITIONS(-D HAVE_COMPRESS) +ADD_DEFINITIONS(-D LIBMARIADB) +ADD_DEFINITIONS(-D THREAD) + +SET(EXPORT_SYMBOLS + load_defaults + mariadb_connection + mariadb_convert_string + 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 + myodbc_remove_escape + mysql_affected_rows + mysql_autocommit + mysql_autocommit_cont + mysql_autocommit_start + mysql_change_user + mysql_change_user_cont + mysql_change_user_start + mysql_character_set_name + mysql_client_find_plugin + mysql_client_register_plugin + mysql_close + mysql_close_cont + mysql_close_start + mysql_commit + mysql_commit_cont + mysql_commit_start + mysql_data_seek + mysql_debug + mysql_dump_debug_info + mysql_dump_debug_info_cont + mysql_dump_debug_info_start + 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_fetch_row_cont + mysql_fetch_row_start + mysql_field_count + mysql_field_seek + mysql_field_tell + mysql_free_result + mysql_free_result_cont + mysql_free_result_start + 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_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_get_timeout_value + mysql_get_timeout_value_ms + mysql_hex_string + mysql_info + mysql_init + mysql_insert_id + mysql_kill + mysql_kill_cont + mysql_kill_start + mysql_list_dbs + mysql_list_dbs_cont + mysql_list_dbs_start + mysql_list_fields + mysql_list_fields_cont + mysql_list_fields_start + mysql_list_processes + mysql_list_processes_cont + mysql_list_processes_start + mysql_list_tables + mysql_list_tables_cont + mysql_list_tables_start + mysql_load_plugin; + mysql_load_plugin_v + mysql_more_results + mysql_next_result + mysql_next_result_cont + mysql_next_result_start + mysql_num_fields + mysql_num_rows + mysql_options + mysql_optionsv + mysql_ping + mysql_ping_cont + mysql_ping_start + mysql_ps_fetch_functions + mysql_query + mysql_query_cont + mysql_query_start + mysql_read_query_result + mysql_read_query_result_cont + mysql_read_query_result_start + mysql_real_connect + mysql_real_connect_cont + mysql_real_connect_start + mysql_real_escape_string + mysql_real_query + mysql_real_query_cont + mysql_real_query_start + mysql_refresh + mysql_refresh_cont + mysql_refresh_start + mysql_rollback + mysql_rollback_cont + mysql_rollback_start + mysql_row_seek + mysql_row_tell + mysql_select_db + mysql_select_db_cont + mysql_select_db_start + mysql_send_query + mysql_send_query_cont + mysql_send_query_start + mysql_server_end + mysql_server_init + mysql_set_character_set + mysql_set_character_set_cont + mysql_set_character_set_start + mysql_set_local_infile_default + mysql_set_local_infile_handler + mysql_set_server_option + mysql_set_server_option_cont + mysql_set_server_option_start + mysql_shutdown + mysql_shutdown_cont + mysql_shutdown_start + mysql_sqlstate + mysql_ssl_set + mysql_stat + mysql_stat_cont + mysql_stat_start + 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_close_cont + mysql_stmt_close_start + mysql_stmt_data_seek + mysql_stmt_errno + mysql_stmt_error + mysql_stmt_execute + mysql_stmt_execute_cont + mysql_stmt_execute_start + mysql_stmt_fetch + mysql_stmt_fetch_column + mysql_stmt_fetch_cont + mysql_stmt_fetch_start + mysql_stmt_field_count + mysql_stmt_free_result + mysql_stmt_free_result_cont + mysql_stmt_free_result_start + mysql_stmt_init + mysql_stmt_insert_id + mysql_stmt_more_results + mysql_stmt_next_result + mysql_stmt_next_result_cont + mysql_stmt_next_result_start + mysql_stmt_num_rows + mysql_stmt_param_count + mysql_stmt_param_metadata + mysql_stmt_prepare + mysql_stmt_prepare_cont + mysql_stmt_prepare_start + mysql_stmt_reset + mysql_stmt_reset_cont + mysql_stmt_reset_start + mysql_stmt_result_metadata + mysql_stmt_row_seek + mysql_stmt_row_tell + mysql_stmt_send_long_data + mysql_stmt_send_long_data_cont + mysql_stmt_send_long_data_start + mysql_stmt_sqlstate + mysql_stmt_store_result + mysql_stmt_store_result_cont + mysql_stmt_store_result_start + mysql_store_result + mysql_store_result_cont + mysql_store_result_start + mysql_thread_end + mysql_thread_id + mysql_thread_init + mysql_thread_safe + mysql_use_result + mysql_warning_count) + +IF(WITH_OPENSSL) + SET(EXPORT_SYMBOLS ${EXPORT_SYMBOLS} mariadb_deinitialize_ssl) +ENDIF() + +IF(WIN32) + SET(EXPORT_CONTENT "EXPORTS\n") + FOREACH(SYMBOL ${EXPORT_SYMBOLS}) + SET(EXPORT_CONTENT "${EXPORT_CONTENT} ${SYMBOL}\n") + ENDFOREACH() + SET(EXPORT_FILE "${CC_BINARY_DIR}/libmariadb/exports.def") + SET(EXPORT_LINK ${EXPORT_FILE}) +ELSE() + SET(EXPORT_CONTENT "{\nglobal:\n") + FOREACH(SYMBOL ${EXPORT_SYMBOLS}) + SET(EXPORT_CONTENT "${EXPORT_CONTENT} ${SYMBOL}\\;\n") + ENDFOREACH() + SET(EXPORT_FILE "${CC_BINARY_DIR}/libmariadb/exports.txt") + SET(EXPORT_CONTENT "${EXPORT_CONTENT}local:\n *\\;\n}\\;") +ENDIF() + +FILE(WRITE ${EXPORT_FILE} ${EXPORT_CONTENT}) + +SET(LIBMARIADB_SOURCES +array.c +ma_dyncol.c +bchange.c +bmove.c +bmove_upp.c +my_charset.c +hash.c +violite.c +net.c +charset.c +ma_time.c +dbug.c +default.c +errmsg.c +my_vsnprintf.c +errors.c +getopt1.c +getopt.c +int2str.c +is_prefix.c +libmariadb.c +list.c +llstr.c +longlong2str.c +ma_dtoa.c +mf_dirname.c +mf_fn_ext.c +mf_format.c +mf_loadpath.c +mf_pack.c +mf_path.c +mf_unixpath.c +mf_wcomp.c +mulalloc.c +my_alloc.c +my_compress.c +my_context.c +my_div.c +my_error.c +my_fopen.c +my_fstream.c +my_getwd.c +my_init.c +my_lib.c +my_malloc.c +my_messnc.c +my_net.c +my_once.c +my_open.c +my_port.c +my_pthread.c +my_read.c +my_realloc.c +my_seek.c +my_static.c +my_symlink.c +my_thr_init.c +my_write.c +mysql_async.c +password.c +str2int.c +strcend.c +strcont.c +strend.c +strfill.c +string.c +strinstr.c +strmake.c +strmov.c +strnmov.c +strtoll.c +strtoull.c +strxmov.c +strxnmov.c +thr_mutex.c +typelib.c +sha1.c +my_stmt.c +my_loaddata.c +my_stmt_codec.c +client_plugin.c +my_auth.c +) + +# 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(WITH_OPENSSL) + SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ma_secure.c) +ENDIF() + +IF(WIN32) + INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/win-iconv) + SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} + ${CC_SOURCE_DIR}/win-iconv/win_iconv.c) +ENDIF() + +IF(ZLIB_FOUND) + INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR}) + LINK_LIBRARIES(${ZLIB_LIBRARY}) +ELSE() + SET(ZLIB_SOURCES + ../zlib/adler32.c + ../zlib/compress.c + ../zlib/crc32.c + ../zlib/deflate.c + ../zlib/gzclose.c + ../zlib/gzlib.c + ../zlib/gzread.c + ../zlib/gzwrite.c + ../zlib/infback.c + ../zlib/inffast.c + ../zlib/inflate.c + ../zlib/inftrees.c + ../zlib/trees.c + ../zlib/uncompr.c + ../zlib/zutil.c + ) +SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} ${ZLIB_SOURCES}) + INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/zlib) +ENDIF() + +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") + SET_VERSION_INFO("TARGET:mariadbclient" + "FILE_TYPE:VFT_STATIC_LIB" + "SOURCE_FILE:libmariadb/libmariadb.c" + "ORIGINAL_FILE_NAME:mariadbclient.lib" + "FILE_DESCRIPTION:Static lib for client/server communication") +ENDIF() + +# 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() + +# 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_SOURCE_DIR}/libmariadb/empty.c "") + SET(EMPTY_FILE ${CC_SOURCE_DIR}/libmariadb/empty.c) +ENDIF() + +ADD_LIBRARY(mariadbclient STATIC ${mariadbclient_RC} $ ${EMPTY_FILE} ${EXPORT_LINK}) +TARGET_LINK_LIBRARIES(mariadbclient ${SYSTEM_LIBS}) + +ADD_LIBRARY(libmariadb SHARED ${libmariadb_RC} $ ${EMPTY_FILE} ${EXPORT_LINK}) +TARGET_LINK_LIBRARIES(libmariadb ${SYSTEM_LIBS}) +IF(UNIX) + SET_TARGET_PROPERTIES(libmariadb PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}") +ENDIF() +SIGN_TARGET(libmariadb) + +IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + TARGET_LINK_LIBRARIES (libmariadb "-Wl,--no-undefined") + SET_TARGET_PROPERTIES(libmariadb PROPERTIES LINK_FLAGS "-Wl,--version-script=${EXPORT_FILE}") + TARGET_LINK_LIBRARIES (mariadbclient "-Wl,--no-undefined") + SET_TARGET_PROPERTIES(mariadbclient PROPERTIES LINK_FLAGS "-Wl,--version-script=${EXPORT_FILE}") +ENDIF() + +SET_TARGET_PROPERTIES(libmariadb PROPERTIES PREFIX "") + +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() + + +# +# 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(NOT WIN32 AND WITH_MYSQLCOMPAT) + SET(INSTALL_PATH ${LIB_INSTALL_DIR}/${LIBSUFFIX_INSTALL_DIR}) + create_symlink(libmysqlclient${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_PATH}) + create_symlink(libmysqlclient_r${CMAKE_SHARED_LIBRARY_SUFFIX} libmariadb ${INSTALL_PATH}) + create_symlink(libmysqlclient${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_PATH}) + create_symlink(libmysqlclient_r${CMAKE_STATIC_LIBRARY_SUFFIX} mariadbclient ${INSTALL_PATH}) +ENDIF() + + +INSTALL(TARGETS + libmariadb mariadbclient + RUNTIME DESTINATION "${LIB_INSTALL_DIR}/${LIBSUFFIX_INSTALL_DIR}" + LIBRARY DESTINATION "${LIB_INSTALL_DIR}/${LIBSUFFIX_INSTALL_DIR}" + ARCHIVE DESTINATION "${LIB_INSTALL_DIR}/${LIBSUFFIX_INSTALL_DIR}") + +INSTALL(DIRECTORY ${CC_SOURCE_DIR}/include/ + DESTINATION ${INCLUDE_INSTALL_DIR}/${SUFFIX_INSTALL_DIR} + PATTERN "*.h.in" EXCLUDE + PATTERN "CMakeLists.txt" EXCLUDE + PATTERN "Makefile.am" EXCLUDE) +INSTALL(FILES + ${CC_BINARY_DIR}/include/my_config.h + ${CC_BINARY_DIR}/include/mysql_version.h + DESTINATION ${INCLUDE_INSTALL_DIR}/${SUFFIX_INSTALL_DIR}) + + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/acinclude.m4 b/mariadb-connector-c-v_2.3.7/libmariadb/acinclude.m4 new file mode 100644 index 0000000..c0211f3 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/acinclude.m4 @@ -0,0 +1,91 @@ +# Local macros for automake & autoconf + +AC_DEFUN(MYSQL_TYPE_ACCEPT, +[ac_save_CXXFLAGS="$CXXFLAGS" +AC_CACHE_CHECK([base type of last arg to accept], mysql_cv_btype_last_arg_accept, +AC_LANG_SAVE +AC_LANG_CPLUSPLUS +if test "$ac_cv_prog_gxx" = "yes" +then + CXXFLAGS="$CXXFLAGS -Werror" +fi +mysql_cv_btype_last_arg_accept=none +[AC_TRY_COMPILE([#include +#include +#include +], +[int a = accept(1, (struct sockaddr *) 0, (socklen_t *) 0);], +mysql_cv_btype_last_arg_accept=socklen_t)] +if test $mysql_cv_btype_last_arg_accept = none; then +[AC_TRY_COMPILE([#include +#include +#include +], +[int a = accept(1, (struct sockaddr *) 0, (size_t *) 0);], +mysql_cv_btype_last_arg_accept=size_t)] +fi +if test $mysql_cv_btype_last_arg_accept = none; then +mysql_cv_btype_last_arg_accept=int +fi) +AC_LANG_RESTORE +AC_DEFINE_UNQUOTED(SOCKET_SIZE_TYPE, $mysql_cv_btype_last_arg_accept) +CXXFLAGS="$ac_save_CXXFLAGS" +]) + + +#---START: Used in for client configure +AC_DEFUN(MYSQL_CHECK_ULONG, +[AC_MSG_CHECKING(for type ulong) +AC_CACHE_VAL(ac_cv_ulong, +[AC_TRY_RUN([#include +#include +main() +{ + ulong foo; + foo++; + exit(0); +}], ac_cv_ulong=yes, ac_cv_ulong=no, ac_cv_ulong=no)]) +AC_MSG_RESULT($ac_cv_ulong) +if test "$ac_cv_ulong" = "yes" +then + AC_DEFINE(HAVE_ULONG) +fi +]) + +AC_DEFUN(MYSQL_CHECK_UCHAR, +[AC_MSG_CHECKING(for type uchar) +AC_CACHE_VAL(ac_cv_uchar, +[AC_TRY_RUN([#include +#include +main() +{ + uchar foo; + foo++; + exit(0); +}], ac_cv_uchar=yes, ac_cv_uchar=no, ac_cv_uchar=no)]) +AC_MSG_RESULT($ac_cv_uchar) +if test "$ac_cv_uchar" = "yes" +then + AC_DEFINE(HAVE_UCHAR) +fi +]) + +AC_DEFUN(MYSQL_CHECK_UINT, +[AC_MSG_CHECKING(for type uint) +AC_CACHE_VAL(ac_cv_uint, +[AC_TRY_RUN([#include +#include +main() +{ + uint foo; + foo++; + exit(0); +}], ac_cv_uint=yes, ac_cv_uint=no, ac_cv_uint=no)]) +AC_MSG_RESULT($ac_cv_uint) +if test "$ac_cv_uint" = "yes" +then + AC_DEFINE(HAVE_UINT) +fi +]) + +#---END: diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/array.c b/mariadb-connector-c-v_2.3.7/libmariadb/array.c new file mode 100644 index 0000000..dd46e9a --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/array.c @@ -0,0 +1,175 @@ +/* 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 */ + +/* Handling of arrays that can grow dynamicly. */ + +#undef SAFEMALLOC /* Problems with threads */ + +#include "mysys_priv.h" +#include "m_string.h" + +/* + Initiate array and alloc space for init_alloc elements. Array is usable + even if space allocation failed +*/ + +my_bool init_dynamic_array(DYNAMIC_ARRAY *array, uint element_size, + uint init_alloc, uint alloc_increment CALLER_INFO_PROTO) +{ + DBUG_ENTER("init_dynamic_array"); + 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*) my_malloc_ci(element_size*init_alloc,MYF(MY_WME)))) + { + array->max_element=0; + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +my_bool insert_dynamic(DYNAMIC_ARRAY *array, gptr element) +{ + gptr buffer; + if (array->elements == array->max_element) + { /* Call only when nessesary */ + if (!(buffer=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 *alloc_dynamic(DYNAMIC_ARRAY *array) +{ + if (array->elements == array->max_element) + { + char *new_ptr; + if (!(new_ptr=(char*) my_realloc(array->buffer,(array->max_element+ + array->alloc_increment)* + array->size_of_element, + MYF(MY_WME | MY_ALLOW_ZERO_PTR)))) + return 0; + array->buffer=new_ptr; + array->max_element+=array->alloc_increment; + } + return array->buffer+(array->elements++ * array->size_of_element); +} + + + /* remove last element from array and return it */ + +unsigned char *pop_dynamic(DYNAMIC_ARRAY *array) +{ + if (array->elements) + return array->buffer+(--array->elements * array->size_of_element); + return 0; +} + + +my_bool set_dynamic(DYNAMIC_ARRAY *array, gptr 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*) my_realloc(array->buffer,size* + array->size_of_element, + MYF(MY_WME | MY_ALLOW_ZERO_PTR)))) + return TRUE; + array->buffer=new_ptr; + array->max_element=size; + } + bzero((gptr) (array->buffer+array->elements*array->size_of_element), + (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 get_dynamic(DYNAMIC_ARRAY *array, gptr element, uint idx) +{ + if (idx >= array->elements) + { + DBUG_PRINT("warning",("To big array idx: %d, array size is %d", + idx,array->elements)); + bzero(element,array->size_of_element); + return; + } + memcpy(element,array->buffer+idx*array->size_of_element, + (size_t) array->size_of_element); +} + + +void delete_dynamic(DYNAMIC_ARRAY *array) +{ + if (array->buffer) + { + my_free(array->buffer); + array->buffer=0; + array->elements=array->max_element=0; + } +} + + +void 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 freeze_size(DYNAMIC_ARRAY *array) +{ + uint elements=max(array->elements,1); + + if (array->buffer && array->max_element != elements) + { + array->buffer=(char*) my_realloc(array->buffer, + elements*array->size_of_element, + MYF(MY_WME)); + array->max_element=elements; + } +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/bchange.c b/mariadb-connector-c-v_2.3.7/libmariadb/bchange.c new file mode 100644 index 0000000..7e672ed --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/bchange.c @@ -0,0 +1,39 @@ +/* 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 : bchange.c + Author : Michael widenius + Updated: 1987-03-20 + Defines: bchange() + + bchange(dst, old_length, src, new_length, tot_length) + replaces old_length characters at dst to new_length characters from + src in a buffer with tot_length bytes. +*/ + +#include +#include "m_string.h" + +void bchange(register char *dst, size_t old_length, register const char *src, size_t new_length, size_t tot_length) +{ + size_t rest=tot_length-old_length; + if (old_length < new_length) + bmove_upp(dst+rest+new_length,dst+tot_length,rest); + else + bmove(dst+new_length,dst+old_length,rest); + memcpy(dst,src,new_length); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/bmove.c b/mariadb-connector-c-v_2.3.7/libmariadb/bmove.c new file mode 100644 index 0000000..76689bd --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/bmove.c @@ -0,0 +1,80 @@ +/* Copyright (C) 2002 MySQL 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 : Richard A. O'Keefe. + Michael Widenius; ifdef MC68000 + Updated: 23 April 1984 + Defines: bmove() + + bmove(dst, src, len) moves exactly "len" bytes from the source "src" + to the destination "dst". It does not check for NUL characters as + strncpy() and strnmov() do. Thus if your C compiler doesn't support + structure assignment, you can simulate it with + bmove(&to, &from, sizeof from); + The standard 4.2bsd routine for this purpose is bcopy. But as bcopy + has its first two arguments the other way around you may find this a + bit easier to get right. + No value is returned. + + Note: the "b" routines are there to exploit certain VAX order codes, + but the MOVC3 instruction will only move 65535 characters. The asm + code is presented for your interest and amusement. +*/ + +#include +#include "m_string.h" + +#if !defined(HAVE_BMOVE) && !defined(bmove) + +#if VaxAsm + +void bmove(dst, src, len) + char *dst, *src; + uint len; + { + asm("movc3 12(ap),*8(ap),*4(ap)"); + } + +#else +#if defined(MC68000) && defined(DS90) + +void bmove(dst, src, len) +char *dst,*src; +uint len; /* 0 <= len <= 65535 */ +{ +asm(" movl 12(a7),d0 "); +asm(" subql #1,d0 "); +asm(" blt .L5 "); +asm(" movl 4(a7),a1 "); +asm(" movl 8(a7),a0 "); +asm(".L4: movb (a0)+,(a1)+ "); +asm(" dbf d0,.L4 "); +asm(".L5: "); +} +#else + +void bmove(dst, src, len) +register char *dst; +register const char *src; +register uint len; +{ + while (len-- != 0) *dst++ = *src++; +} +#endif +#endif +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/bmove_upp.c b/mariadb-connector-c-v_2.3.7/libmariadb/bmove_upp.c new file mode 100644 index 0000000..2871c3f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/bmove_upp.c @@ -0,0 +1,51 @@ +/* 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 +#include "m_string.h" + +#if defined(MC68000) && defined(DS90) + +/* 0 <= len <= 65535 */ +void bmove_upp(byte *dst, const byte *src, size_t len) +{ +asm(" movl 12(a7),d0 "); +asm(" subql #1,d0 "); +asm(" blt .L5 "); +asm(" movl 4(a7),a1 "); +asm(" movl 8(a7),a0 "); +asm(".L4: movb -(a0),-(a1) "); +asm(" dbf d0,.L4 "); +asm(".L5: "); +} +#else + +void bmove_upp(register char *dst, register const char *src, register size_t len) +{ + while (len-- != 0) *--dst = *--src; +} + +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/charset.c b/mariadb-connector-c-v_2.3.7/libmariadb/charset.c new file mode 100644 index 0000000..c69f56d --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/charset.c @@ -0,0 +1,78 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include +#include + +CHARSET_INFO *default_charset_info = (CHARSET_INFO *)&compiled_charsets[5]; +CHARSET_INFO *my_charset_bin= (CHARSET_INFO *)&compiled_charsets[32]; +CHARSET_INFO *my_charset_latin1= (CHARSET_INFO *)&compiled_charsets[5]; +CHARSET_INFO *my_charset_utf8_general_ci= (CHARSET_INFO *)&compiled_charsets[21]; + +CHARSET_INFO * STDCALL mysql_get_charset_by_nr(uint cs_number) +{ + int i= 0; + + while (compiled_charsets[i].nr && cs_number != compiled_charsets[i].nr) + i++; + + return (compiled_charsets[i].nr) ? (CHARSET_INFO *)&compiled_charsets[i] : NULL; +} + +my_bool set_default_charset(uint cs, myf flags) +{ + CHARSET_INFO *new_charset; + DBUG_ENTER("set_default_charset"); + DBUG_PRINT("enter",("character set: %d",(int) cs)); + new_charset = mysql_get_charset_by_nr(cs); + if (!new_charset) + { + DBUG_PRINT("error",("Couldn't set default character set")); + DBUG_RETURN(TRUE); /* error */ + } + default_charset_info = new_charset; + DBUG_RETURN(FALSE); +} + +CHARSET_INFO * STDCALL mysql_get_charset_by_name(const char *cs_name) +{ + int i= 0; + + while (compiled_charsets[i].nr && strcmp(cs_name, compiled_charsets[i].csname) != 0) + i++; + + return (compiled_charsets[i].nr) ? (CHARSET_INFO *)&compiled_charsets[i] : NULL; +} + +my_bool set_default_charset_by_name(const char *cs_name, myf flags) +{ + CHARSET_INFO *new_charset; + DBUG_ENTER("set_default_charset_by_name"); + DBUG_PRINT("enter",("character set: %s", cs_name)); + new_charset = mysql_get_charset_by_name(cs_name); + if (!new_charset) + { + DBUG_PRINT("error",("Couldn't set default character set")); + DBUG_RETURN(TRUE); /* error */ + } + + default_charset_info = new_charset; + DBUG_RETURN(FALSE); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/client_plugin.c b/mariadb-connector-c-v_2.3.7/libmariadb/client_plugin.c new file mode 100644 index 0000000..71d2039 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/client_plugin.c @@ -0,0 +1,466 @@ +/* Copyright (C) 2010 - 2012 Sergei Golubchik and 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 + 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. +*/ + +#if _MSC_VER +/* Silence warnings about variable 'unused' being used. */ +#define FORCE_INIT_OF_VARS 1 +#endif + +#include +#include +#include +#include +#ifdef THREAD +#include +#else +#include +#endif + +#include "errmsg.h" +#include + +#ifndef _WIN32 +#include +#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 MEM_ROOT mem_root; + +#define plugin_declarations_sym "_mysql_client_plugin_declaration_" + +static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= +{ + MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ + 0, /* these two are taken by Connector/C */ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION +}; + +/* + 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]; +#ifdef THREAD +static pthread_mutex_t LOCK_load_client_plugin; +#endif + +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; +} + + +/** + 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; + + DBUG_ASSERT(initialized); + DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS); + if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + return 0; + + for (p= plugin_list[type]; 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]; + + DBUG_ASSERT(initialized); + + plugin_int.plugin= plugin; + plugin_int.dlhandle= dlhandle; + + if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS) + { + errmsg= "Unknown client plugin type"; + goto err1; + } + + if (plugin->interface_version < plugin_version[plugin->type] || + (plugin->interface_version >> 8) > + (plugin_version[plugin->type] >> 8)) + { + errmsg= "Incompatible client plugin interface"; + 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 *) + memdup_root(&mem_root, (char *)&plugin_int, sizeof(plugin_int)); + + if (!p) + { + errmsg= "Out of memory"; + goto err2; + } + +#ifdef THREAD + safe_mutex_assert_owner(&LOCK_load_client_plugin); +#endif + + p->next= plugin_list[plugin->type]; + plugin_list[plugin->type]= p; + + return plugin; + +err2: + if (plugin->deinit) + plugin->deinit(); +err1: + if (dlhandle) + (void)dlclose(dlhandle); + my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, + ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg); + 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"); + + /* no plugins to load */ + if (!s) + return; + + free_env= plugs= my_strdup(s, MYF(MY_WME)); + + do { + if ((s= strchr(plugs, ';'))) + *s= '\0'; + mysql_load_plugin(mysql, plugs, -1, 0); + plugs= s + 1; + } while (s); + + my_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 occured +*/ + +int mysql_client_plugin_init() +{ + MYSQL mysql; + struct st_mysql_client_plugin **builtin; + va_list unused; + LINT_INIT_STRUCT(unused); + + if (initialized) + return 0; + + bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */ + + pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW); + init_alloc_root(&mem_root, 128, 128); + + bzero(&plugin_list, 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); + } + + bzero(&plugin_list, sizeof(plugin_list)); + initialized= 0; + free_root(&mem_root, MYF(0)); + pthread_mutex_destroy(&LOCK_load_client_plugin); +} + +/************* public facing functions, for client consumption *********/ + +/* see for a full description */ +struct st_mysql_client_plugin * STDCALL +mysql_client_register_plugin(MYSQL *mysql, + struct st_mysql_client_plugin *plugin) +{ + 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 (find_plugin(plugin->name, plugin->type)) + { + my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, + SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), + plugin->name, "it is already loaded"); + plugin= NULL; + } + else + plugin= add_plugin(mysql, plugin, 0, 0, unused); + + pthread_mutex_unlock(&LOCK_load_client_plugin); + return plugin; +} + + +/* see 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[255]; +#endif + char dlpath[FN_REFLEN+1]; + void *sym, *dlhandle; + struct st_mysql_client_plugin *plugin; + char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR"); + + 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 */ + strxnmov(dlpath, sizeof(dlpath) - 1, + mysql->options.extension && mysql->options.extension->plugin_dir ? + mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir : + PLUGINDIR, "/", + name, SO_EXT, NullS); + + /* Open new dll handle */ + if (!(dlhandle= dlopen((const char *)dlpath, RTLD_NOW))) + { +#ifdef _WIN32 + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&errbuf, 255, NULL); + 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: + 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 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 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; + + if (is_not_initialized(mysql, name)) + return NULL; + + if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + { + 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/mariadb-connector-c-v_2.3.7/libmariadb/dbug.c b/mariadb-connector-c-v_2.3.7/libmariadb/dbug.c new file mode 100644 index 0000000..cd4a0f1 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/dbug.c @@ -0,0 +1,2457 @@ +/****************************************************************************** + * * + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * * + ****************************************************************************** + */ + +/* + * FILE + * + * dbug.c runtime support routines for dbug package + * + * SCCS + * + * @(#)dbug.c 1.25 7/25/89 + * + * DESCRIPTION + * + * These are the runtime support routines for the dbug package. + * The dbug package has two main components; the user include + * file containing various macro definitions, and the runtime + * support routines which are called from the macro expansions. + * + * Externally visible functions in the runtime support module + * use the naming convention pattern "_db_xx...xx_", thus + * they are unlikely to collide with user defined function names. + * + * AUTHOR(S) + * + * Fred Fish (base code) + * Enhanced Software Technologies, Tempe, AZ + * asuvax!mcdphx!estinc!fnf + * + * Binayak Banerjee (profiling enhancements) + * seismo!bpa!sjuvax!bbanerje + * + * Michael Widenius: + * DBUG_DUMP - To dump a block of memory. + * PUSH_FLAG "O" - To be used insted of "o" if we + * want flushing after each write + * PUSH_FLAG "A" - as 'O', but we will append to the out file instead + * of creating a new one. + * Check of malloc on entry/exit (option "S") + * + * Sergei Golubchik: + * DBUG_EXECUTE_IF + * incremental mode (-#+t:-d,info ...) + * DBUG_SET, _db_explain_ + * thread-local settings + * negative lists (-#-d,info => everything but "info") + * + * function/ syntax + * (the logic is - think of a call stack as of a path. + * "function" means only this function, "function/" means the hierarchy. + * in the future, filters like function1/function2 could be supported. + * following this logic glob(7) wildcards are supported.) + * + */ + +/* + We can't have SAFE_MUTEX defined here as this will cause recursion + in pthread_mutex_lock +*/ + +#undef SAFE_MUTEX +#include +#include +#include + +#ifdef HAVE_FNMATCH_H +#include +#else +#define fnmatch(A,B,C) strcmp(A,B) +#endif + +#if defined(_WIN32) +#include +#else +#include +#include +#endif + +extern int my_snprintf(char* to, size_t n, const char* fmt, ...); + +char _dig_vec_upper[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +char _dig_vec_lower[] = + "0123456789abcdefghijklmnopqrstuvwxyz"; + +#ifndef DBUG_OFF + + +/* + * Manifest constants which may be "tuned" if desired. + */ + +#define PRINTBUF 1024 /* Print buffer size */ +#define INDENT 2 /* Indentation per trace level */ +#define MAXDEPTH 200 /* Maximum trace depth default */ + +/* + * The following flags are used to determine which + * capabilities the user has enabled with the settings + * push macro. + * + * TRACE_ON is also used in _db_stack_frame_->level + * (until we add flags to _db_stack_frame_, increasing it by 4 bytes) + */ + +#define DEBUG_ON (1 << 1) /* Debug enabled */ +#define FILE_ON (1 << 2) /* File name print enabled */ +#define LINE_ON (1 << 3) /* Line number print enabled */ +#define DEPTH_ON (1 << 4) /* Function nest level print enabled */ +#define PROCESS_ON (1 << 5) /* Process name print enabled */ +#define NUMBER_ON (1 << 6) /* Number each line of output */ +#define PROFILE_ON (1 << 7) /* Print out profiling code */ +#define PID_ON (1 << 8) /* Identify each line with process id */ +#define TIMESTAMP_ON (1 << 9) /* timestamp every line of output */ +#define FLUSH_ON_WRITE (1 << 10) /* Flush on every write */ +#define OPEN_APPEND (1 << 11) /* Open for append */ +#define TRACE_ON ((uint)1 << 31) /* Trace enabled. MUST be the highest bit!*/ + +#define TRACING (cs->stack->flags & TRACE_ON) +#define DEBUGGING (cs->stack->flags & DEBUG_ON) +#define PROFILING (cs->stack->flags & PROFILE_ON) + +/* + * Typedefs to make things more obvious. + */ + +#define BOOLEAN my_bool + +/* + * Make it easy to change storage classes if necessary. + */ + +#define IMPORT extern /* Names defined externally */ +#define EXPORT /* Allocated here, available globally */ + +/* + * The default file for profiling. Could also add another flag + * (G?) which allowed the user to specify this. + * + * If the automatic variables get allocated on the stack in + * reverse order from their declarations, then define AUTOS_REVERSE to 1. + * This is used by the code that keeps track of stack usage. For + * forward allocation, the difference in the dbug frame pointers + * represents stack used by the callee function. For reverse allocation, + * the difference represents stack used by the caller function. + * + */ + +#define PROF_FILE "dbugmon.out" +#define PROF_EFMT "E\t%ld\t%s\n" +#define PROF_SFMT "S\t%lx\t%lx\t%s\n" +#define PROF_XFMT "X\t%ld\t%s\n" + +#ifdef M_I386 /* predefined by xenix 386 compiler */ +#define AUTOS_REVERSE 1 +#else +#define AUTOS_REVERSE 0 +#endif + +/* + * Externally supplied functions. + */ + +#ifndef HAVE_PERROR +static void perror(); /* Fake system/library error print routine */ +#endif + +/* + * The user may specify a list of functions to trace or + * debug. These lists are kept in a linear linked list, + * a very simple implementation. + */ + +struct link { + struct link *next_link; /* Pointer to the next link */ + char flags; + char str[1]; /* Pointer to link's contents */ +}; + +/* flags for struct link and return flags of InList */ +#define SUBDIR 1 /* this MUST be 1 */ +#define INCLUDE 2 +#define EXCLUDE 4 +/* this is not a struct link flag, but only a return flags of InList */ +#define MATCHED 65536 +#define NOT_MATCHED 0 + +/* + * Debugging settings can be pushed or popped off of a + * stack which is implemented as a linked list. Note + * that the head of the list is the current settings and the + * stack is pushed by adding a new settings to the head of the + * list or popped by removing the first link. + * + * Note: if out_file is NULL, the other fields are not initialized at all! + */ + +struct settings { + uint flags; /* Current settings flags */ + uint maxdepth; /* Current maximum trace depth */ + uint delay; /* Delay after each output line */ + uint sub_level; /* Sub this from code_state->level */ + FILE *out_file; /* Current output stream */ + FILE *prof_file; /* Current profiling stream */ + char name[FN_REFLEN]; /* Name of output file */ + struct link *functions; /* List of functions */ + struct link *p_functions; /* List of profiled functions */ + struct link *keywords; /* List of debug keywords */ + struct link *processes; /* List of process names */ + struct settings *next; /* Next settings in the list */ +}; + +#define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V) + +/* + * Local variables not seen by user. + */ + + +static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */ +/** + Global debugging settings. + This structure shared between all threads, + and is the last element in each thread @c CODE_STATE::stack chain. + Protected by @c THR_LOCK_init_settings. +*/ +static struct settings init_settings; +static const char *db_process= 0;/* Pointer to process name; argv[0] */ +my_bool _dbug_on_= TRUE; /* FALSE if no debugging at all */ + +typedef struct _db_code_state_ { + const char *process; /* Pointer to process name; usually argv[0] */ + const char *func; /* Name of current user function */ + const char *file; /* Name of current user file */ + struct _db_stack_frame_ *framep; /* Pointer to current frame */ + struct settings *stack; /* debugging settings */ + const char *jmpfunc; /* Remember current function for setjmp */ + const char *jmpfile; /* Remember current file for setjmp */ + int lineno; /* Current debugger output line number */ + uint level; /* Current function nesting level */ + int jmplevel; /* Remember nesting level at setjmp() */ + +/* + * The following variables are used to hold the state information + * between the call to _db_pargs_() and _db_doprnt_(), during + * expansion of the DBUG_PRINT macro. This is the only macro + * that currently uses these variables. + * + * These variables are currently used only by _db_pargs_() and + * _db_doprnt_(). + */ + + uint u_line; /* User source code line number */ + int locked; /* If locked with _db_lock_file_ */ + const char *u_keyword; /* Keyword for current macro */ + uint m_read_lock_count; +} CODE_STATE; + +/* + The test below is so we could call functions with DBUG_ENTER before + my_thread_init(). +*/ +#define get_code_state_if_not_set_or_return if (!cs && !((cs=code_state()))) return +#define get_code_state_or_return if (!((cs=code_state()))) return + + /* Handling lists */ +#define ListAdd(A,B,C) ListAddDel(A,B,C,INCLUDE) +#define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE) +static struct link *ListAddDel(struct link *, const char *, const char *, int); +static struct link *ListCopy(struct link *); +static int InList(struct link *linkp,const char *cp); +static uint ListFlags(struct link *linkp); +static void FreeList(struct link *linkp); + + /* OpenClose debug output stream */ +static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int); +static void DBUGCloseFile(CODE_STATE *cs, FILE *fp); + /* Push current debug settings */ +static void PushState(CODE_STATE *cs); + /* Free memory associated with debug state. */ +static void FreeState (CODE_STATE *cs, struct settings *state, int free_state); + /* Test for tracing enabled */ +static int DoTrace(CODE_STATE *cs); +/* + return values of DoTrace. + Can also be used as bitmask: ret & DO_TRACE +*/ +#define DO_TRACE 1 +#define DONT_TRACE 2 +#define ENABLE_TRACE 3 +#define DISABLE_TRACE 4 + + /* Test to see if file is writable */ +#if defined(HAVE_ACCESS) +static BOOLEAN Writable(const char *pathname); +#endif + +static void DoPrefix(CODE_STATE *cs, uint line); + +static char *DbugMalloc(size_t size); +static const char *BaseName(const char *pathname); +static void Indent(CODE_STATE *cs, int indent); +static void DbugFlush(CODE_STATE *); +static void DbugExit(const char *why); +static const char *DbugStrTok(const char *s); + +/* + * Miscellaneous printf format strings. + */ + +#define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n" +#define ERR_MISSING_UNLOCK "missing DBUG_UNLOCK_FILE macro in function \"%s\"\n" +#define ERR_OPEN "%s: can't open debug output stream \"%s\": " +#define ERR_CLOSE "%s: can't close debug file: " +#define ERR_ABORT "%s: debugger aborting because %s\n" + +/* + * Macros and defines for testing file accessibility under UNIX and MSDOS. + */ + +#undef EXISTS +#if !defined(HAVE_ACCESS) +#define EXISTS(pathname) (FALSE) /* Assume no existance */ +#define Writable(name) (TRUE) +#else +#define EXISTS(pathname) (access(pathname, F_OK) == 0) +#define WRITABLE(pathname) (access(pathname, W_OK) == 0) +#endif + + +/* +** Macros to allow dbugging with threads +*/ + +#include +static pthread_mutex_t THR_LOCK_dbug; + +/** + A mutex protecting flushing of gcov data, see _db_flush_gcov_(). + We don't re-use THR_LOCK_dbug, because that would disallow: + DBUG_LOCK_FILE; ..... DBUG_SUICIDE(); .... DBUG_UNLOCK_FILE; +*/ +static pthread_mutex_t THR_LOCK_gcov; +static pthread_mutex_t THR_LOCK_init_settings; + +static CODE_STATE *code_state(void) +{ + CODE_STATE *cs, **cs_ptr; + + /* + _dbug_on_ is reset if we don't plan to use any debug commands at all and + we want to run on maximum speed + */ + if (!_dbug_on_) + return 0; + + if (!init_done) + { + init_done=TRUE; + pthread_mutex_init(&THR_LOCK_dbug, NULL); + pthread_mutex_init(&THR_LOCK_gcov, NULL); + pthread_mutex_init(&THR_LOCK_init_settings, NULL); + memset(&init_settings, 0, sizeof(init_settings)); + init_settings.out_file=stderr; + init_settings.flags=OPEN_APPEND; + } + + if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug())) + return 0; /* Thread not initialised */ + if (!(cs= *cs_ptr)) + { + cs=(CODE_STATE*) DbugMalloc(sizeof(*cs)); + memset(cs, 0, sizeof(*cs)); + cs->process= db_process ? db_process : "dbug"; + cs->func="?func"; + cs->file="?file"; + cs->stack=&init_settings; + cs->m_read_lock_count= 0; + *cs_ptr= cs; + } + return cs; +} + +/** + Lock the stack debugging settings. + Only the shared (global) settings are locked if necessary, + per thread settings are local and safe to use. + This lock is re entrant. + @sa unlock_stack +*/ +static void read_lock_stack(CODE_STATE *cs) +{ + if (cs->stack == &init_settings) + { + if (++(cs->m_read_lock_count) == 1) + pthread_mutex_lock(&THR_LOCK_init_settings); + } +} + +/** + Unlock the stack debugging settings. + @sa read_lock_stack +*/ +static void unlock_stack(CODE_STATE *cs) +{ + if (cs->stack == &init_settings) + { + if (--(cs->m_read_lock_count) == 0) + pthread_mutex_unlock(&THR_LOCK_init_settings); + } +} + +/* + * Translate some calls among different systems. + */ + +#ifdef HAVE_SLEEP +/* sleep() wants seconds */ +#define Delay(A) sleep(((uint) A)/10) +#else +#define Delay(A) (0) +#endif + +/* + * FUNCTION + * + * _db_process_ give the name to the current process/thread + * + * SYNOPSIS + * + * VOID _process_(name) + * char *name; + * + */ + +void _db_process_(const char *name) +{ + CODE_STATE *cs; + + if (!db_process) + db_process= name; + + get_code_state_or_return; + cs->process= name; +} + +/* + * FUNCTION + * + * DbugParse parse control string and set current debugger settings + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", + * parses the control string, and sets + * up a current debug settings. + * + * The debug control string is a sequence of colon separated fields + * as follows: + * + * [+]::...: + * + * Each field consists of a mandatory flag character followed by + * an optional "," and comma separated list of modifiers: + * + * [sign]flag[,modifier,modifier,...,modifier] + * + * See the manual for the list of supported signs, flags, and modifiers + * + * For convenience, any leading "-#" is stripped off. + * + * RETURN + * 1 - a list of functions ("f" flag) was possibly changed + * 0 - a list of functions was not changed + */ + +int DbugParse(CODE_STATE *cs, const char *control) +{ + const char *end; + int rel, f_used=0; + struct settings *stack; + + /* + Make sure we are not changing settings while inside a + DBUG_LOCK_FILE + DBUG_UNLOCK_FILE + section, that is a mis use, that would cause changing + DBUG_FILE while the caller prints to it. + */ + assert(! cs->locked); + + stack= cs->stack; + + /* + When parsing the global init_settings itself, + make sure to block every other thread using dbug functions. + */ + assert(cs->m_read_lock_count == 0); + if (stack == &init_settings) + pthread_mutex_lock(&THR_LOCK_init_settings); + + if (control[0] == '-' && control[1] == '#') + control+=2; + + rel= control[0] == '+' || control[0] == '-'; + if ((!rel || (!stack->out_file && !stack->next))) + { + /* Free memory associated with the state before resetting its members */ + FreeState(cs, stack, 0); + stack->flags= 0; + stack->delay= 0; + stack->maxdepth= 0; + stack->sub_level= 0; + stack->out_file= stderr; + stack->prof_file= NULL; + stack->functions= NULL; + stack->p_functions= NULL; + stack->keywords= NULL; + stack->processes= NULL; + } + else if (!stack->out_file) + { + stack->flags= stack->next->flags; + stack->delay= stack->next->delay; + stack->maxdepth= stack->next->maxdepth; + stack->sub_level= stack->next->sub_level; + strcpy(stack->name, stack->next->name); + stack->prof_file= stack->next->prof_file; + if (stack->next == &init_settings) + { + assert(stack != &init_settings); + pthread_mutex_lock(&THR_LOCK_init_settings); + + /* + Never share with the global parent - it can change under your feet. + + Reset out_file to stderr to prevent sharing of trace files between + global and session settings. + */ + stack->out_file= stderr; + stack->functions= ListCopy(init_settings.functions); + stack->p_functions= ListCopy(init_settings.p_functions); + stack->keywords= ListCopy(init_settings.keywords); + stack->processes= ListCopy(init_settings.processes); + + pthread_mutex_unlock(&THR_LOCK_init_settings); + } + else + { + stack->out_file= stack->next->out_file; + stack->functions= stack->next->functions; + stack->p_functions= stack->next->p_functions; + stack->keywords= stack->next->keywords; + stack->processes= stack->next->processes; + } + } + + end= DbugStrTok(control); + while (control < end) + { + int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0; + if (sign) control++; + c= *control++; + if (*control == ',') control++; + /* XXX when adding new cases here, don't forget _db_explain_ ! */ + switch (c) { + case 'd': + if (sign < 0 && control == end) + { + if (!is_shared(stack, keywords)) + FreeList(stack->keywords); + stack->keywords=NULL; + stack->flags &= ~DEBUG_ON; + break; + } + if (rel && is_shared(stack, keywords)) + stack->keywords= ListCopy(stack->keywords); + if (sign < 0) + { + if (DEBUGGING) + stack->keywords= ListDel(stack->keywords, control, end); + break; + } + stack->keywords= ListAdd(stack->keywords, control, end); + stack->flags |= DEBUG_ON; + break; + case 'D': + stack->delay= atoi(control); + break; + case 'f': + f_used= 1; + if (sign < 0 && control == end) + { + if (!is_shared(stack,functions)) + FreeList(stack->functions); + stack->functions=NULL; + break; + } + if (rel && is_shared(stack,functions)) + stack->functions= ListCopy(stack->functions); + if (sign < 0) + stack->functions= ListDel(stack->functions, control, end); + else + stack->functions= ListAdd(stack->functions, control, end); + break; + case 'F': + if (sign < 0) + stack->flags &= ~FILE_ON; + else + stack->flags |= FILE_ON; + break; + case 'i': + if (sign < 0) + stack->flags &= ~PID_ON; + else + stack->flags |= PID_ON; + break; + case 'L': + if (sign < 0) + stack->flags &= ~LINE_ON; + else + stack->flags |= LINE_ON; + break; + case 'n': + if (sign < 0) + stack->flags &= ~DEPTH_ON; + else + stack->flags |= DEPTH_ON; + break; + case 'N': + if (sign < 0) + stack->flags &= ~NUMBER_ON; + else + stack->flags |= NUMBER_ON; + break; + case 'A': + case 'O': + stack->flags |= FLUSH_ON_WRITE; + /* fall through */ + case 'a': + case 'o': + /* In case we already have an open file. */ + if (!is_shared(stack, out_file)) + DBUGCloseFile(cs, stack->out_file); + if (sign < 0) + { + stack->flags &= ~FLUSH_ON_WRITE; + stack->out_file= stderr; + break; + } + if (c == 'a' || c == 'A') + stack->flags |= OPEN_APPEND; + else + stack->flags &= ~OPEN_APPEND; + if (control != end) + DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND); + else + DBUGOpenFile(cs, "-",0,0); + break; + case 'p': + if (sign < 0 && control == end) + { + if (!is_shared(stack,processes)) + FreeList(stack->processes); + stack->processes=NULL; + break; + } + if (rel && is_shared(stack, processes)) + stack->processes= ListCopy(stack->processes); + if (sign < 0) + stack->processes= ListDel(stack->processes, control, end); + else + stack->processes= ListAdd(stack->processes, control, end); + break; + case 'P': + if (sign < 0) + stack->flags &= ~PROCESS_ON; + else + stack->flags |= PROCESS_ON; + break; + case 'r': + stack->sub_level= cs->level; + break; + case 't': + if (sign < 0) + { + if (control != end) + stack->maxdepth-= atoi(control); + else + stack->maxdepth= 0; + } + else + { + if (control != end) + stack->maxdepth+= atoi(control); + else + stack->maxdepth= MAXDEPTH; + } + if (stack->maxdepth > 0) + stack->flags |= TRACE_ON; + else + stack->flags &= ~TRACE_ON; + break; + case 'T': + if (sign < 0) + stack->flags &= ~TIMESTAMP_ON; + else + stack->flags |= TIMESTAMP_ON; + break; + } + if (!*end) + break; + control=end+1; + end= DbugStrTok(control); + } + + if (stack->next == &init_settings) + { + /* + Enforce nothing is shared with the global init_settings + */ + assert((stack->functions == NULL) || (stack->functions != init_settings.functions)); + assert((stack->p_functions == NULL) || (stack->p_functions != init_settings.p_functions)); + assert((stack->keywords == NULL) || (stack->keywords != init_settings.keywords)); + assert((stack->processes == NULL) || (stack->processes != init_settings.processes)); + } + + if (stack == &init_settings) + pthread_mutex_unlock(&THR_LOCK_init_settings); + + return !rel || f_used; +} + +#define framep_trace_flag(cs, frp) (frp ? \ + frp->level & TRACE_ON : \ + (ListFlags(cs->stack->functions) & INCLUDE) ? \ + 0 : (uint)TRACE_ON) + +void FixTraceFlags_helper(CODE_STATE *cs, const char *func, + struct _db_stack_frame_ *framep) +{ + if (framep->prev) + FixTraceFlags_helper(cs, framep->func, framep->prev); + + cs->func= func; + cs->level= framep->level & ~TRACE_ON; + framep->level= cs->level | framep_trace_flag(cs, framep->prev); + /* + we don't set cs->framep correctly, even though DoTrace uses it. + It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return + values, but we ignore them here anyway + */ + switch(DoTrace(cs)) { + case ENABLE_TRACE: + framep->level|= TRACE_ON; + break; + case DISABLE_TRACE: + framep->level&= ~TRACE_ON; + break; + } +} + +#define fflags(cs) cs->stack->out_file ? ListFlags(cs->stack->functions) : TRACE_ON; + +void FixTraceFlags(uint old_fflags, CODE_STATE *cs) +{ + const char *func; + uint new_fflags, traceon, level; + struct _db_stack_frame_ *framep; + + /* + first (a.k.a. safety) check: + if we haven't started tracing yet, no call stack at all - we're safe. + */ + framep=cs->framep; + if (framep == 0) + return; + + /* + Ok, the tracing has started, call stack isn't empty. + + second check: does the new list have a SUBDIR rule ? + */ + new_fflags=fflags(cs); + if (new_fflags & SUBDIR) + goto yuck; + + /* + Ok, new list doesn't use SUBDIR. + + third check: we do NOT need to re-scan if + neither old nor new lists used SUBDIR flag and if a default behavior + (whether an unlisted function is traced) hasn't changed. + Default behavior depends on whether there're INCLUDE elements in the list. + */ + if (!(old_fflags & SUBDIR) && !((new_fflags^old_fflags) & INCLUDE)) + return; + + /* + Ok, old list may've used SUBDIR, or defaults could've changed. + + fourth check: are we inside a currently active SUBDIR rule ? + go up the call stack, if TRACE_ON flag ever changes its value - we are. + */ + for (traceon=framep->level; framep; framep=framep->prev) + if ((traceon ^ framep->level) & TRACE_ON) + goto yuck; + + /* + Ok, TRACE_ON flag doesn't change in the call stack. + + fifth check: but is the top-most value equal to a default one ? + */ + if (((traceon & TRACE_ON) != 0) == ((new_fflags & INCLUDE) == 0)) + return; + +yuck: + /* + Yuck! function list was changed, and one of the currently active rules + was possibly affected. For example, a tracing could've been enabled or + disabled for a function somewhere up the call stack. + To react correctly, we must go up the call stack all the way to + the top and re-match rules to set TRACE_ON bit correctly. + + We must traverse the stack forwards, not backwards. + That's what a recursive helper is doing. + It'll destroy two CODE_STATE fields, save them now. + */ + func= cs->func; + level= cs->level; + FixTraceFlags_helper(cs, func, cs->framep); + /* now we only need to restore CODE_STATE fields, and we're done */ + cs->func= func; + cs->level= level; +} + +/* + * FUNCTION + * + * _db_set_ set current debugger settings + * + * SYNOPSIS + * + * VOID _db_set_(control) + * char *control; + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", + * parses the control string, and sets up a current debug + * settings. Pushes a new debug settings if the current is + * set to the initial debugger settings. + * + */ + +void _db_set_(const char *control) +{ + CODE_STATE *cs; + uint old_fflags; + get_code_state_or_return; + + read_lock_stack(cs); + old_fflags=fflags(cs); + unlock_stack(cs); + + if (cs->stack == &init_settings) + PushState(cs); + + if (DbugParse(cs, control)) + { + read_lock_stack(cs); + FixTraceFlags(old_fflags, cs); + unlock_stack(cs); + } +} + +/* + * FUNCTION + * + * _db_push_ push current debugger settings and set up new one + * + * SYNOPSIS + * + * VOID _db_push_(control) + * char *control; + * + * DESCRIPTION + * + * Given pointer to a debug control string in "control", pushes + * the current debug settings, parses the control string, and sets + * up a new debug settings with DbugParse() + * + */ + +void _db_push_(const char *control) +{ + CODE_STATE *cs; + uint old_fflags; + get_code_state_or_return; + + read_lock_stack(cs); + old_fflags=fflags(cs); + unlock_stack(cs); + + PushState(cs); + + if (DbugParse(cs, control)) + { + read_lock_stack(cs); + FixTraceFlags(old_fflags, cs); + unlock_stack(cs); + } +} + + +/** + Returns TRUE if session-local settings have been set. +*/ + +int _db_is_pushed_() +{ + CODE_STATE *cs= NULL; + get_code_state_or_return FALSE; + return (cs->stack != &init_settings); +} + +/* + * FUNCTION + * + * _db_set_init_ set initial debugger settings + * + * SYNOPSIS + * + * VOID _db_set_init_(control) + * char *control; + * + * DESCRIPTION + * see _db_set_ + * + */ + +void _db_set_init_(const char *control) +{ + CODE_STATE tmp_cs; + memset(&tmp_cs, 0, sizeof(tmp_cs)); + tmp_cs.stack= &init_settings; + tmp_cs.process= db_process ? db_process : "dbug"; + DbugParse(&tmp_cs, control); +} + +/* + * FUNCTION + * + * _db_pop_ pop the debug stack + * + * DESCRIPTION + * + * Pops the debug stack, returning the debug settings to its + * condition prior to the most recent _db_push_ invocation. + * Note that the pop will fail if it would remove the last + * valid settings from the stack. This prevents user errors + * in the push/pop sequence from screwing up the debugger. + * Maybe there should be some kind of warning printed if the + * user tries to pop too many states. + * + */ + +void _db_pop_() +{ + struct settings *discard; + uint old_fflags; + CODE_STATE *cs; + + get_code_state_or_return; + + discard= cs->stack; + if (discard != &init_settings) + { + read_lock_stack(cs); + old_fflags=fflags(cs); + unlock_stack(cs); + + cs->stack= discard->next; + FreeState(cs, discard, 1); + + read_lock_stack(cs); + FixTraceFlags(old_fflags, cs); + unlock_stack(cs); + } +} + +/* + * FUNCTION + * + * _db_explain_ generates 'control' string for the current settings + * + * RETURN + * 0 - ok + * 1 - buffer too short, output truncated + * + */ + +/* helper macros */ +#define char_to_buf(C) do { \ + *buf++=(C); \ + if (buf >= end) goto overflow; \ + } while (0) +#define str_to_buf(S) do { \ + char_to_buf(','); \ + buf=strnmov(buf, (S), len+1); \ + if (buf >= end) goto overflow; \ + } while (0) +#define list_to_buf(l, f) do { \ + struct link *listp=(l); \ + while (listp) \ + { \ + if (listp->flags & (f)) \ + { \ + str_to_buf(listp->str); \ + if (listp->flags & SUBDIR) \ + char_to_buf('/'); \ + } \ + listp=listp->next_link; \ + } \ + } while (0) +#define int_to_buf(i) do { \ + char b[50]; \ + int10_to_str((i), b, 10); \ + str_to_buf(b); \ + } while (0) +#define colon_to_buf do { \ + if (buf != start) char_to_buf(':'); \ + } while(0) +#define op_int_to_buf(C, val, def) do { \ + if ((val) != (def)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + int_to_buf(val); \ + } \ + } while (0) +#define op_intf_to_buf(C, val, def, cond) do { \ + if ((cond)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + if ((val) != (def)) int_to_buf(val); \ + } \ + } while (0) +#define op_str_to_buf(C, val, cond) do { \ + if ((cond)) \ + { \ + char *s=(val); \ + colon_to_buf; \ + char_to_buf((C)); \ + if (*s) str_to_buf(s); \ + } \ + } while (0) +#define op_list_to_buf(C, val, cond) do { \ + if ((cond)) \ + { \ + int f=ListFlags(val); \ + colon_to_buf; \ + char_to_buf((C)); \ + if (f & INCLUDE) \ + list_to_buf(val, INCLUDE); \ + if (f & EXCLUDE) \ + { \ + colon_to_buf; \ + char_to_buf('-'); \ + char_to_buf((C)); \ + list_to_buf(val, EXCLUDE); \ + } \ + } \ + } while (0) +#define op_bool_to_buf(C, cond) do { \ + if ((cond)) \ + { \ + colon_to_buf; \ + char_to_buf((C)); \ + } \ + } while (0) + +int _db_explain_ (CODE_STATE *cs, char *buf, size_t len) +{ + char *start=buf, *end=buf+len-4; + + get_code_state_if_not_set_or_return *buf=0; + + read_lock_stack(cs); + + op_list_to_buf('d', cs->stack->keywords, DEBUGGING); + op_int_to_buf ('D', cs->stack->delay, 0); + op_list_to_buf('f', cs->stack->functions, cs->stack->functions); + op_bool_to_buf('F', cs->stack->flags & FILE_ON); + op_bool_to_buf('i', cs->stack->flags & PID_ON); + op_list_to_buf('g', cs->stack->p_functions, PROFILING); + op_bool_to_buf('L', cs->stack->flags & LINE_ON); + op_bool_to_buf('n', cs->stack->flags & DEPTH_ON); + op_bool_to_buf('N', cs->stack->flags & NUMBER_ON); + op_str_to_buf( + ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) | + (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')), + cs->stack->name, cs->stack->out_file != stderr); + op_list_to_buf('p', cs->stack->processes, cs->stack->processes); + op_bool_to_buf('P', cs->stack->flags & PROCESS_ON); + op_bool_to_buf('r', cs->stack->sub_level != 0); + op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING); + op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON); + + unlock_stack(cs); + + *buf= '\0'; + return 0; + +overflow: + *end++= '.'; + *end++= '.'; + *end++= '.'; + *end= '\0'; + + unlock_stack(cs); + return 1; +} + +#undef char_to_buf +#undef str_to_buf +#undef list_to_buf +#undef int_to_buf +#undef colon_to_buf +#undef op_int_to_buf +#undef op_intf_to_buf +#undef op_str_to_buf +#undef op_list_to_buf +#undef op_bool_to_buf + +/* + * FUNCTION + * + * _db_explain_init_ explain initial debugger settings + * + * DESCRIPTION + * see _db_explain_ + */ + +int _db_explain_init_(char *buf, size_t len) +{ + CODE_STATE cs; + memset(&cs, 0, sizeof(cs)); + cs.stack=&init_settings; + return _db_explain_(&cs, buf, len); +} + +/* + * FUNCTION + * + * _db_enter_ process entry point to user function + * + * SYNOPSIS + * + * VOID _db_enter_(_func_, _file_, _line_, _stack_frame_) + * char *_func_; points to current function name + * char *_file_; points to current file name + * int _line_; called from source line number + * struct _db_stack_frame_ allocated on the caller's stack + * + * DESCRIPTION + * + * Called at the beginning of each user function to tell + * the debugger that a new function has been entered. + * Note that the pointers to the previous user function + * name and previous user file name are stored on the + * caller's stack (this is why the ENTER macro must be + * the first "executable" code in a function, since it + * allocates these storage locations). The previous nesting + * level is also stored on the callers stack for internal + * self consistency checks. + * + * Also prints a trace line if tracing is enabled and + * increments the current function nesting depth. + * + * Note that this mechanism allows the debugger to know + * what the current user function is at all times, without + * maintaining an internal stack for the function names. + * + */ + +void _db_enter_(const char *_func_, const char *_file_, + uint _line_, struct _db_stack_frame_ *_stack_frame_) +{ + int save_errno; + CODE_STATE *cs; + if (!((cs=code_state()))) + { + _stack_frame_->level= 0; /* Set to avoid valgrind warnings if dbug is enabled later */ + _stack_frame_->prev= 0; + return; + } + save_errno= errno; + + read_lock_stack(cs); + + _stack_frame_->func= cs->func; + _stack_frame_->file= cs->file; + cs->func= _func_; + cs->file= _file_; + _stack_frame_->prev= cs->framep; + _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep); + cs->framep= _stack_frame_; + + switch (DoTrace(cs)) { + case ENABLE_TRACE: + cs->framep->level|= TRACE_ON; + if (!TRACING) break; + /* fall through */ + case DO_TRACE: + if (TRACING) + { + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, _line_); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file, ">%s\n", cs->func); + DbugFlush(cs); /* This does a unlock */ + } + break; + case DISABLE_TRACE: + cs->framep->level&= ~TRACE_ON; + /* fall through */ + case DONT_TRACE: + break; + } + errno=save_errno; + + unlock_stack(cs); +} + +/* + * FUNCTION + * + * _db_return_ process exit from user function + * + * SYNOPSIS + * + * VOID _db_return_(_line_, _stack_frame_) + * int _line_; current source line number + * struct _db_stack_frame_ allocated on the caller's stack + * + * DESCRIPTION + * + * Called just before user function executes an explicit or implicit + * return. Prints a trace line if trace is enabled, decrements + * the current nesting level, and restores the current function and + * file names from the defunct function's stack. + * + */ + +void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_) +{ + int save_errno=errno; + uint _slevel_= _stack_frame_->level & ~TRACE_ON; + CODE_STATE *cs; + get_code_state_or_return; + + if (cs->framep != _stack_frame_) + { + char buf[512]; + my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func); + DbugExit(buf); + } + + read_lock_stack(cs); + + if (DoTrace(cs) & DO_TRACE) + { + if (TRACING) + { + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, _line_); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file, "<%s %u\n", cs->func, _line_); + DbugFlush(cs); + } + } + /* + Check to not set level < 0. This can happen if DBUG was disabled when + function was entered and enabled in function. + */ + cs->level= _slevel_ != 0 ? _slevel_ - 1 : 0; + cs->func= _stack_frame_->func; + cs->file= _stack_frame_->file; + if (cs->framep != NULL) + cs->framep= cs->framep->prev; + errno=save_errno; + + unlock_stack(cs); +} + + +/* + * FUNCTION + * + * _db_pargs_ log arguments for subsequent use by _db_doprnt_() + * + * SYNOPSIS + * + * VOID _db_pargs_(_line_, keyword) + * int _line_; + * char *keyword; + * + * DESCRIPTION + * + * The new universal printing macro DBUG_PRINT, which replaces + * all forms of the DBUG_N macros, needs two calls to runtime + * support routines. The first, this function, remembers arguments + * that are used by the subsequent call to _db_doprnt_(). + * + */ + +void _db_pargs_(uint _line_, const char *keyword) +{ + CODE_STATE *cs; + get_code_state_or_return; + cs->u_line= _line_; + cs->u_keyword= keyword; +} + + +/* + * FUNCTION + * + * _db_doprnt_ handle print of debug lines + * + * SYNOPSIS + * + * VOID _db_doprnt_(format, va_alist) + * char *format; + * va_dcl; + * + * DESCRIPTION + * + * When invoked via one of the DBUG macros, tests the current keyword + * set by calling _db_pargs_() to see if that macro has been selected + * for processing via the debugger control string, and if so, handles + * printing of the arguments via the format string. The line number + * of the DBUG macro in the source is found in u_line. + * + * Note that the format string SHOULD NOT include a terminating + * newline, this is supplied automatically. + * + */ + +#include + +void _db_doprnt_(const char *format,...) +{ + va_list args; + CODE_STATE *cs; + get_code_state_or_return; + + /* Dirty read, for DBUG_PRINT() performance. */ + if (! DEBUGGING) + return; + + va_start(args,format); + read_lock_stack(cs); + + if (_db_keyword_(cs, cs->u_keyword, 0)) + { + int save_errno=errno; + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, cs->u_line); + if (TRACING) + Indent(cs, cs->level + 1); + else + (void) fprintf(cs->stack->out_file, "%s: ", cs->func); + (void) fprintf(cs->stack->out_file, "%s: ", cs->u_keyword); + (void) vfprintf(cs->stack->out_file, format, args); + DbugFlush(cs); + errno=save_errno; + } + unlock_stack(cs); + va_end(args); +} + +/* + * FUNCTION + * + * _db_dump_ dump a string in hex + * + * SYNOPSIS + * + * void _db_dump_(_line_,keyword,memory,length) + * int _line_; current source line number + * char *keyword; + * char *memory; Memory to print + * int length; Bytes to print + * + * DESCRIPTION + * Dump N characters in a binary array. + * Is used to examine corrupted memory or arrays. + */ + +void _db_dump_(uint _line_, const char *keyword, + const unsigned char *memory, size_t length) +{ + int pos; + CODE_STATE *cs; + get_code_state_or_return; + + /* Dirty read, for DBUG_DUMP() performance. */ + if (! DEBUGGING) + return; + + read_lock_stack(cs); + + if (_db_keyword_(cs, keyword, 0)) + { + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, _line_); + if (TRACING) + { + Indent(cs, cs->level + 1); + pos= MIN(MAX(cs->level-cs->stack->sub_level,0)*INDENT,80); + } + else + { + fprintf(cs->stack->out_file, "%s: ", cs->func); + } + (void) fprintf(cs->stack->out_file, "%s: Memory: 0x%lx Bytes: (%ld)\n", + keyword, (ulong) memory, (long) length); + + pos=0; + while (length-- > 0) + { + uint tmp= *((unsigned char*) memory++); + if ((pos+=3) >= 80) + { + fputc('\n',cs->stack->out_file); + pos=3; + } + fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file); + fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file); + fputc(' ',cs->stack->out_file); + } + (void) fputc('\n',cs->stack->out_file); + DbugFlush(cs); + } + + unlock_stack(cs); +} + + +/* + * FUNCTION + * + * ListAddDel modify the list according to debug control string + * + * DESCRIPTION + * + * Given pointer to a comma separated list of strings in "cltp", + * parses the list, and modifies "listp", returning a pointer + * to the new list. + * + * The mode of operation is defined by "todo" parameter. + * + * If it is INCLUDE, elements (strings from "cltp") are added to the + * list, they will have INCLUDE flag set. If the list already contains + * the string in question, new element is not added, but a flag of + * the existing element is adjusted (INCLUDE bit is set, EXCLUDE bit + * is removed). + * + * If it is EXCLUDE, elements are added to the list with the EXCLUDE + * flag set. If the list already contains the string in question, + * it is removed, new element is not added. + */ + +static struct link *ListAddDel(struct link *head, const char *ctlp, + const char *end, int todo) +{ + const char *start; + struct link **cur; + size_t len; + int subdir; + + ctlp--; +next: + while (++ctlp < end) + { + start= ctlp; + subdir=0; + while (ctlp < end && *ctlp != ',') + ctlp++; + len=ctlp-start; + if (start[len-1] == '/') + { + len--; + subdir=SUBDIR; + } + if (len == 0) continue; + for (cur=&head; *cur; cur=&((*cur)->next_link)) + { + if (!strncmp((*cur)->str, start, len)) + { + if ((*cur)->flags & todo) /* same action ? */ + (*cur)->flags|= subdir; /* just merge the SUBDIR flag */ + else if (todo == EXCLUDE) + { + struct link *delme=*cur; + *cur=(*cur)->next_link; + free((void*) delme); + } + else + { + (*cur)->flags&=~(EXCLUDE | SUBDIR); + (*cur)->flags|=INCLUDE | subdir; + } + goto next; + } + } + *cur= (struct link *) DbugMalloc(sizeof(struct link)+len); + memcpy((*cur)->str, start, len); + (*cur)->str[len]=0; + (*cur)->flags=todo | subdir; + (*cur)->next_link=0; + } + return head; +} + +/* + * FUNCTION + * + * ListCopy make a copy of the list + * + * SYNOPSIS + * + * static struct link *ListCopy(orig) + * struct link *orig; + * + * DESCRIPTION + * + * Given pointer to list, which contains a copy of every element from + * the original list. + * + * the orig pointer can be NULL + * + * Note that since each link is added at the head of the list, + * the final list will be in "reverse order", which is not + * significant for our usage here. + * + */ + +static struct link *ListCopy(struct link *orig) +{ + struct link *new_malloc; + struct link *head; + size_t len; + + head= NULL; + while (orig != NULL) + { + len= strlen(orig->str); + new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len); + memcpy(new_malloc->str, orig->str, len); + new_malloc->str[len]= 0; + new_malloc->flags=orig->flags; + new_malloc->next_link= head; + head= new_malloc; + orig= orig->next_link; + } + return head; +} + +/* + * FUNCTION + * + * InList test a given string for member of a given list + * + * DESCRIPTION + * + * Tests the string pointed to by "cp" to determine if it is in + * the list pointed to by "linkp". Linkp points to the first + * link in the list. If linkp is NULL or contains only EXCLUDE + * elements then the string is treated as if it is in the list. + * This may seem rather strange at first but leads to the desired + * operation if no list is given. The net effect is that all + * strings will be accepted when there is no list, and when there + * is a list, only those strings in the list will be accepted. + * + * RETURN + * combination of SUBDIR, INCLUDE, EXCLUDE, MATCHED flags + * + */ + +static int InList(struct link *linkp, const char *cp) +{ + int result; + + for (result=MATCHED; linkp != NULL; linkp= linkp->next_link) + { + if (!fnmatch(linkp->str, cp, 0)) + return linkp->flags; + if (!(linkp->flags & EXCLUDE)) + result=NOT_MATCHED; + if (linkp->flags & SUBDIR) + result|=SUBDIR; + } + return result; +} + +/* + * FUNCTION + * + * ListFlags returns aggregated list flags (ORed over all elements) + * + */ + +static uint ListFlags(struct link *linkp) +{ + uint f; + for (f=0; linkp != NULL; linkp= linkp->next_link) + f|= linkp->flags; + return f; +} + +/* + * FUNCTION + * + * PushState push current settings onto stack and set up new one + * + * SYNOPSIS + * + * static VOID PushState() + * + * DESCRIPTION + * + * Pushes the current settings on the settings stack, and creates + * a new settings. The new settings is NOT initialized + * + * The settings stack is a linked list of settings, with the new + * settings added at the head. This allows the stack to grow + * to the limits of memory if necessary. + * + */ + +static void PushState(CODE_STATE *cs) +{ + struct settings *new_malloc; + + new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings)); + memset(new_malloc, 0, sizeof(struct settings)); + new_malloc->next= cs->stack; + cs->stack= new_malloc; +} + +/* + * FUNCTION + * + * FreeState Free memory associated with a struct state. + * + * SYNOPSIS + * + * static void FreeState (state) + * struct state *state; + * int free_state; + * + * DESCRIPTION + * + * Deallocates the memory allocated for various information in a + * state. If free_state is set, also free 'state' + * + */ +static void FreeState(CODE_STATE *cs, struct settings *state, int free_state) +{ + if (!is_shared(state, keywords)) + FreeList(state->keywords); + if (!is_shared(state, functions)) + FreeList(state->functions); + if (!is_shared(state, processes)) + FreeList(state->processes); + if (!is_shared(state, p_functions)) + FreeList(state->p_functions); + + if (!is_shared(state, out_file)) + DBUGCloseFile(cs, state->out_file); + else + (void) fflush(state->out_file); + + if (!is_shared(state, prof_file)) + DBUGCloseFile(cs, state->prof_file); + else + (void) fflush(state->prof_file); + + if (free_state) + free((void*) state); +} + + +/* + * FUNCTION + * + * _db_end_ End debugging, freeing state stack memory. + * + * SYNOPSIS + * + * static VOID _db_end_ () + * + * DESCRIPTION + * + * Ends debugging, de-allocating the memory allocated to the + * state stack. + * + * To be called at the very end of the program. + * + */ +void _db_end_() +{ + struct settings *discard; + static struct settings tmp; + CODE_STATE *cs; + /* + Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was + called after dbug was initialized + */ + _dbug_on_= 1; + get_code_state_or_return; + + /* + The caller may have missed a DBUG_UNLOCK_FILE, + we are breaking this lock to enforce DBUG_END can proceed. + */ + if (cs->locked) + { + fprintf(stderr, ERR_MISSING_UNLOCK, "(unknown)"); + cs->locked= 0; + pthread_mutex_unlock(&THR_LOCK_dbug); + } + + while ((discard= cs->stack)) + { + if (discard == &init_settings) + break; + cs->stack= discard->next; + FreeState(cs, discard, 1); + } + + pthread_mutex_lock(&THR_LOCK_init_settings); + tmp= init_settings; + init_settings.flags= OPEN_APPEND; + init_settings.out_file= stderr; + init_settings.prof_file= stderr; + init_settings.maxdepth= 0; + init_settings.delay= 0; + init_settings.sub_level= 0; + init_settings.functions= 0; + init_settings.p_functions= 0; + init_settings.keywords= 0; + init_settings.processes= 0; + pthread_mutex_unlock(&THR_LOCK_init_settings); + FreeState(cs, &tmp, 0); +} + + +/* + * FUNCTION + * + * DoTrace check to see if tracing is current enabled + * + * DESCRIPTION + * + * Checks to see if dbug in this function is enabled based on + * whether the maximum trace depth has been reached, the current + * function is selected, and the current process is selected. + * + */ + +static int DoTrace(CODE_STATE *cs) +{ + if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) && + InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE)) + switch(InList(cs->stack->functions, cs->func)) { + case INCLUDE|SUBDIR: return ENABLE_TRACE; + case INCLUDE: return DO_TRACE; + case MATCHED|SUBDIR: + case NOT_MATCHED|SUBDIR: + case MATCHED: return framep_trace_flag(cs, cs->framep) ? + DO_TRACE : DONT_TRACE; + case EXCLUDE: + case NOT_MATCHED: return DONT_TRACE; + case EXCLUDE|SUBDIR: return DISABLE_TRACE; + } + return DONT_TRACE; +} + +FILE *_db_fp_(void) +{ + CODE_STATE *cs; + get_code_state_or_return NULL; + return cs->stack->out_file; +} + +/* + * FUNCTION + * + * _db_keyword_ test keyword for member of keyword list + * + * DESCRIPTION + * + * Test a keyword to determine if it is in the currently active + * keyword list. If strict=0, a keyword is accepted + * if the list is null, otherwise it must match one of the list + * members. When debugging is not on, no keywords are accepted. + * After the maximum trace level is exceeded, no keywords are + * accepted (this behavior subject to change). Additionally, + * the current function and process must be accepted based on + * their respective lists. + * + * Returns TRUE if keyword accepted, FALSE otherwise. + * + */ + +BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) +{ + BOOLEAN result; + get_code_state_if_not_set_or_return FALSE; + + /* Dirty read, for DBUG_EXECUTE(), DBUG_EXECUTE_IF() ... performance. */ + if (! DEBUGGING) + return FALSE; + + read_lock_stack(cs); + + strict=strict ? INCLUDE : INCLUDE|MATCHED; + result= DoTrace(cs) & DO_TRACE && + InList(cs->stack->keywords, keyword) & strict; + + unlock_stack(cs); + + return result; +} + +/* + * FUNCTION + * + * Indent indent a line to the given indentation level + * + * SYNOPSIS + * + * static VOID Indent(indent) + * int indent; + * + * DESCRIPTION + * + * Indent a line to the given level. Note that this is + * a simple minded but portable implementation. + * There are better ways. + * + * Also, the indent must be scaled by the compile time option + * of character positions per nesting level. + * + */ + +static void Indent(CODE_STATE *cs, int indent) +{ + int count; + + indent= MAX(indent-1-cs->stack->sub_level,0)*INDENT; + for (count= 0; count < indent ; count++) + { + if ((count % INDENT) == 0) + fputc('|',cs->stack->out_file); + else + fputc(' ',cs->stack->out_file); + } +} + + +/* + * FUNCTION + * + * FreeList free all memory associated with a linked list + * + * SYNOPSIS + * + * static VOID FreeList(linkp) + * struct link *linkp; + * + * DESCRIPTION + * + * Given pointer to the head of a linked list, frees all + * memory held by the list and the members of the list. + * + */ + +static void FreeList(struct link *linkp) +{ + struct link *old; + + while (linkp != NULL) + { + old= linkp; + linkp= linkp->next_link; + free((void*) old); + } +} + + +/* + * FUNCTION + * + * DoPrefix print debugger line prefix prior to indentation + * + * SYNOPSIS + * + * static VOID DoPrefix(_line_) + * int _line_; + * + * DESCRIPTION + * + * Print prefix common to all debugger output lines, prior to + * doing indentation if necessary. Print such information as + * current process name, current source file name and line number, + * and current function nesting depth. + * + */ + +static void DoPrefix(CODE_STATE *cs, uint _line_) +{ + cs->lineno++; + if (cs->stack->flags & PID_ON) + { + (void) fprintf(cs->stack->out_file, "%-7s: ", my_thread_name()); + } + if (cs->stack->flags & NUMBER_ON) + (void) fprintf(cs->stack->out_file, "%5d: ", cs->lineno); + if (cs->stack->flags & TIMESTAMP_ON) + { +#ifdef _WIN32 + /* FIXME This doesn't give microseconds as in Unix case, and the resolution is + in system ticks, 10 ms intervals. See my_getsystime.c for high res */ + SYSTEMTIME loc_t; + GetLocalTime(&loc_t); + (void) fprintf (cs->stack->out_file, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +#else + struct timeval tv; + struct tm *tm_p; + if (gettimeofday(&tv, NULL) != -1) + { + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) + { + (void) fprintf (cs->stack->out_file, + /* "%04d-%02d-%02d " */ + "%02d:%02d:%02d.%06d ", + /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ + tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, + (int) (tv.tv_usec)); + } + } +#endif + } + if (cs->stack->flags & PROCESS_ON) + (void) fprintf(cs->stack->out_file, "%s: ", cs->process); + if (cs->stack->flags & FILE_ON) + (void) fprintf(cs->stack->out_file, "%14s: ", BaseName(cs->file)); + if (cs->stack->flags & LINE_ON) + (void) fprintf(cs->stack->out_file, "%5d: ", _line_); + if (cs->stack->flags & DEPTH_ON) + (void) fprintf(cs->stack->out_file, "%4d: ", cs->level); +} + + +/* + * FUNCTION + * + * DBUGOpenFile open new output stream for debugger output + * + * SYNOPSIS + * + * static VOID DBUGOpenFile(name) + * char *name; + * + * DESCRIPTION + * + * Given name of a new file (or "-" for stdout) opens the file + * and sets the output stream to the new file. + * + */ + +static void DBUGOpenFile(CODE_STATE *cs, + const char *name,const char *end,int append) +{ + FILE *fp; + + if (name != NULL) + { + if (end) + { + size_t len=end-name; + memcpy(cs->stack->name, name, len); + cs->stack->name[len]=0; + } + else + strmov(cs->stack->name,name); + name=cs->stack->name; + if (strcmp(name, "-") == 0) + { + cs->stack->out_file= stdout; + cs->stack->flags |= FLUSH_ON_WRITE; + cs->stack->name[0]=0; + } + else + { + if (!Writable(name)) + { + (void) fprintf(stderr, ERR_OPEN, cs->process, name); + perror(""); + fflush(stderr); + } + else + { +#ifdef _WIN32 + if (fopen_s(&fp, name, append ? "a+" : "w")) +#else + if (!(fp= fopen(name, append ? "a+" : "w"))) +#endif + { + (void) fprintf(stderr, ERR_OPEN, cs->process, name); + perror(""); + fflush(stderr); + } + else + { + cs->stack->out_file= fp; + } + } + } + } +} + +/* + * FUNCTION + * + * DBUGCloseFile close the debug output stream + * + * SYNOPSIS + * + * static VOID DBUGCloseFile(fp) + * FILE *fp; + * + * DESCRIPTION + * + * Closes the debug output stream unless it is standard output + * or standard error. + * + */ + +static void DBUGCloseFile(CODE_STATE *cs, FILE *fp) +{ + if (fp != NULL && fp != stderr && fp != stdout && fclose(fp) == EOF) + { + pthread_mutex_lock(&THR_LOCK_dbug); + (void) fprintf(cs->stack->out_file, ERR_CLOSE, cs->process); + perror(""); + DbugFlush(cs); + } +} + + +/* + * FUNCTION + * + * DbugExit print error message and exit + * + * SYNOPSIS + * + * static VOID DbugExit(why) + * char *why; + * + * DESCRIPTION + * + * Prints error message using current process name, the reason for + * aborting (typically out of memory), and exits with status 1. + * This should probably be changed to use a status code + * defined in the user's debugger include file. + * + */ + +static void DbugExit(const char *why) +{ + CODE_STATE *cs=code_state(); + (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why); + (void) fflush(stderr); + DBUG_ABORT(); +} + + +/* + * FUNCTION + * + * DbugMalloc allocate memory for debugger runtime support + * + * SYNOPSIS + * + * static long *DbugMalloc(size) + * int size; + * + * DESCRIPTION + * + * Allocate more memory for debugger runtime support functions. + * Failure to to allocate the requested number of bytes is + * immediately fatal to the current process. This may be + * rather unfriendly behavior. It might be better to simply + * print a warning message, freeze the current debugger cs, + * and continue execution. + * + */ + +static char *DbugMalloc(size_t size) +{ + char *new_malloc; + + if (!(new_malloc= (char*) malloc(size))) + DbugExit("out of memory"); + return new_malloc; +} + + +/* + * strtok lookalike - splits on ':', magically handles ::, :\ and :/ + */ + +static const char *DbugStrTok(const char *s) +{ + while (s[0] && (s[0] != ':' || + (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++)))) + s++; + return s; +} + + +/* + * FUNCTION + * + * BaseName strip leading pathname components from name + * + * SYNOPSIS + * + * static char *BaseName(pathname) + * char *pathname; + * + * DESCRIPTION + * + * Given pointer to a complete pathname, locates the base file + * name at the end of the pathname and returns a pointer to + * it. + * + */ + +static const char *BaseName(const char *pathname) +{ + const char *base; + + base= strrchr(pathname, FN_LIBCHAR); + if (base++ == NullS) + base= pathname; + return base; +} + + +/* + * FUNCTION + * + * Writable test to see if a pathname is writable/creatable + * + * SYNOPSIS + * + * static BOOLEAN Writable(pathname) + * char *pathname; + * + * DESCRIPTION + * + * Because the debugger might be linked in with a program that + * runs with the set-uid-bit (suid) set, we have to be careful + * about opening a user named file for debug output. This consists + * of checking the file for write access with the real user id, + * or checking the directory where the file will be created. + * + * Returns TRUE if the user would normally be allowed write or + * create access to the named file. Returns FALSE otherwise. + * + */ + + +#ifndef Writable + +static BOOLEAN Writable(const char *pathname) +{ + BOOLEAN granted; + char *lastslash; + + granted= FALSE; + if (EXISTS(pathname)) + { + if (WRITABLE(pathname)) + granted= TRUE; + } + else + { + lastslash= strrchr(pathname, '/'); + if (lastslash != NULL) + *lastslash= '\0'; + else + pathname= "."; + if (WRITABLE(pathname)) + granted= TRUE; + if (lastslash != NULL) + *lastslash= '/'; + } + return granted; +} +#endif + + +/* + * FUNCTION + * + * _db_setjmp_ save debugger environment + * + * SYNOPSIS + * + * VOID _db_setjmp_() + * + * DESCRIPTION + * + * Invoked as part of the user's DBUG_SETJMP macro to save + * the debugger environment in parallel with saving the user's + * environment. + * + */ + +#ifdef HAVE_LONGJMP + +EXPORT void _db_setjmp_() +{ + CODE_STATE *cs; + get_code_state_or_return; + + cs->jmplevel= cs->level; + cs->jmpfunc= cs->func; + cs->jmpfile= cs->file; +} + +/* + * FUNCTION + * + * _db_longjmp_ restore previously saved debugger environment + * + * SYNOPSIS + * + * VOID _db_longjmp_() + * + * DESCRIPTION + * + * Invoked as part of the user's DBUG_LONGJMP macro to restore + * the debugger environment in parallel with restoring the user's + * previously saved environment. + * + */ + +EXPORT void _db_longjmp_() +{ + CODE_STATE *cs; + get_code_state_or_return; + + cs->level= cs->jmplevel; + if (cs->jmpfunc) + cs->func= cs->jmpfunc; + if (cs->jmpfile) + cs->file= cs->jmpfile; +} +#endif + +/* + * FUNCTION + * + * perror perror simulation for systems that don't have it + * + * SYNOPSIS + * + * static VOID perror(s) + * char *s; + * + * DESCRIPTION + * + * Perror produces a message on the standard error stream which + * provides more information about the library or system error + * just encountered. The argument string s is printed, followed + * by a ':', a blank, and then a message and a newline. + * + * An undocumented feature of the unix perror is that if the string + * 's' is a null string (NOT a NULL pointer!), then the ':' and + * blank are not printed. + * + * This version just complains about an "unknown system error". + * + */ + +#ifndef HAVE_PERROR +static void perror(s) +char *s; +{ + if (s && *s != '\0') + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "\n"); +} +#endif /* HAVE_PERROR */ + + + /* flush dbug-stream, free mutex lock & wait delay */ + /* This is because some systems (MSDOS!!) dosn't flush fileheader */ + /* and dbug-file isn't readable after a system crash !! */ + +static void DbugFlush(CODE_STATE *cs) +{ + if (cs->stack->flags & FLUSH_ON_WRITE) + { + (void) fflush(cs->stack->out_file); + if (cs->stack->delay) + (void) Delay(cs->stack->delay); + } + if (!cs->locked) + pthread_mutex_unlock(&THR_LOCK_dbug); +} /* DbugFlush */ + + +/* For debugging */ + +void _db_flush_() +{ + CODE_STATE *cs= NULL; + get_code_state_or_return; + (void) fflush(cs->stack->out_file); +} + + +#ifndef _WIN32 + +#ifdef HAVE_GCOV +extern void __gcov_flush(); +#endif + +void _db_flush_gcov_() +{ +#ifdef HAVE_GCOV + // Gcov will assert() if we try to flush in parallel. + pthread_mutex_lock(&THR_LOCK_gcov); + __gcov_flush(); + pthread_mutex_unlock(&THR_LOCK_gcov); +#endif +} + +void _db_suicide_() +{ + int retval; + sigset_t new_mask; + sigfillset(&new_mask); + +#ifdef HAVE_GCOV + fprintf(stderr, "Flushing gcov data\n"); + fflush(stderr); + _db_flush_gcov_(); +#endif + + fprintf(stderr, "SIGKILL myself\n"); + fflush(stderr); + + retval= kill(getpid(), SIGKILL); + assert(retval == 0); + retval= sigsuspend(&new_mask); + fprintf(stderr, "sigsuspend returned %d errno %d \n", retval, errno); + assert(FALSE); /* With full signal mask, we should never return here. */ +} +#endif /* ! _WIN32 */ + + +void _db_lock_file_() +{ + CODE_STATE *cs; + get_code_state_or_return; + pthread_mutex_lock(&THR_LOCK_dbug); + cs->locked=1; +} + +void _db_unlock_file_() +{ + CODE_STATE *cs; + get_code_state_or_return; + cs->locked=0; + pthread_mutex_unlock(&THR_LOCK_dbug); +} + +const char* _db_get_func_(void) +{ + CODE_STATE *cs; + get_code_state_or_return NULL; + return cs->func; +} + + +#else + +/* + * Dummy function, workaround for MySQL bug#14420 related + * build failure on a platform where linking with an empty + * archive fails. + * + * This block can be removed as soon as a fix for bug#14420 + * is implemented. + */ +int i_am_a_dummy_function() { + return 0; +} + +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/default.c b/mariadb-connector-c-v_2.3.7/libmariadb/default.c new file mode 100644 index 0000000..928c9aa --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/default.c @@ -0,0 +1,433 @@ +/* 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 */ + +/**************************************************************************** +** Add all options from files named "group".cnf from the default_directories +** before the command line arguments. +** On Windows defaults will also search in the Windows directory for a file +** called 'group'.ini +** As long as the program uses the last argument for conflicting +** options one only have to add a call to "load_defaults" to enable +** use of default values. +** pre- and end 'blank space' are removed from options and values. The +** following escape sequences are recognized in values: \b \t \n \r \\ +** +** The following arguments are handled automaticly; If used, they must be +** first argument on the command line! +** --no-defaults ; no options are read. +** --defaults-file=full-path-to-default-file ; Only this file will be read. +** --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ +** --print-defaults ; Print the modified command line and exit +****************************************************************************/ + +#undef SAFEMALLOC /* safe_malloc is not yet initailized */ + +#include "mysys_priv.h" +#include "m_string.h" +#include +#include "m_ctype.h" +#include + +char *defaults_extra_file=0; + +/* Which directories are searched for options (and in which order) */ + +const char *default_directories[]= { +#ifdef _WIN32 +"C:/", +#else +"/etc/", +#endif +#ifdef DATADIR +DATADIR, +#endif +"", /* Place for defaults_extra_dir */ +#ifndef _WIN32 +"~/", +#endif +NullS, +}; + +#define default_ext ".cnf" /* extension for config file */ +#ifdef _WIN32 +#include +#define windows_ext ".ini" +#endif + +static my_bool search_default_file(DYNAMIC_ARRAY *args,MEM_ROOT *alloc, + const char *dir, const char *config_file, + const char *ext, TYPELIB *group); + + +void load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv) +{ + DYNAMIC_ARRAY args; + const char **dirs, *forced_default_file; + TYPELIB group; + my_bool found_print_defaults=0; + uint args_used=0; + MEM_ROOT alloc; + char *ptr,**res; + DBUG_ENTER("load_defaults"); + + init_alloc_root(&alloc,128,0); + if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults")) + { + /* remove the --no-defaults argument and return only the other arguments */ + uint i; + if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ + (*argc + 1)*sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + res[0]= **argv; /* Copy program name */ + for (i=2 ; i < (uint) *argc ; i++) + res[i-1]=argv[0][i]; + (*argc)--; + *argv=res; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + DBUG_VOID_RETURN; + } + + /* Check if we want to force the use a specific default file */ + forced_default_file=0; + if (*argc >= 2) + { + if (is_prefix(argv[0][1],"--defaults-file=")) + { + forced_default_file=strchr(argv[0][1],'=')+1; + args_used++; + } + else if (is_prefix(argv[0][1],"--defaults-extra-file=")) + { + defaults_extra_file=strchr(argv[0][1],'=')+1; + args_used++; + } + } + + group.count=0; + group.name= "defaults"; + group.type_names= groups; + for (; *groups ; groups++) + group.count++; + + if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32)) + goto err; + if (forced_default_file) + { + if (search_default_file(&args, &alloc, "", forced_default_file, "", + &group)) + goto err; + } + else if (dirname_length(conf_file)) + { + if (search_default_file(&args, &alloc, NullS, conf_file, default_ext, + &group)) + goto err; + } + else + { +#ifdef _WIN32 + char system_dir[FN_REFLEN]; + GetWindowsDirectory(system_dir,sizeof(system_dir)); + if (search_default_file(&args, &alloc, system_dir, conf_file, windows_ext, + &group)) + goto err; +#endif +#if defined(__EMX__) || defined(OS2) + if (getenv("ETC") && + search_default_file(&args, &alloc, getenv("ETC"), conf_file, + default_ext, &group)) + goto err; +#endif + for (dirs=default_directories ; *dirs; dirs++) + { + int error=0; + if (**dirs) + error=search_default_file(&args, &alloc, *dirs, conf_file, + default_ext, &group); + else if (defaults_extra_file) + error=search_default_file(&args, &alloc, NullS, defaults_extra_file, + default_ext, &group); + if (error) + goto err; + } + } + if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+ + (args.elements + *argc +1) *sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + + /* copy name + found arguments + command line arguments to new array */ + res[0]=argv[0][0]; + memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*)); + /* Skipp --defaults-file and --defaults-extra-file */ + (*argc)-= args_used; + (*argv)+= args_used; + + /* Check if we wan't to see the new argument list */ + if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) + { + found_print_defaults=1; + --*argc; ++*argv; /* skipp argument */ + } + + memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1), + (*argc-1)*sizeof(char*)); + res[args.elements+ *argc]=0; /* last null */ + + (*argc)+=args.elements; + *argv= (char**) res; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + delete_dynamic(&args); + if (found_print_defaults) + { + int i; + printf("%s would have been started with the following arguments:\n", + **argv); + for (i=1 ; i < *argc ; i++) + printf("%s ", (*argv)[i]); + puts(""); + exit(1); + } + DBUG_VOID_RETURN; + + err: + fprintf(stderr,"Program aborted\n"); + exit(1); +} + + +void free_defaults(char **argv) +{ + MEM_ROOT ptr; + memcpy_fixed((char*) &ptr,(char *) argv - sizeof(ptr), sizeof(ptr)); + free_root(&ptr,MYF(0)); +} + + +static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, + const char *dir, const char *config_file, + const char *ext, TYPELIB *group) +{ + char name[FN_REFLEN+10],buff[4096],*ptr,*end,*value,*tmp; + FILE *fp; + uint line=0; + my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0; + + if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) + return 0; /* Ignore wrong paths */ + if (dir) + { + strmov(name,dir); + convert_dirname(name); + if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ + strcat(name,"."); + strxmov(strend(name),config_file,ext,NullS); + } + else + { + strmov(name,config_file); + } + fn_format(name,name,"","",4); +#if !defined(_WIN32) && !defined(OS2) + { + MY_STAT stat_info; + if (!my_stat(name,&stat_info,MYF(0))) + return 0; + if (stat_info.st_mode & S_IWOTH) /* ignore world-writeable files */ + { + fprintf(stderr, "warning: World-writeable config file %s is ignored\n", + name); + return 0; + } + } +#endif + if (!(fp = my_fopen(fn_format(name,name,"","",4),O_RDONLY,MYF(0)))) + return 0; /* Ignore wrong files */ + + while (fgets(buff,sizeof(buff)-1,fp)) + { + line++; + /* Ignore comment and empty lines */ + for (ptr=buff ; isspace(*ptr) ; ptr++ ); + if (!is_escaped && (*ptr == '\"' || *ptr== '\'')) + { + is_quoted= !is_quoted; + continue; + } + if (*ptr == '#' || *ptr == ';' || !*ptr) + continue; + is_escaped= (*ptr == '\\'); + if (*ptr == '[') /* Group name */ + { + found_group=1; + if (!(end=(char *) strchr(++ptr,']'))) + { + fprintf(stderr, + "error: Wrong group definition in config file: %s at line %d\n", + name,line); + goto err; + } + for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ + end[0]=0; + read_values=find_type(ptr,group,3) > 0; + continue; + } + if (!found_group) + { + fprintf(stderr, + "error: Found option without preceding group in config file: %s at line: %d\n", + name,line); + goto err; + } + if (!read_values) + continue; + if (!(end=value=strchr(ptr,'='))) + end=strend(ptr); /* Option without argument */ + for ( ; isspace(end[-1]) ; end--) ; + if (!value) + { + if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3))) + goto err; + strmake(strmov(tmp,"--"),ptr,(uint) (end-ptr)); + if (insert_dynamic(args,(gptr) &tmp)) + goto err; + } + else + { + /* Remove pre- and end space */ + char *value_end; + for (value++ ; isspace(*value); value++) ; + value_end=strend(value); + 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; + if (!(tmp=alloc_root(alloc,(uint) (end-ptr)+3 + + (uint) (value_end-value)+1))) + goto err; + if (insert_dynamic(args,(gptr) &tmp)) + goto err; + ptr=strnmov(strmov(tmp,"--"),ptr,(uint) (end-ptr)); + *ptr++= '='; + 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; + } + } + my_fclose(fp,MYF(0)); + return(0); + + err: + my_fclose(fp,MYF(0)); + return 1; +} + + +void print_defaults(const char *conf_file, const char **groups) +{ +#ifdef _WIN32 + bool have_ext=fn_ext(conf_file)[0] != 0; +#endif + char name[FN_REFLEN]; + const char **dirs; + puts("\nDefault options are read from the following files in the given order:"); + + if (dirname_length(conf_file)) + fputs(conf_file,stdout); + else + { +#ifdef _WIN32 + GetWindowsDirectory(name,sizeof(name)); + printf("%s\\%s%s ",name,conf_file,have_ext ? "" : windows_ext); +#endif +#if defined(__EMX__) || defined(OS2) + if (getenv("ETC")) + printf("%s\\%s%s ", getenv("ETC"), conf_file, default_ext); +#endif + for (dirs=default_directories ; *dirs; dirs++) + { + if (**dirs) + strmov(name,*dirs); + else if (defaults_extra_file) + strmov(name,defaults_extra_file); + else + continue; + convert_dirname(name); + if (name[0] == FN_HOMELIB) /* Add . to filenames in home */ + strcat(name,"."); + strxmov(strend(name),conf_file,default_ext," ",NullS); + fputs(name,stdout); + } + puts(""); + } + fputs("The following groups are read:",stdout); + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + } + puts("\nThe following options may be given as the first argument:\n\ +--print-defaults Print the program argument list and exit\n\ +--no-defaults Don't read default options from any options file\n\ +--defaults-file=# Only read default options from the given file #\n\ +--defaults-extra-file=# Read this file after the global files are read"); +} + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/errmsg.c b/mariadb-connector-c-v_2.3.7/libmariadb/errmsg.c new file mode 100644 index 0000000..c9e72ba --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/errmsg.c @@ -0,0 +1,152 @@ +/* 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 MySQL clients */ +/* error messages for the demon is in share/language/errmsg.sys */ + +#include +#include +#include "errmsg.h" +#include + +#ifdef GERMAN +const char *client_errors[]= +{ + "Unbekannter MySQL Fehler", + "Kann UNIX-Socket nicht anlegen (%d)", + "Keine Verbindung zu lokalem MySQL Server, socket: '%-.64s' (%d)", + "Keine Verbindung zu MySQL Server auf %-.64s (%d)", + "Kann TCP/IP-Socket nicht anlegen (%d)", + "Unbekannter MySQL Server Host (%-.64s) (%d)", + "MySQL Server nicht vorhanden", + "Protokolle ungleich. Server Version = % d Client Version = %d", + "MySQL client got out of memory", + "Wrong host info", + "Localhost via UNIX socket", + "%-.64s via TCP/IP", + "Error in server handshake", + "Lost connection to MySQL server during query", + "Commands out of sync; you can't run this command now", + "Verbindung ueber Named Pipe; Host: %-.64s", + "Kann nicht auf Named Pipe warten. Host: %-.64s pipe: %-.32s (%lu)", + "Kann Named Pipe nicht oeffnen. Host: %-.64s pipe: %-.32s (%lu)", + "Kann den Status der Named Pipe nicht setzen. Host: %-.64s pipe: %-.32s (%lu)", + "Can't initialize character set %-.64s (path: %-.64s)", + "Got packet bigger than 'max_allowed_packet'" +}; + +/* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ + +#elif defined PORTUGUESE +const char *client_errors[]= +{ + "Erro desconhecido do MySQL", + "Não pode criar 'UNIX socket' (%d)", + "Não pode se conectar ao servidor MySQL local através do 'socket' '%-.64s' (%d)", + "Não pode se conectar ao servidor MySQL em '%-.64s' (%d)", + "Não pode criar 'socket TCP/IP' (%d)", + "'Host' servidor MySQL '%-.64s' (%d) desconhecido", + "Servidor MySQL desapareceu", + "Incompatibilidade de protocolos. Versão do Servidor: %d - Versão do Cliente: %d", + "Cliente do MySQL com falta de memória", + "Informação inválida de 'host'", + "Localhost via 'UNIX socket'", + "%-.64s via 'TCP/IP'", + "Erro na negociação de acesso ao servidor", + "Conexão perdida com servidor MySQL durante 'query'", + "Comandos fora de sincronismo. Você não pode executar este comando agora", + "%-.64s via 'named pipe'", + "Não pode esperar pelo 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "Não pode abrir 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "Não pode estabelecer o estado do 'named pipe' para o 'host' %-.64s - 'pipe' %-.32s (%lu)", + "Não pode inicializar conjunto de caracteres %-.64s (caminho %-.64s)", + "Obteve pacote maior do que 'max_allowed_packet'" +}; + +#else /* ENGLISH */ +const char *client_errors[]= +{ +/* 2000 */ "Unknown MySQL error", +/* 2001 */ "Can't create UNIX socket (%d)", +/* 2002 */ "Can't connect to local MySQL server through socket '%-.64s' (%d)", +/* 2003 */ "Can't connect to MySQL server on '%-.64s' (%d)", +/* 2004 */ "Can't create TCP/IP socket (%d)", +/* 2005 */ "Unknown MySQL Server Host '%-.64s' (%d)", +/* 2006 */ "MySQL server has gone away", +/* 2007 */ "Protocol mismatch. Server Version = %d Client Version = %d", +/* 2008 */ "MySQL 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 MySQL 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 */ "SSL connection error: %100s", +/* 2027 */ "received malformed packet", +/* 2028 */ "", +/* 2029 */ "", +/* 2030 */ "Statement is not prepared", +/* 2031 */ "No data supplied for parameters in prepared statement", +/* 2032 */ "", +/* 2033 */ "", +/* 2034 */ "", +/* 2035 */ "", +/* 2036 */ "Buffer type is not supported", +/* 2037 */ "", +/* 2038 */ "", +/* 2039 */ "", +/* 2040 */ "", +/* 2041 */ "", +/* 2042 */ "", +/* 2043 */ "", +/* 2044 */ "", +/* 2045 */ "", +/* 2046 */ "", +/* 2047 */ "", +/* 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 MySQL server at '%s', system error: %d", +/* 2056 */ "", +/* 2057 */ "The number of parameters in bound buffers differs from number of columns in resultset", +/* 2058 */ "Plugin %s could not be loaded: %s", +/* 2059 */ "Can't connect twice. Already connected", +/* 2060 */ "Plugin doesn't support this function", + "" +}; +#endif + + +void init_client_errs(void) +{ + my_errmsg[CLIENT_ERRMAP] = &client_errors[0]; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/errors.c b/mariadb-connector-c-v_2.3.7/libmariadb/errors.c new file mode 100644 index 0000000..cfa1da1 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/errors.c @@ -0,0 +1,96 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" + +#ifndef SHARED_LIBRARY + +const char * NEAR globerrs[GLOBERRS]= +{ + "Can't create/write to file '%s' (Errcode: %d)", + "Error reading file '%s' (Errcode: %d)", + "Error writing file '%s' (Errcode: %d)", + "Error on close of '%s' (Errcode: %d)", + "Out of memory (Needed %u bytes)", + "Error on delete of '%s' (Errcode: %d)", + "Error on rename of '%s' to '%s' (Errcode: %d)", + "", + "Unexpected eof found when reading file '%s' (Errcode: %d)", + "Can't lock file (Errcode: %d)", + "Can't unlock file (Errcode: %d)", + "Can't read dir of '%s' (Errcode: %d)", + "Can't get stat of '%s' (Errcode: %d)", + "Can't change size of file (Errcode: %d)", + "Can't open stream from handle (Errcode: %d)", + "Can't get working dirctory (Errcode: %d)", + "Can't change dir to '%s' (Errcode: %d)", + "Warning: '%s' had %d links", + "Warning: %d files and %d streams is left open\n", + "Disk is full writing '%s' (Errcode: %d). Waiting for someone to free space... (Expect up to %d secs delay for server to continue after freeing disk space)", + "Can't create directory '%s' (Errcode: %d)", + "Character set '%s' is not a compiled character set and is not specified in the '%s' file", + "Out of resources when opening file '%s' (Errcode: %d)", + "Can't read value for symlink '%s' (Error %d)", + "Can't create symlink '%s' pointing at '%s' (Error %d)", + "Error on realpath() on '%s' (Error %d)", + "Can't sync file '%s' to disk (Errcode: %d)", + "Collation '%s' is not a compiled collation and is not specified in the '%s' file", + "File '%s' not found (Errcode: %d)", + "File '%s' (fileno: %d) was not closed", + "Can't change mode for file '%s' to 0x%lx (Error: %d)" +}; + +void init_glob_errs(void) +{ + my_errmsg[GLOB] = & globerrs[0]; +} /* init_glob_errs */ + +#else + +void init_glob_errs() +{ + my_errmsg[GLOB] = & globerrs[0]; + + EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; + EE(EE_CANTCREATEFILE) = "Can't create/write to file '%s' (Errcode: %d)"; + EE(EE_READ) = "Error reading file '%s' (Errcode: %d)"; + EE(EE_WRITE) = "Error writing file '%s' (Errcode: %d)"; + EE(EE_BADCLOSE) = "Error on close of '%'s (Errcode: %d)"; + EE(EE_OUTOFMEMORY) = "Out of memory (Needed %u bytes)"; + EE(EE_DELETE) = "Error on delete of '%s' (Errcode: %d)"; + EE(EE_LINK) = "Error on rename of '%s' to '%s' (Errcode: %d)"; + EE(EE_EOFERR) = "Unexpected eof found when reading file '%s' (Errcode: %d)"; + EE(EE_CANTLOCK) = "Can't lock file (Errcode: %d)"; + EE(EE_CANTUNLOCK) = "Can't unlock file (Errcode: %d)"; + EE(EE_DIR) = "Can't read dir of '%s' (Errcode: %d)"; + EE(EE_STAT) = "Can't get stat of '%s' (Errcode: %d)"; + EE(EE_CANT_CHSIZE) = "Can't change size of file (Errcode: %d)"; + EE(EE_CANT_OPEN_STREAM)= "Can't open stream from handle (Errcode: %d)"; + EE(EE_GETWD) = "Can't get working dirctory (Errcode: %d)"; + EE(EE_SETWD) = "Can't change dir to '%s' (Errcode: %d)"; + EE(EE_LINK_WARNING) = "Warning: '%s' had %d links"; + EE(EE_OPEN_WARNING) = "%d files and %d streams is left open\n"; + EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space..."; + EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)"; + EE(EE_UNKNOWN_CHARSET)= "Character set is not a compiled character set and is not specified in the %s file"; + EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)"; + EE(EE_CANT_READLINK)="Can't read value for symlink '%s' (Error %d)"; + EE(EE_CANT_SYMLINK)="Can't create symlink '%s' pointing at '%s' (Error %d)"; + EE(EE_REALPATH)="Error on realpath() on '%s' (Error %d)"; +} +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/get_password.c b/mariadb-connector-c-v_2.3.7/libmariadb/get_password.c new file mode 100644 index 0000000..3637205 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/get_password.c @@ -0,0 +1,175 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA +*************************************************************************************/ +#include +#include +#include "mysql.h" +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#else +#include +#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, &CharsProcessed, NULL) || + !CharsProcessed) + break; +#else + inChar= 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; + int Offset= 0; + DWORD CharsProcessed= 0; + + 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/mariadb-connector-c-v_2.3.7/libmariadb/getopt.c b/mariadb-connector-c-v_2.3.7/libmariadb/getopt.c new file mode 100644 index 0000000..cbec667 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/getopt.c @@ -0,0 +1,746 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + +Changes by monty: +- Added include of string.h when nessessary. +- Removed two warnings from gcc. + +This file is part of the GNU C Library. Its master source is NOT part of +the C library, however. The master source lives in /gd/gnu/lib. + +The GNU C 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. + +The GNU C 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 the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if (!defined (__STDC__) || !__STDC__) && !defined(MSDOS) && !defined(OS2) +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include /* Changes for mysys */ +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (const char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound=0; /* Keep gcc happy */ + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((size_t) (nameend - nextchar) == (size_t) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +#ifdef __EMX__ +int getopt (int argc, char **argv, __const__ char *optstring) +#else +int +getopt (int argc, char *const *argv, const char *optstring) +#endif +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/getopt1.c b/mariadb-connector-c-v_2.3.7/libmariadb/getopt1.c new file mode 100644 index 0000000..d89f7bc --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/getopt1.c @@ -0,0 +1,170 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994 + Free Software Foundation, Inc. + +This file is part of the GNU C Library. Its master source is NOT part of +the C library, however. The master source lives in /gd/gnu/lib. + +The GNU C 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. + +The GNU C 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 the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "getopt.h" + +#if (!defined (__STDC__) || !__STDC__) && !defined(MSDOS) && !defined(OS2) +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + +#ifndef _WIN32 +#include +#endif /* _WIN32 */ + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/hash.c b/mariadb-connector-c-v_2.3.7/libmariadb/hash.c new file mode 100644 index 0000000..8436ef9 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/hash.c @@ -0,0 +1,644 @@ +/************************************************************************************ + Copyright (C) 2000, 2012 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 + 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 saveing keys */ +/* One of key_length or key_length_offset must be given */ +/* Key length of 0 isn't allowed */ + +#include "mysys_priv.h" +#include +#include +#include "hash.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(HASH_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(HASH *hash,HASH_LINK *pos,const uchar *key,uint length); + + +my_bool _hash_init(HASH *hash,uint size,uint key_offset,uint key_length, + hash_get_key get_key, + void (*free_element)(void*),uint flags CALLER_INFO_PROTO) +{ + DBUG_ENTER("hash_init"); + DBUG_PRINT("enter",("hash: %lx size: %d",hash,size)); + + hash->records=0; + if (my_init_dynamic_array_ci(&hash->array,sizeof(HASH_LINK),size,0)) + { + hash->free=0; /* Allow call to hash_free */ + DBUG_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 & HASH_CASE_INSENSITIVE) + hash->calc_hashnr=calc_hashnr_caseup; + else + hash->calc_hashnr=calc_hashnr; + DBUG_RETURN(0); +} + + +void hash_free(HASH *hash) +{ + DBUG_ENTER("hash_free"); + if (hash->free) + { + uint i,records; + HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); + for (i=0,records=hash->records ; i < records ; i++) + (*hash->free)(data[i].data); + hash->free=0; + } + delete_dynamic(&hash->array); + hash->records=0; + DBUG_VOID_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(HASH *hash,const uchar *record,uint *length,my_bool first) +{ + if (hash->get_key) + return (*hash->get_key)(record,(uint *)length,first); + *length=hash->key_length; + return (uchar*) 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(HASH *hash,HASH_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_HASH_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 indepenently */ + +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(HASH *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 */ + +gptr hash_search(HASH *hash,const uchar *key,uint length) +{ + HASH_LINK *pos; + uint flag,idx; + DBUG_ENTER("hash_search"); + + 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,HASH_LINK*); + if (!hashcmp(hash,pos,key,length)) + { + DBUG_PRINT("exit",("found key at %d",idx)); + hash->current_record= idx; + DBUG_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; + DBUG_RETURN(0); +} + + /* Get next record with identical key */ + /* Can only be called if previous calls was hash_search */ + +gptr hash_next(HASH *hash,const uchar *key,uint length) +{ + HASH_LINK *pos; + uint idx; + + if (hash->current_record != NO_RECORD) + { + HASH_LINK *data=dynamic_element(&hash->array,0,HASH_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(HASH_LINK *array,uint find,uint next_link,uint newlink) +{ + HASH_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(HASH *hash,HASH_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 hash_insert(HASH *info,const uchar *record) +{ + int flag; + uint halfbuff,hash_nr,first_index,idx; + uchar *ptr_to_rec,*ptr_to_rec2; + HASH_LINK *data,*empty,*gpos,*gpos2,*pos; + + LINT_INIT(gpos); LINT_INIT(gpos2); + LINT_INIT(ptr_to_rec); LINT_INIT(ptr_to_rec2); + + flag=0; + if (!(empty=(HASH_LINK*) alloc_dynamic(&info->array))) + return(TRUE); /* No more memory */ + + info->current_record= NO_RECORD; + data=dynamic_element(&info->array,0,HASH_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 hash_delete(HASH *hash,uchar *record) +{ + uint blength,pos2,pos_hashnr,lastpos_hashnr,idx,empty_index; + HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty; + DBUG_ENTER("hash_delete"); + if (!hash->records) + DBUG_RETURN(1); + + blength=hash->blength; + data=dynamic_element(&hash->array,0,HASH_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) + DBUG_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: + VOID(pop_dynamic(&hash->array)); + if (hash->free) + (*hash->free)((uchar*) record); + DBUG_RETURN(0); +} + + /* + Update keys when record has changed. + This is much more efficent than using a delete & insert. + */ + +my_bool hash_update(HASH *hash,uchar *record,uchar *old_key,uint old_key_length) +{ + uint idx,new_index,new_pos_index,blength,records,empty; + HASH_LINK org_link,*data,*previous,*pos; + DBUG_ENTER("hash_update"); + + data=dynamic_element(&hash->array,0,HASH_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) + DBUG_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) + DBUG_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; + } + DBUG_RETURN(0); +} + + +uchar *hash_element(HASH *hash,uint idx) +{ + if (idx < hash->records) + return dynamic_element(&hash->array,idx,HASH_LINK*)->data; + return 0; +} + + +#ifndef DBUG_OFF + +my_bool hash_check(HASH *hash) +{ + int error; + uint i,rec_link,found,max_links,seek,links,idx; + uint records,blength; + HASH_LINK *data,*hash_info; + + records=hash->records; blength=hash->blength; + data=dynamic_element(&hash->array,0,HASH_LINK*); + error=0; + + for (i=found=max_links=seek=0 ; i < records ; i++) + { + if (hash_rec_mask(hash,data+i,blength,records) == i) + { + found++; seek++; links=1; + for (idx=data[i].next ; + idx != NO_RECORD && found < records + 1; + idx=hash_info->next) + { + if (idx >= records) + { + DBUG_PRINT("error", + ("Found pointer outside array to %d from link starting at %d", + idx,i)); + error=1; + } + hash_info=data+idx; + seek+= ++links; + if ((rec_link=hash_rec_mask(hash,hash_info,blength,records)) != i) + { + DBUG_PRINT("error", + ("Record in wrong link at %d: Start %d Record: %lx Record-link %d", idx,i,hash_info->data,rec_link)); + error=1; + } + else + found++; + } + if (links > max_links) max_links=links; + } + } + if (found != records) + { + DBUG_PRINT("error",("Found %ld of %ld records")); + error=1; + } + if (records) + DBUG_PRINT("info", + ("records: %ld seeks: %d max links: %d hitrate: %.2f", + records,seek,max_links,(float) seek / (float) records)); + return error; +} +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/int2str.c b/mariadb-connector-c-v_2.3.7/libmariadb/int2str.c new file mode 100644 index 0000000..6f201c6 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/int2str.c @@ -0,0 +1,153 @@ +/* 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 */ + +/* + Defines: int2str(), itoa(), ltoa() + + int2str(dst, radix, val) + converts the (long) integer "val" to character form and moves it to + the destination string "dst" followed by a terminating NUL. The + result is normally a pointer to this NUL character, but if the radix + is dud the result will be NullS and nothing will be changed. + + If radix is -2..-36, val is taken to be SIGNED. + If radix is 2.. 36, val is taken to be UNSIGNED. + That is, val is signed if and only if radix is. You will normally + use radix -10 only through itoa and ltoa, for radix 2, 8, or 16 + unsigned is what you generally want. + + _dig_vec is public just in case someone has a use for it. + The definitions of itoa and ltoa are actually macros in m_string.h, + but this is where the code is. + + Note: The standard itoa() returns a pointer to the argument, when int2str + returns the pointer to the end-null. + itoa assumes that 10 -base numbers are allways signed and other arn't. +*/ + +#include +#include "m_string.h" + +char NEAR _dig_vec[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +char *int2str(register long int val, register char *dst, register int radix) +{ + char buffer[65]; + register char *p; + long int new_val; + + if (radix < 0) { + if (radix < -36 || radix > -2) return NullS; + if (val < 0) { + *dst++ = '-'; + val = -val; + } + radix = -radix; + } else { + if (radix > 36 || radix < 2) return NullS; + } + /* The slightly contorted code which follows is due to the + fact that few machines directly support unsigned long / and %. + Certainly the VAX C compiler generates a subroutine call. In + the interests of efficiency (hollow laugh) I let this happen + for the first digit only; after that "val" will be in range so + that signed integer division will do. Sorry 'bout that. + CHECK THE CODE PRODUCED BY YOUR C COMPILER. The first % and / + should be unsigned, the second % and / signed, but C compilers + tend to be extraordinarily sensitive to minor details of style. + This works on a VAX, that's all I claim for it. + */ + p = &buffer[sizeof(buffer)-1]; + *p = '\0'; + new_val=(ulong) val / (ulong) radix; + *--p = _dig_vec[(uchar) ((ulong) val- (ulong) new_val*(ulong) radix)]; + val = new_val; +#ifdef HAVE_LDIV + while (val != 0) + { + ldiv_t res; + res=ldiv(val,radix); + *--p = _dig_vec[res.rem]; + val= res.quot; + } +#else + while (val != 0) + { + new_val=val/radix; + *--p = _dig_vec[(uchar) (val-new_val*radix)]; + val= new_val; + } +#endif + while ((*dst++ = *p++) != 0) ; + return dst-1; +} + + +/* + This is a faster version of the above optimized for the normal case of + radix 10 / -10 +*/ + +char *int10_to_str(long int val, char *dst, int radix) +{ + char buffer[65]; + register char *p; + long int new_val; + unsigned long int uval= (unsigned long int)val; + + if (radix < 0 && val < 0) /* -10 */ + { + *dst++ = '-'; + uval = (unsigned long int)0-uval; + } + + p = &buffer[sizeof(buffer)-1]; + *p = '\0'; + new_val= (long)(uval / 10); + *--p = '0'+ (char)(uval - (unsigned long)new_val * 10); + val = new_val; + + while (val != 0) + { + new_val=val/10; + *--p = '0' + (char) (val-new_val*10); + val= new_val; + } + while ((*dst++ = *p++) != 0) ; + return dst-1; +} + + +#ifdef USE_MY_ITOA + + /* Change to less general itoa interface */ + +char *my_itoa(int val, char *dst, int radix) +{ + VOID(int2str((long) val,dst,(radix == 10 ? -10 : radix))); + return dst; +} + +char *my_ltoa(long int val, char *dst, int radix) +{ + VOID(int2str((long) val,dst,(radix == 10 ? -10 : radix))); + return dst; +} + +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/is_prefix.c b/mariadb-connector-c-v_2.3.7/libmariadb/is_prefix.c new file mode 100644 index 0000000..36665ad --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/is_prefix.c @@ -0,0 +1,34 @@ +/* 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 : is_prefix.c + Author : Michael Widenius + Defines: is_prefix() + + is_prefix(s, t) returns 1 if s starts with t. + A empty t is allways a prefix. +*/ + +#include +#include "m_string.h" + +int is_prefix(register const char *s, register const char *t) +{ + while (*t) + if (*s++ != *t++) return 0; + return 1; /* WRONG */ +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c b/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c new file mode 100644 index 0000000..2f0953f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/libmariadb.c @@ -0,0 +1,3728 @@ +/************************************************************************************ + Copyright (C) 2000, 2012 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 + 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 + +#include +#include +#include +#include +#include +#include "my_context.h" +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "errmsg.h" +#include +#include +#include +#ifdef HAVE_PWD_H +#include +#endif +#if !defined(MSDOS) && !defined(_WIN32) +#include +#include +#include +#include +#ifdef HAVE_SELECT_H +# include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#endif +#ifdef HAVE_SYS_UN_H +# include +#endif +#if defined(THREAD) && !defined(_WIN32) +#include /* because of signal() */ +#endif +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif +#include +#include +#ifdef HAVE_OPENSSL +#include +#endif +#ifndef _WIN32 +#include +#endif +#include +#include + +#define DNS_TIMEOUT 30 +#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) + +#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); +extern my_bool my_init_done; +extern my_bool mysql_ps_subsystem_initialized; +extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename); +extern const CHARSET_INFO * mysql_find_charset_nr(uint charsetnr); +extern const CHARSET_INFO * mysql_find_charset_name(const char * const name); +extern int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + const char *data_plugin, const char *db); + +/* 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_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 unsigned char *mysql_net_store_length(unsigned char *packet, size_t length); + +uint mysql_port=0; +my_string mysql_unix_port=0; + +#ifdef _WIN32 +#define CONNECT_TIMEOUT 20 +#else +#define CONNECT_TIMEOUT 0 +#endif + +struct st_mysql_methods MARIADB_DEFAULT_METHODS; + +#if defined(MSDOS) || defined(_WIN32) +// socket_errno is defined in my_global.h for all platforms +#define perror(A) +#else +#include +#define SOCKET_ERROR -1 +#endif /* _WIN32 */ + +#include + +#define native_password_plugin_name "mysql_native_password" + +static void end_server(MYSQL *mysql); +static void mysql_close_memory(MYSQL *mysql); +void read_user_name(char *name); +static void append_wild(char *to,char *end,const char *wild); +static my_bool mysql_reconnect(MYSQL *mysql); +static sig_handler pipe_sig_handler(int sig); +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); + +extern int mysql_client_plugin_init(); +extern void mysql_client_plugin_deinit(); + +/* + Let the user specify that we don't want SIGPIPE; This doesn't however work + with threaded applications as we can have multiple read in progress. +*/ + +#if !defined(_WIN32) && defined(SIGPIPE) && !defined(THREAD) +#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; +#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler) +#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); +#else +#define init_sigpipe_variables +#define set_sigpipe(mysql) +#define reset_sigpipe(mysql) +#endif + + + +/**************************************************************************** +* A modified version of connect(). connect2() allows you to specify +* a timeout value, in seconds, that we should wait until we +* derermine we can't connect to a particular host. If timeout is 0, +* connect2() will behave exactly like connect(). +* +* Base version coded by Steve Bernacki, Jr. +*****************************************************************************/ + +int socket_block(my_socket s,my_bool blocked) +{ +#ifdef _WIN32 + unsigned long socket_blocked= blocked ? 0 : 1; + return ioctlsocket(s, FIONBIO, &socket_blocked); +#else + int flags= fcntl(s, F_GETFL, 0); + if (blocked) + flags&= ~O_NONBLOCK; + else + flags|= O_NONBLOCK; + return fcntl(s, F_SETFL, flags); +#endif +} + +static int connect2(my_socket s, const struct sockaddr *name, size_t namelen, + uint timeout) +{ + int res, s_err; + socklen_t s_err_size = sizeof(uint); +#ifndef _WIN32 + struct pollfd poll_fd; +#else + FD_SET sfds, efds; + struct timeval tv; +#endif + + if (!timeout) + return connect(s, (struct sockaddr*) name, (int)namelen); + + /* set socket to non blocking */ + if (socket_block(s, 0) == SOCKET_ERROR) + return -1; + + res= connect(s, (struct sockaddr*) name, (int)namelen); + if (res == 0) + return res; + +#ifdef _WIN32 + if (GetLastError() != WSAEWOULDBLOCK && + GetLastError() != WSAEINPROGRESS) +#else + if (errno != EINPROGRESS) +#endif + return -1; +#ifndef _WIN32 + memset(&poll_fd, 0, sizeof(struct pollfd)); + poll_fd.events= POLLOUT | POLLERR; + poll_fd.fd= s; + + /* connection timeout in milliseconds */ + res= poll(&poll_fd, 1, timeout); + + switch(res) + { + /* Error= - 1, timeout = 0 */ + case -1: + break; + case 0: + errno= ETIMEDOUT; + break; + } + s_err=0; + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) + return(-1); + + if (s_err) + { /* getsockopt could succeed */ + errno = s_err; + return(-1); /* but return an error... */ + } +#else + FD_ZERO(&sfds); + FD_ZERO(&efds); + FD_SET(s, &sfds); + FD_SET(s, &efds); + + memset(&tv, 0, sizeof(struct timeval)); + tv.tv_sec= timeout; + + res= select((int)s+1, NULL, &sfds, &efds, &tv); + if (res== -1) + { + errno= WSAGetLastError(); + } + else if (res == 0) + { + errno= ETIMEDOUT; + res= SOCKET_ERROR; + } + else if (FD_ISSET(s, &efds)) + { + int err; + int len = sizeof(int); + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &len) != SOCKET_ERROR) + { + errno= err; + } + res= SOCKET_ERROR; + } + if (res < 1) + return -1; +#endif + return (0); /* ok */ +} + +static int +connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, + const struct sockaddr *name, uint namelen) +{ + int vio_timeout= (mysql->options.connect_timeout > 0) ? + mysql->options.connect_timeout * 1000 : -1; + + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + { + my_bool old_mode; + vio_blocking(net->vio, FALSE, &old_mode); + return my_connect_async(mysql->options.extension->async_context, fd, + name, namelen, vio_timeout); + } + + return connect2(fd, name, namelen, vio_timeout); +} +/* +** Create a named pipe connection +*/ + +#ifdef _WIN32 + +HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, + char **arg_unix_socket) +{ + HANDLE hPipe=INVALID_HANDLE_VALUE; + char szPipeName [ 257 ]; + DWORD dwMode; + int i; + my_bool testing_named_pipes=0; + char *host= *arg_host, *unix_socket= *arg_unix_socket; + + if ( ! unix_socket || (unix_socket)[0] == 0x00) + unix_socket = mysql_unix_port; + if (!host || !strcmp(host,LOCAL_HOST)) + host=LOCAL_HOST_NAMEDPIPE; + + sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket); + DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s", + host, unix_socket)); + + for (i=0 ; i < 100 ; i++) /* Don't retry forever */ + { + if ((hPipe = CreateFile(szPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL )) != INVALID_HANDLE_VALUE) + break; + if (GetLastError() != ERROR_PIPE_BUSY) + { + net->last_errno=CR_NAMEDPIPEOPEN_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + /* wait for for an other instance */ + if (! WaitNamedPipe(szPipeName, connect_timeout) ) + { + net->last_errno=CR_NAMEDPIPEWAIT_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + } + if (hPipe == INVALID_HANDLE_VALUE) + { + net->last_errno=CR_NAMEDPIPEOPEN_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) ) + { + CloseHandle( hPipe ); + net->last_errno=CR_NAMEDPIPESETSTATE_ERROR; + sprintf(net->last_error,ER(net->last_errno),host, unix_socket, + (ulong) GetLastError()); + return INVALID_HANDLE_VALUE; + } + *arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */ + return (hPipe); +} +#endif + +/* 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, unknown_sqlstate, SQLSTATE_LENGTH); + } +} + + +/***************************************************************************** +** read a packet from server. Give error message if socket was down +** or packet is an error message +*****************************************************************************/ + +ulong +net_safe_read(MYSQL *mysql) +{ + NET *net= &mysql->net; + ulong len=0; + init_sigpipe_variables + +restart: + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + if (net->vio != 0) + len=my_net_read(net); + reset_sigpipe(mysql); + + if (len == packet_error || len == 0) + { + end_server(mysql); + my_set_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ? + CR_NET_PACKET_TOO_LARGE: + CR_SERVER_LOST, + SQLSTATE_UNKNOWN, 0); + 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 && + (mysql->server_capabilities & CLIENT_PROGRESS)) + { + if (cli_report_progress(mysql, (uchar *)pos, (uint) (len-1))) + { + /* Wrong packet */ + my_set_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate, 0); + return (packet_error); + } + goto restart; + } + net->last_errno= last_errno; + if (pos[0]== '#') + { + strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH); + pos+= SQLSTATE_LENGTH + 1; + } + else + { + strmov(net->sqlstate, SQLSTATE_UNKNOWN); + } + (void) 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; + + DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno, + net->last_error)); + 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 my_ulonglong +net_field_length_ll(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (my_ulonglong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return (my_ulonglong) NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (my_ulonglong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (my_ulonglong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG + return (my_ulonglong) uint4korr(pos+1); +#else + return (my_ulonglong) uint8korr(pos+1); +#endif +} + + +void free_rows(MYSQL_DATA *cur) +{ + if (cur) + { + free_root(&cur->alloc,MYF(0)); + my_free(cur); + } +} + + +int +mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg) +{ + NET *net= &mysql->net; + int result= -1; + init_sigpipe_variables + + DBUG_ENTER("mthd_my_send_command"); + + DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); + + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + if (mysql->net.vio == 0) + { /* Do reconnect if possible */ + if (mysql_reconnect(mysql)) + { + DBUG_PRINT("info", ("reconnect failed")); + DBUG_RETURN(1); + } + } + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXIST) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + goto end; + } + + CLEAR_CLIENT_ERROR(mysql); + + mysql->info=0; + mysql->affected_rows= ~(my_ulonglong) 0; + net_clear(net); /* Clear receive buffer */ + if (!arg) + arg=""; + + if (net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg))) + { + DBUG_PRINT("error",("Can't send command to server. Error: %d",socket_errno)); + 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 (mysql_reconnect(mysql)) + goto end; + if (net_write_command(net,(uchar) command,arg, + length ? length : (ulong) strlen(arg))) + { + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + goto end; + } + } + result=0; + if (!skipp_check) { + result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? + 1 : 0); + DBUG_PRINT("info", ("packet_length=%llu", mysql->packet_length)); + } + end: + reset_sigpipe(mysql); + DBUG_RETURN(result); +} + +int +simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg) +{ + return mysql->methods->db_command(mysql, command, arg, length, skipp_check, opt_arg); +} + +static void free_old_query(MYSQL *mysql) +{ + DBUG_ENTER("free_old_query"); + if (mysql->fields) + free_root(&mysql->field_alloc,MYF(0)); + init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */ + mysql->fields=0; + mysql->field_count=0; /* For API */ + mysql->info= 0; + DBUG_VOID_RETURN; +} + +#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL) +struct passwd *getpwuid(uid_t); +char* getlogin(void); +#endif + +#if !defined(MSDOS) && ! defined(VMS) && !defined(_WIN32) && !defined(OS2) +void read_user_name(char *name) +{ + DBUG_ENTER("read_user_name"); + if (geteuid() == 0) + (void) strmov(name,"root"); /* allow use of surun */ + else + { +#ifdef HAVE_GETPWUID + struct passwd *skr; + const char *str; + if ((str=getlogin()) == NULL) + { + if ((skr=getpwuid(geteuid())) != NULL) + str=skr->pw_name; + else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) && + !(str=getenv("LOGIN"))) + str="UNKNOWN_USER"; + } + (void) strmake(name,str,USERNAME_LENGTH); +#elif HAVE_CUSERID + (void) cuserid(name); +#else + strmov(name,"UNKNOWN_USER"); +#endif + } + DBUG_VOID_RETURN; +} + +#else /* If MSDOS || VMS */ + +void read_user_name(char *name) +{ + char *str=getenv("USERNAME"); /* ODBC will send user variable */ + strmake(name,str ? str : "ODBC", USERNAME_LENGTH); +} + +#endif + +#ifdef _WIN32 +static my_bool is_NT(void) +{ + char *os=getenv("OS"); + return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; +} +#endif + +/* +** Expand wildcard to a sql string +*/ + +static void +append_wild(char *to, char *end, const char *wild) +{ + end-=5; /* Some extra */ + if (wild && wild[0]) + { + to=strmov(to," like '"); + while (*wild && to < end) + { + if (*wild == '\\' || *wild == '\'') + *to++='\\'; + *to++= *wild++; + } + if (*wild) /* Too small buffer */ + *to++='%'; /* Nicer this way */ + to[0]='\''; + to[1]=0; + } +} + + + +/************************************************************************** +** Init debugging if MYSQL_DEBUG environment variable is found +**************************************************************************/ +void STDCALL mysql_debug_end() +{ +#ifndef DBUG_OFF + DEBUGGER_OFF; + DBUG_POP(); +#endif +} + +void STDCALL +mysql_debug(const char *debug __attribute__((unused))) +{ +#ifndef DBUG_OFF + char *env; + if (debug) + { + DEBUGGER_ON; + DBUG_PUSH(debug); + } + else if ((env = getenv("MYSQL_DEBUG"))) + { + DEBUGGER_ON; + DBUG_PUSH(env); +#if !defined(_WINVER) && !defined(WINVER) + puts("\n-------------------------------------------------------"); + puts("MYSQL_DEBUG found. libmariadb started with the following:"); + puts(env); + puts("-------------------------------------------------------\n"); +#else + { + char buff[80]; + strmov(strmov(buff,"libmariadb: "),env); + MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK); + } +#endif + } +#endif +} + + +/************************************************************************** +** Close the server connection if we get a SIGPIPE + ARGSUSED +**************************************************************************/ + +static sig_handler +pipe_sig_handler(int sig __attribute__((unused))) +{ + DBUG_PRINT("info",("Hit by signal %d",sig)); +#ifdef DONT_REMEMBER_SIGNAL + (void) signal(SIGPIPE,pipe_sig_handler); +#endif +} + + +/************************************************************************** +** Shut down connection +**************************************************************************/ + +static void +end_server(MYSQL *mysql) +{ + DBUG_ENTER("end_server"); + if (mysql->net.vio != 0) + { + init_sigpipe_variables + set_sigpipe(mysql); + vio_delete(mysql->net.vio); + reset_sigpipe(mysql); + mysql->net.vio= 0; /* Marker */ + } + net_end(&mysql->net); + free_old_query(mysql); + DBUG_VOID_RETURN; +} + +void mthd_my_skip_result(MYSQL *mysql) +{ + ulong pkt_len; + DBUG_ENTER("madb_skip_result"); + + do { + pkt_len= net_safe_read(mysql); + if (pkt_len == packet_error) + break; + } while (pkt_len > 8 || mysql->net.read_pos[0] != 254); + DBUG_VOID_RETURN; +} + +void STDCALL +mysql_free_result(MYSQL_RES *result) +{ + DBUG_ENTER("mysql_free_result"); + DBUG_PRINT("enter",("mysql_res: %lx",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) + free_root(&result->field_alloc,MYF(0)); + if (result->row) + my_free(result->row); + my_free(result); + } + DBUG_VOID_RETURN; +} + + +/**************************************************************************** +** Get options from my.cnf +****************************************************************************/ + +static const char *default_options[]= +{ + "port","socket","compress","password","pipe", "timeout", "user", + "init-command", "host", "database", "debug", "return-found-rows", + "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", + "character-sets-dir", "default-character-set", "interactive-timeout", + "connect-timeout", "local-infile", "disable-local-infile", + "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", + "multi-results", "multi-statements", "multi-queries", "secure-auth", + "report-data-truncation", "plugin-dir", "default-auth", "database-type", + "ssl-fp", "ssl-fp-list", "bind-address", + NULL +}; + +enum option_val +{ + OPT_port=1, OPT_socket, OPT_compress, OPT_password, OPT_pipe, + OPT_timeout, OPT_user, OPT_init_command, OPT_host, OPT_database, + OPT_debug, OPT_return_found_rows, OPT_ssl_key, OPT_ssl_cert, + OPT_ssl_ca, OPT_ssl_capath, OPT_charset_dir, + OPT_charset_name, OPT_interactive_timeout, + OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile, + OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, + OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, + OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type, + OPT_ssl_fp, OPT_ssl_fp_list, OPT_bind_address +}; + +#define CHECK_OPT_EXTENSION_SET(OPTS)\ + if (!(OPTS)->extension) \ + (OPTS)->extension= (struct st_mysql_options_extension *) \ + my_malloc(sizeof(struct st_mysql_options_extension), \ + MYF(MY_WME | MY_ZEROFILL)); \ + +#define OPT_SET_EXTENDED_VALUE(OPTS, KEY, VAL, IS_STRING) \ + CHECK_OPT_EXTENSION_SET(OPTS) \ + if (IS_STRING) { \ + my_free((OPTS)->extension->KEY); \ + (OPTS)->extension->KEY= my_strdup((char *)(VAL), MYF(MY_WME)); \ + } else \ + (OPTS)->extension->KEY= (VAL); + +static TYPELIB option_types={array_elements(default_options)-1, + "options",default_options}; + +const char *protocol_names[]= {"TCP", "SOCKED", "PIPE", "MEMORY", NULL}; +static TYPELIB protocol_types= {array_elements(protocol_names)-1, + "protocol names", + protocol_names}; + +static void options_add_initcommand(struct st_mysql_options *options, + const char *init_cmd) +{ + char *insert= my_strdup(init_cmd, MYF(MY_WME)); + if (!options->init_command) + { + options->init_command= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY), + MYF(MY_WME)); + my_init_dynamic_array(options->init_command, sizeof(char*), 5, 5); + } + + if (insert_dynamic(options->init_command, (gptr)&insert)) + my_free(insert); +} + + +static void mysql_read_default_options(struct st_mysql_options *options, + const char *filename,const char *group) +{ + int argc; + char *argv_buff[1],**argv; + const char *groups[3]; + DBUG_ENTER("mysql_read_default_options"); + DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); + + argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; + groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0; + + load_defaults(filename, groups, &argc, &argv); + if (argc != 1) /* If some default option */ + { + char **option=argv; + while (*++option) + { + /* DBUG_PRINT("info",("option: %s",option[0])); */ + if (option[0][0] == '-' && option[0][1] == '-') + { + char *end=strcend(*option,'='); + char *opt_arg=0; + if (*end) + { + opt_arg=end+1; + *end=0; /* Remove '=' */ + } + /* Change all '_' in variable name to '-' */ + for (end= *option ; *(end= strcend(end,'_')) ; ) + *end= '-'; + switch (find_type(*option+2,&option_types,2)) { + case OPT_port: + if (opt_arg) + options->port=atoi(opt_arg); + break; + case OPT_socket: + if (opt_arg) + { + my_free(options->unix_socket); + options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_compress: + options->compress=1; + break; + case OPT_password: + if (opt_arg) + { + my_free(options->password); + options->password=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_pipe: + options->named_pipe=1; /* Force named pipe */ + break; + case OPT_connect_timeout: + case OPT_timeout: + if (opt_arg) + options->connect_timeout=atoi(opt_arg); + break; + case OPT_user: + if (opt_arg) + { + my_free(options->user); + options->user=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_init_command: + if (opt_arg) + options_add_initcommand(options, opt_arg); + break; + case OPT_host: + if (opt_arg) + { + my_free(options->host); + options->host=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_database: + if (opt_arg) + { + my_free(options->db); + options->db=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case OPT_debug: + mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace"); + break; + case OPT_return_found_rows: + options->client_flag|=CLIENT_FOUND_ROWS; + break; +#ifdef HAVE_OPENSSL + case OPT_ssl_key: + my_free(options->ssl_key); + options->ssl_key = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_cert: + my_free(options->ssl_cert); + options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_ca: + my_free(options->ssl_ca); + options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_capath: + my_free(options->ssl_capath); + options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_ssl_cipher: + break; + case OPT_ssl_fp: + OPT_SET_EXTENDED_VALUE(options, ssl_fp, opt_arg, 1); + break; + case OPT_ssl_fp_list: + OPT_SET_EXTENDED_VALUE(options, ssl_fp_list, opt_arg, 1); + break; +#else + case OPT_ssl_key: + case OPT_ssl_cert: + case OPT_ssl_ca: + case OPT_ssl_capath: + case OPT_ssl_cipher: + case OPT_ssl_fp: + case OPT_ssl_fp_list: + break; +#endif /* HAVE_OPENSSL */ + case OPT_charset_dir: + my_free(options->charset_dir); + options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_charset_name: + my_free(options->charset_name); + options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); + break; + case OPT_interactive_timeout: + options->client_flag|= CLIENT_INTERACTIVE; + break; + case OPT_local_infile: + if (!opt_arg || atoi(opt_arg) != 0) + options->client_flag|= CLIENT_LOCAL_FILES; + else + options->client_flag&= ~CLIENT_LOCAL_FILES; + break; + case OPT_disable_local_infile: + options->client_flag&= CLIENT_LOCAL_FILES; + break; + case OPT_max_allowed_packet: + if(opt_arg) + options->max_allowed_packet= atoi(opt_arg); + break; + case OPT_protocol: + options->protocol= find_type(opt_arg, &protocol_types, 0); +#ifndef _WIN32 + if (options->protocol < 0 || options->protocol > 1) +#else + if (options->protocol < 0) +#endif + { + fprintf(stderr, "Unknown or unsupported protocol %s", opt_arg); + } + break; + case OPT_shared_memory_base_name: + /* todo */ + break; + case OPT_multi_results: + options->client_flag|= CLIENT_MULTI_RESULTS; + break; + case OPT_multi_statements: + case OPT_multi_queries: + options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS; + break; + case OPT_report_data_truncation: + if (opt_arg) + options->report_data_truncation= atoi(opt_arg); + else + options->report_data_truncation= 1; + break; + case OPT_secure_auth: + options->secure_auth= 1; + break; + case OPT_plugin_dir: + { + char directory[FN_REFLEN]; + if (strlen(opt_arg) >= FN_REFLEN) + opt_arg[FN_REFLEN]= 0; + if (!my_realpath(directory, opt_arg, 0)) + OPT_SET_EXTENDED_VALUE(options, plugin_dir, convert_dirname(directory), 1); + } + break; + case OPT_default_auth: + OPT_SET_EXTENDED_VALUE(options, default_auth, opt_arg, 1); + break; + case OPT_bind_address: + my_free(options->bind_address); + options->bind_address= my_strdup(opt_arg, MYF(MY_WME)); + break; + default: + DBUG_PRINT("warning",("unknown option: %s",option[0])); + } + } + } + } + free_defaults(argv); + DBUG_VOID_RETURN; +} + + +/*************************************************************************** +** 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(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, + my_bool default_value, my_bool long_flag_protocol) +{ + MYSQL_ROWS *row; + MYSQL_FIELD *field,*result; + char *p; + unsigned int i, field_count= sizeof(rset_field_offsets)/sizeof(size_t)/2; + + DBUG_ENTER("unpack_fields"); + field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); + if (!result) + DBUG_RETURN(0); + + for (row=data->data; row ; row = row->next,field++) + { + if (field >= result + fields) + goto error; + for (i=0; i < field_count; i++) + { + switch(row->data[i][0]) { + case 0: + *(char **)(((char *)field) + rset_field_offsets[i*2])= strdup_root(alloc, ""); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= 0; + break; + default: + *(char **)(((char *)field) + rset_field_offsets[i*2])= + strdup_root(alloc, (char *)row->data[i]); + *(unsigned int *)(((char *)field) + rset_field_offsets[i*2+1])= + (uint)(row->data[i+1] - row->data[i] - 1); + break; + } + } + + p= (char *)row->data[6]; + /* 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; + + if (default_value && row->data[7]) + { + field->def=strdup_root(alloc,(char*) row->data[7]); + } + else + field->def=0; + field->max_length= 0; + } + if (field < result + fields) + goto error; + free_rows(data); /* Free old data */ + DBUG_RETURN(result); +error: + free_rows(data); + 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; + DBUG_ENTER("madb_read_rows"); + + if ((pkt_len= net_safe_read(mysql)) == packet_error) + DBUG_RETURN(0); + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + 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*) alloc_root(&result->alloc, + sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) + alloc_root(&result->alloc, + (fields+1)*sizeof(char *)+fields+pkt_len)))) + { + free_rows(result); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_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)) + { + free_rows(result); + SET_CLIENT_ERROR(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate, 0); + DBUG_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=net_safe_read(mysql)) == packet_error) + { + free_rows(result); + DBUG_RETURN(0); + } + } + *prev_ptr=0; /* last pointer is null */ + /* save status */ + if (pkt_len > 1) + { + cp++; + mysql->warning_count= uint2korr(cp); + cp+= 2; + mysql->server_status= uint2korr(cp); + } + DBUG_PRINT("exit",("Got %d rows",result->rows)); + DBUG_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) net_safe_read(mysql)) == packet_error) + return -1; + + if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) + { + mysql->warning_count= uint2korr(mysql->net.read_pos + 1); + mysql->server_status= uint2korr(mysql->net.read_pos + 3); + 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)) + { + mysql->net.last_errno=CR_UNKNOWN_ERROR; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + 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*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL)))) + return 0; + mysql->free_me=1; + mysql->net.vio= 0; + } + else + bzero((char*) (mysql),sizeof(*(mysql))); + mysql->options.connect_timeout=CONNECT_TIMEOUT; + mysql->charset= default_charset_info; + strmov(mysql->net.sqlstate, "00000"); + mysql->net.last_error[0]= mysql->net.last_errno= 0; +#if defined(SIGPIPE) && defined(THREAD) && !defined(_WIN32) + if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE)) + (void) signal(SIGPIPE,pipe_sig_handler); +#endif + +/* + Only enable LOAD DATA INFILE by default if configured with + --enable-local-infile +*/ +#ifdef ENABLED_LOCAL_INFILE + mysql->options.client_flag|= CLIENT_LOCAL_FILES; +#endif + mysql->reconnect= 0; + return mysql; +} + + + +int STDCALL +mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, + const char *ca, const char *capath, const char *cipher) +{ + #ifdef HAVE_OPENSSL + mysql->options.use_ssl= 1; + return (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) +{ +#ifdef HAVE_OPENSSL + if (mysql->net.vio && mysql->net.vio->ssl) + { + return SSL_get_cipher_name(mysql->net.vio->ssl); + } +#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. +**************************************************************************/ + +/************************************************************************** +** Connect to sql server +** If host == 0 then use localhost +**************************************************************************/ + +MYSQL * STDCALL +mysql_connect(MYSQL *mysql,const char *host, + const char *user, const char *passwd) +{ + MYSQL *res; + mysql=mysql_init(mysql); /* Make it thread safe */ + { + DBUG_ENTER("mysql_connect"); + if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) + { + if (mysql->free_me) + my_free(mysql); + } + DBUG_RETURN(res); + } +} + +uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer) +{ + if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS) + { + buffer= mysql_net_store_length((unsigned char *)buffer, (mysql->options.extension) ? + mysql->options.extension->connect_attrs_len : 0); + if (mysql->options.extension && + hash_inited(&mysql->options.extension->connect_attrs)) + { + uint i; + for (i=0; i < mysql->options.extension->connect_attrs.records; i++) + { + size_t len; + uchar *p= hash_element(&mysql->options.extension->connect_attrs, i); + + len= *(size_t *)p; + buffer= mysql_net_store_length(buffer, len); + p+= sizeof(size_t); + memcpy(buffer, p, len); + buffer+= len; + p+= len; + len= *(size_t *)p; + buffer= mysql_net_store_length(buffer, len); + p+= sizeof(size_t); + memcpy(buffer, p, len); + buffer+= len; + } + } + } + return buffer; +} + +/** set some default attributes */ +static my_bool +ma_set_connect_attrs(MYSQL *mysql) +{ + 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") + +#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") + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_server_host"); + + 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); + +#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); + rc+= mysql_optionsv(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_server_host", mysql->host); + + 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) +{ + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + + return mysql->methods->db_connect(mysql, host, user, passwd, + db, port, unix_socket, client_flag); +} + +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, + *charset_name= NULL; + my_socket sock; + char *scramble_data; + const char *scramble_plugin; + uint pkt_length, scramble_len, pkt_scramble_len= 0; + NET *net= &mysql->net; +#ifdef _WIN32 + HANDLE hPipe=INVALID_HANDLE_VALUE; +#endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un UNIXaddr; +#endif + time_t start_t= time(NULL); + init_sigpipe_variables + DBUG_ENTER("mysql_real_connect"); + + DBUG_PRINT("enter",("host: %s db: %s user: %s", + host ? host : "(Null)", + db ? db : "(Null)", + user ? user : "(Null)")); + + ma_set_connect_attrs(mysql); + + if (net->vio) /* check if we are already connected */ + { + SET_CLIENT_ERROR(mysql, CR_ALREADY_CONNECTED, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + + /* use default options */ + if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) + { + mysql_read_default_options(&mysql->options, + (mysql->options.my_cnf_file ? + mysql->options.my_cnf_file : "my"), + mysql->options.my_cnf_group); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); + mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; + } + + /* Some empty-string-tests are done because of ODBC */ + if (!host || !host[0]) + host=mysql->options.host; + 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) */ +#endif + } + if (!db || !db[0]) + db=mysql->options.db; + if (!port) + port=mysql->options.port; + if (!unix_socket) + unix_socket=mysql->options.unix_socket; + + + /* Since 5.0.3 reconnect is not enabled by default!! + mysql->reconnect=1; */ + mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + + /* + ** Grab a socket and connect it to the server + */ + +#if defined(HAVE_SYS_UN_H) + if ((!host || strcmp(host,LOCAL_HOST) == 0) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP && + (unix_socket || mysql_unix_port)) + { + host=LOCAL_HOST; + if (!unix_socket) + unix_socket=mysql_unix_port; + host_info=(char*) ER(CR_LOCALHOST_CONNECTION); + DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket)); + if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) + { + net->last_errno=CR_SOCKET_CREATE_ERROR; + sprintf(net->last_error,ER(net->last_errno),socket_errno); + goto error; + } + net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); + bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); + UNIXaddr.sun_family = AF_UNIX; + strmov(UNIXaddr.sun_path, unix_socket); + if (connect_sync_or_async(mysql, net, sock, + (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr))) + { + DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno)); + my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONNECTION_ERROR), + unix_socket, socket_errno); + goto error; + } + if (socket_block(sock, 1) == SOCKET_ERROR) + goto error; + } + else +#elif defined(_WIN32) + { + if ((unix_socket || + (!host && is_NT()) || + (host && strcmp(host,LOCAL_HOST_NAMEDPIPE) == 0) || + mysql->options.named_pipe || + !have_tcpip) && + mysql->options.protocol != MYSQL_PROTOCOL_TCP) + { + sock=0; + if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout, + (char**) &host, (char**) &unix_socket)) == + INVALID_HANDLE_VALUE) + { + DBUG_PRINT("error", + ("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d", + host ? host : "", + unix_socket ? unix_socket : "", + (int) mysql->options.named_pipe, + (int) have_tcpip)); + if (mysql->options.named_pipe || + (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || + (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE))) + goto error; /* User only requested named pipes */ + /* Try also with TCP/IP */ + } + else + { + net->vio=vio_new_win32pipe(hPipe); + sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host, + unix_socket); + } + } + } + if (hPipe == INVALID_HANDLE_VALUE) +#endif + { + struct addrinfo hints, *save_res= 0, *res= 0, + *bind_res= 0, *bres= 0; + char server_port[NI_MAXSERV]; + int gai_rc, bind_gai_rc; + int rc; +#ifdef _WIN32 + DWORD wait_gai; +#else + unsigned int wait_gai; +#endif + + unix_socket=0; /* This is not used */ + if (!port) + port=mysql_port; + if (!host) + host=LOCAL_HOST; + sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host); + bzero(&server_port, NI_MAXSERV); + + DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); + + my_snprintf(server_port, NI_MAXSERV, "%d", port); + + /* set hints for getaddrinfo */ + bzero(&hints, sizeof(hints)); + hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */ + hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */ + hints.ai_socktype= SOCK_STREAM; + + /* if client has multiple interfaces, we will bind socket to given + * bind_address */ + if (mysql->options.bind_address) + { + wait_gai= 1; + while ((bind_gai_rc= getaddrinfo(mysql->options.bind_address, 0, &hints, &bind_res)) == EAI_AGAIN) + { + unsigned int timeout= (mysql->options.connect_timeout) ? mysql->options.connect_timeout : DNS_TIMEOUT; + if (time(NULL) - start_t > timeout) + break; +#ifndef _WIN32 + usleep(wait_gai); +#else + Sleep(wait_gai); +#endif + wait_gai*= 2; + } + if (bind_gai_rc != 0 || !bind_res) + { +#ifndef _WIN32 + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), mysql->options.bind_address, + bind_gai_rc == EAI_SYSTEM ? errno : bind_gai_rc); +#else + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), mysql->options.bind_address, + WSAGetLastError()); +#endif + goto error; + } + } + + + /* Get the address information for the server using getaddrinfo() */ + wait_gai= 1; + while ((gai_rc= getaddrinfo(host, server_port, &hints, &res)) == EAI_AGAIN) + { + unsigned int timeout= (mysql->options.connect_timeout) ? mysql->options.connect_timeout : DNS_TIMEOUT; + if (time(NULL) - start_t > timeout) + break; +#ifndef _WIN32 + usleep(wait_gai); +#else + Sleep(wait_gai); +#endif + wait_gai*= 2; + } + if (gai_rc != 0) + { + if (bind_res) + freeaddrinfo(bind_res); +#ifndef _WIN32 + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), host, + gai_rc == EAI_SYSTEM ? errno : gai_rc); +#else + my_set_error(mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, + ER(CR_UNKNOWN_HOST), host, + WSAGetLastError()); +#endif + goto error; + } + + /* res is a linked list of addresses. If connect to an address fails we will not return + an error, instead we will try the next address */ + for (save_res= res; save_res; save_res= save_res->ai_next) + { + sock= socket(save_res->ai_family, save_res->ai_socktype, + save_res->ai_protocol); + if (sock == SOCKET_ERROR) + /* we do error handling after for loop only for last call */ + continue; + if (bind_res) + { + for (bres= bind_res; bres; bres= bres->ai_next) + { + if (!(rc= bind(sock, bres->ai_addr, (int)bres->ai_addrlen))) + break; + } + if (rc) + { + closesocket(sock); + continue; + } + } + if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, FALSE))) + { + my_set_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + closesocket(sock); + freeaddrinfo(res); + if (bind_res) + freeaddrinfo(bind_res); + goto error; + } + rc= connect_sync_or_async(mysql, net, sock, + save_res->ai_addr, save_res->ai_addrlen); + if (!rc) + { + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + break; + else if (socket_block(sock, 0) == SOCKET_ERROR) + { + closesocket(sock); + continue; + } + break; /* success! */ + } + vio_delete(mysql->net.vio); + mysql->net.vio= NULL; + } + + freeaddrinfo(res); + if (bind_res) + freeaddrinfo(bind_res); + + if (sock == SOCKET_ERROR) + { + my_set_error(mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR), + socket_errno); + goto error; + } + + /* last call to connect 2 failed */ + if (rc) + { + my_set_error(mysql, CR_CONN_HOST_ERROR, SQLSTATE_UNKNOWN, ER(CR_CONN_HOST_ERROR), + host, socket_errno); + goto error; + } + if (socket_block(sock, 1) == SOCKET_ERROR) + goto error; + } + + if (mysql->options.extension && mysql->options.extension->async_context) + net->vio->async_context= mysql->options.extension->async_context; + + if (!net->vio || my_net_init(net, net->vio)) + { + vio_delete(net->vio); + net->vio = 0; + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + vio_keepalive(net->vio,TRUE); + strmov(mysql->net.sqlstate, "00000"); + + + /* set read timeout */ + vio_read_timeout(net->vio, mysql->options.read_timeout); + + /* set write timeout */ + vio_write_timeout(net->vio, mysql->options.write_timeout); + + /* Get version info */ + mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ + if (mysql->options.connect_timeout > 0 && + vio_wait_or_timeout(net->vio, TRUE, mysql->options.connect_timeout * 1000) < 1) + { + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "handshake: waiting for inital communication packet", + errno); + goto error; + } + if ((pkt_length=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 inital communication packet", + errno); + + goto error; + } + end= (char *)net->read_pos; + end_pkt= (char *)net->read_pos + pkt_length; + + /* Check if version of protocoll 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; + } + + DBUG_DUMP("packet",net->read_pos,10); + DBUG_PRINT("info",("mysql protocol version %d, server=%d", + PROTOCOL_VERSION, mysql->protocol_version)); + 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 (!passwd) passwd=""; + + if (!my_multi_malloc(MYF(0), + &mysql->host_info, (uint) strlen(host_info)+1, + &mysql->host, (uint) strlen(host)+1, + &mysql->unix_socket,unix_socket ? + (uint) strlen(unix_socket)+1 : (uint) 1, + &mysql->server_version, (uint) (end - (char*) net->read_pos), + NullS) || + !(mysql->user=my_strdup(user,MYF(0))) || + !(mysql->passwd=my_strdup(passwd,MYF(0)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + strmov(mysql->host_info,host_info); + strmov(mysql->host,host); + if (unix_socket) + strmov(mysql->unix_socket,unix_socket); + else + mysql->unix_socket=0; + mysql->port=port; + client_flag|=mysql->options.client_flag; + + if (strncmp(end, "5.5.5-", 6) == 0) + { + if (!(mysql->server_version= my_strdup(end + 6, 0))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto error; + } + } + else + { + if (!(mysql->server_version= my_strdup(end, MYF(0)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 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|= uint2korr(end + 5) << 16; + pkt_scramble_len= uint1korr(end + 7); + } + /* 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) + scramble_len= (uint)(end_pkt - scramble_data); + } 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, unknown_sqlstate, 0); + goto error; + } + } + + + /* Set character set */ + if (mysql->options.charset_name) + mysql->charset= mysql_find_charset_name(mysql->options.charset_name); + else + mysql->charset=default_charset_info; + + if (!mysql->charset) + { + net->last_errno=CR_CANT_READ_CHARSET; + sprintf(net->last_error,ER(net->last_errno), + charset_name ? charset_name : "unknown", + "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) + net->compress= 1; + + /* last part: select default db */ + if (db && !mysql->db) + { + if (mysql_select_db(mysql, db)) + { + my_set_error(mysql, CR_SERVER_LOST, unknown_sqlstate, + ER(CR_SERVER_LOST_EXTENDED), + "Setting intital database", + errno); + goto error; + } + } + + DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d client_flag: %d", + mysql->server_version,mysql->server_capabilities, + mysql->server_status, client_flag)); + + 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->reconnect; + mysql->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->reconnect= save_reconnect; + } + + strmov(mysql->net.sqlstate, "00000"); + + DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); + reset_sigpipe(mysql); + DBUG_RETURN(mysql); + +error: + reset_sigpipe(mysql); + DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error)); + { + /* Free alloced memory */ + end_server(mysql); + /* only free the allocated memory, user needs to call mysql_close */ + mysql_close_memory(mysql); + if (!(((ulong) client_flag) & CLIENT_REMEMBER_OPTIONS)) + mysql_close_options(mysql); + } + DBUG_RETURN(0); +} + + +static my_bool mysql_reconnect(MYSQL *mysql) +{ + MYSQL tmp_mysql; + LIST *li_stmt= mysql->stmts; + DBUG_ENTER("mysql_reconnect"); + + if (!mysql->reconnect || + (mysql->server_status & SERVER_STATUS_IN_TRANS) || !mysql->host_info) + { + /* Allov reconnect next time */ + mysql->server_status&= ~SERVER_STATUS_IN_TRANS; + my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + mysql_init(&tmp_mysql); + tmp_mysql.options=mysql->options; + + /* don't reread options from configuration files */ + tmp_mysql.options.my_cnf_group= tmp_mysql.options.my_cnf_file= NULL; + + /* make sure that we reconnect with the same character set */ + if (!tmp_mysql.options.charset_name || + strcmp(tmp_mysql.options.charset_name, mysql->charset->csname)) + { + my_free(tmp_mysql.options.charset_name); + tmp_mysql.options.charset_name= my_strdup(mysql->charset->csname, MYF(MY_WME)); + } + + tmp_mysql.reconnect= mysql->reconnect; + 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)) + { + /* 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); + DBUG_RETURN(1); + } + + /* reset the connection in all active statements + todo: check stmt->mysql in mysql_stmt* functions ! */ + 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; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + } + } + + tmp_mysql.free_me= mysql->free_me; + tmp_mysql.stmts= mysql->stmts; + mysql->stmts= NULL; + + /* Don't free options, we moved them to tmp_mysql */ + memset(&mysql->options, 0, sizeof(mysql->options)); + mysql->free_me=0; + mysql->stmts= NULL; + mysql_close(mysql); + *mysql=tmp_mysql; + mysql->reconnect= 1; + net_clear(&mysql->net); + mysql->affected_rows= ~(my_ulonglong) 0; + DBUG_RETURN(0); +} + + +/************************************************************************** +** Change user and database +**************************************************************************/ + +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db) +{ + const CHARSET_INFO *s_cs= mysql->charset; + char *s_user= mysql->user, + *s_passwd= mysql->passwd, + *s_db= mysql->db; + int rc; + + DBUG_ENTER("mysql_change_user"); + + if (!user) + user=""; + if (!passwd) + passwd=""; + if (!db) + db=""; + + if (mysql->options.charset_name) + mysql->charset =mysql_find_charset_name(mysql->options.charset_name); + else + mysql->charset=default_charset_info; + + mysql->user= (char *)user; + mysql->passwd= (char *)passwd; + mysql->db= (char *)db; + rc= run_plugin_auth(mysql, 0, 0, 0, db); + + if (rc==0) + { + LIST *li_stmt= mysql->stmts; + my_free(s_user); + my_free(s_passwd); + my_free(s_db); + + if (!(mysql->user= my_strdup(user,MYF(MY_WME))) || + !(mysql->passwd=my_strdup(passwd,MYF(MY_WME))) || + !(mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + rc= 1; + } + + for (;li_stmt;li_stmt= li_stmt->next) + { + MYSQL_STMT *stmt= (MYSQL_STMT *)li_stmt->data; + stmt->mysql= NULL; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + }/* detach stmts */ + mysql->stmts= NULL; + + } else + { + mysql->user= s_user; + mysql->passwd= s_passwd; + mysql->db= s_db; + mysql->charset= s_cs; + } + DBUG_RETURN(rc); +} + + +/************************************************************************** +** Set current database +**************************************************************************/ + +int STDCALL +mysql_select_db(MYSQL *mysql, const char *db) +{ + int error; + DBUG_ENTER("mysql_select_db"); + DBUG_PRINT("enter",("db: '%s'",db)); + + if ((error=simple_command(mysql,MYSQL_COM_INIT_DB,db,(uint) strlen(db),0,0))) + DBUG_RETURN(error); + my_free(mysql->db); + mysql->db=my_strdup(db,MYF(MY_WME)); + DBUG_RETURN(0); +} + + +/************************************************************************* +** Send a QUIT to the server and close the connection +** If handle is alloced 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++) + my_free(*begin); + delete_dynamic(mysql->options.init_command); + my_free(mysql->options.init_command); + } + my_free(mysql->options.user); + my_free(mysql->options.host); + my_free(mysql->options.password); + my_free(mysql->options.unix_socket); + my_free(mysql->options.db); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); + my_free(mysql->options.charset_dir); + my_free(mysql->options.charset_name); + my_free(mysql->options.bind_address); + my_free(mysql->options.ssl_key); + my_free(mysql->options.ssl_cert); + my_free(mysql->options.ssl_ca); + my_free(mysql->options.ssl_capath); + my_free(mysql->options.ssl_cipher); + if (mysql->options.extension) + { + struct mysql_async_context *ctxt; + my_free(mysql->options.extension->plugin_dir); + my_free(mysql->options.extension->default_auth); + my_free(mysql->options.extension->db_driver); + my_free(mysql->options.extension->ssl_crl); + my_free(mysql->options.extension->ssl_crlpath); + my_free(mysql->options.extension->ssl_fp); + my_free(mysql->options.extension->ssl_fp_list); + if(hash_inited(&mysql->options.extension->connect_attrs)) + hash_free(&mysql->options.extension->connect_attrs); + if (mysql->options.extension && (ctxt = mysql->options.extension->async_context) != 0) + { + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + mysql->options.extension->async_context= 0; + } + + } + my_free(mysql->options.extension); + mysql->options.extension= 0; + /* clear all pointer */ + memset(&mysql->options, 0, sizeof(mysql->options)); +} + +static void mysql_close_memory(MYSQL *mysql) +{ + my_free(mysql->host_info); + my_free(mysql->user); + my_free(mysql->passwd); + my_free(mysql->db); + my_free(mysql->server_version); + mysql->host_info= 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; + + DBUG_ENTER("my_set_error"); + + mysql->net.last_errno= error_nr; + strncpy(mysql->net.sqlstate, sqlstate, SQLSTATE_LENGTH); + va_start(ap, format); + my_vsnprintf(mysql->net.last_error, MYSQL_ERRMSG_SIZE, + format ? format : ER(error_nr), ap); + DBUG_PRINT("info", ("error(%d) %s", error_nr, mysql->net.last_error)); + va_end(ap); + DBUG_VOID_RETURN; +} + +void mysql_close_slow_part(MYSQL *mysql) +{ + if (mysql->net.vio) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + mysql->reconnect=0; + simple_command(mysql,MYSQL_COM_QUIT,NullS,0,1,0); + end_server(mysql); + } +} + +void STDCALL +mysql_close(MYSQL *mysql) +{ + MYSQL_STMT *stmt; + DBUG_ENTER("mysql_close"); + if (mysql) /* Some simple safety */ + { + LIST *li_stmt= mysql->stmts; + + if (mysql->methods) + mysql->methods->db_close(mysql); + + /* reset the connection in all active statements + todo: check stmt->mysql in mysql_stmt* functions ! */ + for (;li_stmt;li_stmt= li_stmt->next) + { + stmt= (MYSQL_STMT *)li_stmt->data; + stmt->mysql= NULL; + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + } + mysql_close_memory(mysql); + mysql_close_options(mysql); + mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + + /* Clear pointers for better safety */ + bzero((char*) &mysql->options,sizeof(mysql->options)); + mysql->net.vio= 0; + if (mysql->free_me) + my_free(mysql); + } + DBUG_VOID_RETURN; +} + + +/************************************************************************** +** Do a query. If query returned rows, free old rows. +** Read data by mysql_store_result or by repeat call of mysql_fetch_row +**************************************************************************/ + +int STDCALL +mysql_query(MYSQL *mysql, const char *query) +{ + return mysql_real_query(mysql,query, (uint) 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 simple_command(mysql, MYSQL_COM_QUERY, query, length, 1,0); +} + +int mthd_my_read_query_result(MYSQL *mysql) +{ + uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + ulong length; + DBUG_ENTER("mthd_my_read_query_result"); + + if (!mysql || (length = net_safe_read(mysql)) == packet_error) + DBUG_RETURN(1); + free_old_query(mysql); /* Free old result */ +get_info: + pos=(uchar*) mysql->net.read_pos; + if ((field_count= net_field_length(&pos)) == 0) + { + mysql->affected_rows= net_field_length_ll(&pos); + mysql->insert_id= net_field_length_ll(&pos); + mysql->server_status=uint2korr(pos); + pos+=2; + mysql->warning_count=uint2korr(pos); + pos+=2; + if (pos < mysql->net.read_pos+length && net_field_length(&pos)) + mysql->info=(char*) pos; + DBUG_RETURN(0); + } + if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ + { + int error= 0; + /* check if a callback was specified for load local infile */ + if (mysql->options.extension && mysql->options.extension->verify_local_infile) + { + if (mysql->options.extension->verify_local_infile(mysql->options.local_infile_userdata[1], (const char *)pos)) + { + my_set_error(mysql, EE_READ, SQLSTATE_UNKNOWN, "filename could not be verified"); + DBUG_RETURN(-1); + } + } + error=mysql_handle_local_infile(mysql, (char *)pos); + + if ((length=net_safe_read(mysql)) == packet_error || error) + DBUG_RETURN(-1); + goto get_info; /* Get info packet */ + } + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) + mysql->server_status|= SERVER_STATUS_IN_TRANS; + + mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ + if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + DBUG_RETURN(-1); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, + (uint) field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(0); +} + +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) +{ + DBUG_ENTER("mysql_real_query"); + DBUG_PRINT("enter",("handle: %lx",mysql)); + DBUG_PRINT("query",("Query = \"%.255s\" length=%u",query, length)); + + free_old_query(mysql); + + if (simple_command(mysql,MYSQL_COM_QUERY,query,length,1,0)) + DBUG_RETURN(-1); + DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); +} + +/************************************************************************** +** 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; + DBUG_ENTER("mysql_store_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + mysql->status=MYSQL_STATUS_READY; /* server is ready */ + if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_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))) + { + my_free(result); + DBUG_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 */ + DBUG_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; + DBUG_ENTER("mysql_use_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_RETURN(0); + } + if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(0); + result->lengths=(ulong*) (result+1); + if (!(result->row=(MYSQL_ROW) + my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) + { /* Ptrs: to one row */ + my_free(result); + DBUG_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; + DBUG_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 next row of the query results +**************************************************************************/ +MYSQL_ROW STDCALL +mysql_fetch_row(MYSQL_RES *res) +{ + DBUG_ENTER("mysql_fetch_row"); + if (!res) + return 0; + if (!res->data) + { /* Unbufferred fetch */ + if (!res->eof) + { + if (!(res->handle->methods->db_read_one_row(res->handle,res->field_count,res->row, res->lengths))) + { + res->row_count++; + DBUG_RETURN(res->current_row=res->row); + } + DBUG_PRINT("info",("end of data")); + res->eof=1; + res->handle->status=MYSQL_STATUS_READY; + /* Don't clear handle in mysql_free_results */ + res->handle=0; + } + DBUG_RETURN((MYSQL_ROW) NULL); + } + { + MYSQL_ROW tmp; + if (!res->data_cursor) + { + DBUG_PRINT("info",("end of data")); + DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL); + } + tmp = res->data_cursor->data; + res->data_cursor = res->data_cursor->next; + DBUG_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, my_ulonglong row) +{ + MYSQL_ROWS *tmp=0; + DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + 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; +} + +/***************************************************************************** +** List all databases +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_dbs(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_dbs"); + + append_wild(strmov(buff,"show databases"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/***************************************************************************** +** List all tables in a database +** If wild is given then only the tables matching wild is returned +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_tables(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_tables"); + + append_wild(strmov(buff,"show tables"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/************************************************************************** +** List all fields in a table +** If wild is given then only the fields matching wild is 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[257],*end; + DBUG_ENTER("mysql_list_fields"); + DBUG_PRINT("enter",("table: '%s' wild: '%s'",table,wild ? wild : "")); + + LINT_INIT(query); + + end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); + if (simple_command(mysql,MYSQL_COM_FIELD_LIST,buff,(uint) (end-buff),1,0) || + !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + DBUG_RETURN(NULL); + + free_old_query(mysql); + if (!(result = (MYSQL_RES *) my_malloc(sizeof(MYSQL_RES), + MYF(MY_WME | MY_ZEROFILL)))) + { + free_rows(query); + DBUG_RETURN(NULL); + } + result->field_alloc=mysql->field_alloc; + mysql->fields=0; + result->field_count = (uint) query->rows; + result->fields= unpack_fields(query,&result->field_alloc, + result->field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)); + result->eof=1; + DBUG_RETURN(result); +} + +/* List all running processes (threads) in server */ + +MYSQL_RES * STDCALL +mysql_list_processes(MYSQL *mysql) +{ + MYSQL_DATA *fields; + uint field_count; + uchar *pos; + DBUG_ENTER("mysql_list_processes"); + + LINT_INIT(fields); + if (simple_command(mysql,MYSQL_COM_PROCESS_INFO,0,0,0,0)) + DBUG_RETURN(0); + 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,5))) + DBUG_RETURN(NULL); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(0); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(mysql_store_result(mysql)); +} + + +int STDCALL +mysql_create_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_createdb"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_CREATE_DB,db, (uint) strlen(db),0,0)); +} + + +int STDCALL +mysql_drop_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_drop_db"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_DROP_DB,db,(uint) strlen(db),0,0)); +} + +/* 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]; + DBUG_ENTER("mysql_shutdown"); + s_level[0]= (uchar)shutdown_level; + DBUG_RETURN(simple_command(mysql, MYSQL_COM_SHUTDOWN, (char *)s_level, 1, 0, 0)); +} + +int STDCALL +mysql_refresh(MYSQL *mysql,uint options) +{ + uchar bits[1]; + DBUG_ENTER("mysql_refresh"); + bits[0]= (uchar) options; + DBUG_RETURN(simple_command(mysql,MYSQL_COM_REFRESH,(char*) bits,1,0,0)); +} + +int STDCALL +mysql_kill(MYSQL *mysql,ulong pid) +{ + char buff[12]; + DBUG_ENTER("mysql_kill"); + int4store(buff,pid); + /* if we kill our own thread, reading the response packet will fail */ + DBUG_RETURN(simple_command(mysql,MYSQL_COM_PROCESS_KILL,buff,4,0,0)); +} + + +int STDCALL +mysql_dump_debug_info(MYSQL *mysql) +{ + DBUG_ENTER("mysql_dump_debug_info"); + DBUG_RETURN(simple_command(mysql,MYSQL_COM_DEBUG,0,0,0,0)); +} + +char * STDCALL +mysql_stat(MYSQL *mysql) +{ + DBUG_ENTER("mysql_stat"); + if (simple_command(mysql,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 , unknown_sqlstate, 0); + return mysql->net.last_error; + } + DBUG_RETURN((char*) mysql->net.read_pos); +} + + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + int rc; + DBUG_ENTER("mysql_ping"); + rc= simple_command(mysql,MYSQL_COM_PING,0,0,0,0); + + DBUG_RETURN(rc); +} + + +char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + +unsigned long STDCALL mysql_get_server_version(MYSQL *mysql) +{ + long 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 (unsigned long)(major * 10000L + (unsigned long)(minor * 100L + patch)); +} + + + +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*) MYSQL_CLIENT_VERSION; +} + +static size_t get_store_length(size_t length) +{ + if (length < (size_t) L64(251)) + return 1; + if (length < (size_t) L64(65536)) + return 2; + if (length < (size_t) L64(16777216)) + return 3; + return 9; +} + +uchar *ma_get_hash_key(const uchar *hash_entry, + unsigned int *length, + my_bool not_used __attribute__((unused))) +{ + /* Hash entry has the following format: + Offset: 0 key length + sizeof(size_t) key + key_length + + sizeof(size_t) value length + value + */ + uchar *p= (uchar *)hash_entry; + size_t len=*((size_t*)p); + p+= sizeof(size_t); + *length= (uint)len; + return p; +} + +void ma_hash_free(void *p) +{ + my_free(p); +} + + +int STDCALL +mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) +{ + va_list ap; + void *arg1; + struct mysql_async_context *ctxt; + size_t stacksize; + + DBUG_ENTER("mysql_option"); + DBUG_PRINT("enter",("option: %d",(int) option)); + + 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_BIND: + my_free(mysql->options.bind_address); + mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); + break; + case MYSQL_OPT_LOCAL_INFILE: /* Allow LOAD DATA LOCAL ?*/ + if (!arg1 || test(*(uint*) arg1)) + mysql->options.client_flag|= CLIENT_LOCAL_FILES; + else + mysql->options.client_flag&= ~CLIENT_LOCAL_FILES; + break; + case MYSQL_INIT_COMMAND: + options_add_initcommand(&mysql->options, (char *)arg1); + break; + case MYSQL_READ_DEFAULT_FILE: + my_free(mysql->options.my_cnf_file); + mysql->options.my_cnf_file=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_READ_DEFAULT_GROUP: + my_free(mysql->options.my_cnf_group); + mysql->options.my_cnf_group=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_SET_CHARSET_DIR: + /* not supported in this version. Since all character sets + are internally available, we don't throw an error */ + break; + case MYSQL_SET_CHARSET_NAME: + my_free(mysql->options.charset_name); + mysql->options.charset_name=my_strdup((char *)arg1,MYF(MY_WME)); + break; + case MYSQL_OPT_RECONNECT: + mysql->reconnect= *(my_bool *)arg1; + break; + case MYSQL_OPT_PROTOCOL: +#ifdef _WIN32 + if (*(uint *)arg1 > MYSQL_PROTOCOL_PIPE) +#else + if (*(uint *)arg1 > MYSQL_PROTOCOL_SOCKET) +#endif + DBUG_RETURN(-1); + mysql->options.protocol= *(uint *)arg1; + break; + 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= *(uint *)arg1; + break; + case MYSQL_PROGRESS_CALLBACK: + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension) + mysql->options.extension->report_progress= + (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg1; + break; + case MYSQL_PLUGIN_DIR: + OPT_SET_EXTENDED_VALUE(&mysql->options, plugin_dir, (char *)arg1, 1); + break; + case MYSQL_DEFAULT_AUTH: + OPT_SET_EXTENDED_VALUE(&mysql->options, default_auth, (char *)arg1, 1); + break; + case MYSQL_DATABASE_DRIVER: + { + MARIADB_DB_PLUGIN *db_plugin; + if (!(db_plugin= (MARIADB_DB_PLUGIN *)mysql_client_find_plugin(mysql, (char *)arg1, + MYSQL_CLIENT_DB_PLUGIN))) + break; + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)); + if (!mysql->options.extension->db_driver) + mysql->options.extension->db_driver= (MARIADB_DB_DRIVER *) + my_malloc(sizeof(MARIADB_DB_DRIVER), MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension && + mysql->options.extension->db_driver) + { + mysql->options.extension->db_driver->plugin = db_plugin; + mysql->options.extension->db_driver->buffer= NULL; + mysql->options.extension->db_driver->name= (char *)db_plugin->name; + mysql->methods= db_plugin->methods; + } + } + 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) + DBUG_RETURN(1); + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + } + if (!(ctxt= (struct mysql_async_context *) + my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + 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)) + { + my_free(ctxt); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + if (!mysql->options.extension) + if(!(mysql->options.extension= (struct st_mysql_options_extension *) + my_malloc(sizeof(struct st_mysql_options_extension), + MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + DBUG_RETURN(1); + } + mysql->options.extension->async_context= ctxt; + if (mysql->net.vio) + mysql->net.vio->async_context= ctxt; + break; + + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + if (*(my_bool *)arg1) + mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT; + else + mysql->options.client_flag &= ~CLIENT_SSL_VERIFY_SERVER_CERT; + break; + case MYSQL_OPT_SSL_KEY: + my_free(mysql->options.ssl_key); + mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CERT: + my_free(mysql->options.ssl_cert); + mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CA: + my_free(mysql->options.ssl_ca); + mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CAPATH: + my_free(mysql->options.ssl_capath); + mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CIPHER: + my_free(mysql->options.ssl_cipher); + mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR)); + break; + case MYSQL_OPT_SSL_CRL: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crl, (char *)arg1, 1); + break; + case MYSQL_OPT_SSL_CRLPATH: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg1, 1); + break; + case MARIADB_OPT_SSL_FP: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp, (char *)arg1, 1); + break; + case MARIADB_OPT_SSL_FP_LIST: + OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp_list, (char *)arg1, 1); + break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + { + uchar *p; + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs) && + (p= (uchar *)hash_search(&mysql->options.extension->connect_attrs, (uchar *)arg1, + arg1 ? (uint)strlen((char *)arg1) : 0))) + { + size_t key_len= *(size_t *)p; + mysql->options.extension->connect_attrs_len-= key_len; + mysql->options.extension->connect_attrs_len-= get_store_length(key_len); + key_len= *(size_t *)(p + sizeof(size_t) + key_len); + mysql->options.extension->connect_attrs_len-= key_len; + mysql->options.extension->connect_attrs_len-= get_store_length(key_len); + hash_delete(&mysql->options.extension->connect_attrs, p); + } + } + break; + case MYSQL_OPT_CONNECT_ATTR_RESET: + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (hash_inited(&mysql->options.extension->connect_attrs)) + { + hash_free(&mysql->options.extension->connect_attrs); + mysql->options.extension->connect_attrs_len= 0; + } + break; + case MYSQL_OPT_CONNECT_ATTR_ADD: + { + uchar *buffer; + void *arg2= va_arg(ap, void *); + size_t key_len= arg1 ? strlen((char *)arg1) : 0, + value_len= arg2 ? strlen((char *)arg2) : 0; + size_t storage_len; + + CHECK_OPT_EXTENSION_SET(&mysql->options); + if (!key_len || !value_len) + { + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0); + goto end; + } + storage_len= key_len + value_len + + get_store_length(key_len) + + get_store_length(value_len); + + if (!hash_inited(&mysql->options.extension->connect_attrs)) + { + if (_hash_init(&mysql->options.extension->connect_attrs, + 0, 0, 0, ma_get_hash_key, ma_hash_free, 0)) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto end; + } + } + if ((buffer= (uchar *)my_malloc(2 * sizeof(size_t) + key_len + value_len, + MYF(MY_WME | MY_ZEROFILL)))) + { + uchar *p= buffer; + *((size_t *)p)= key_len; + p+= sizeof(size_t); + memcpy(p, arg1, key_len); + p+= key_len; + *((size_t *)p)= value_len; + p+= sizeof(size_t); + if (arg2) + memcpy(p, arg2, value_len); + + if (hash_insert(&mysql->options.extension->connect_attrs, buffer)) + { + my_free(buffer); + SET_CLIENT_ERROR(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate, 0); + goto end; + } + mysql->options.extension->connect_attrs_len+= storage_len; + } + else + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0); + goto end; + } + } + break; + case MYSQL_ENABLE_CLEARTEXT_PLUGIN: + break; + case MYSQL_SECURE_AUTH: + mysql->options.secure_auth= *(my_bool *)arg1; + break; + case MARIADB_OPT_VERIFY_LOCAL_INFILE_CALLBACK: + { + void *arg2= va_arg(ap, void *); + CHECK_OPT_EXTENSION_SET(&mysql->options); + mysql->options.extension->verify_local_infile= (int (*)(void *, const char *))arg1; + if (arg2) + mysql->options.local_infile_userdata[1]= arg2; + } + break; + default: + va_end(ap); + DBUG_RETURN(-1); + } + va_end(ap); + DBUG_RETURN(0); +end: + va_end(ap); + DBUG_RETURN(1); +} + + +int STDCALL +mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) +{ + return mysql_optionsv(mysql, option, arg); +} + + +/**************************************************************************** +** 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) +{ + DBUG_ENTER("mysql_autocommit"); + DBUG_RETURN((my_bool) mysql_real_query(mysql, (mode) ? "SET autocommit=1" : + "SET autocommit=0", 16)); +} + +my_bool STDCALL mysql_commit(MYSQL *mysql) +{ + DBUG_ENTER("mysql_commit"); + DBUG_RETURN((my_bool)mysql_real_query(mysql, "COMMIT", sizeof("COMMIT"))); +} + +my_bool STDCALL mysql_rollback(MYSQL *mysql) +{ + DBUG_ENTER("mysql_rollback"); + DBUG_RETURN((my_bool)mysql_real_query(mysql, "ROLLBACK", sizeof("ROLLBACK"))); +} + +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) +{ + return (mysql)->insert_id; +} + +uint STDCALL mysql_errno(MYSQL *mysql) +{ + return (mysql)->net.last_errno; +} + +const char * STDCALL mysql_error(MYSQL *mysql) +{ + return (mysql)->net.last_error; +} + +const char *STDCALL mysql_info(MYSQL *mysql) +{ + return (mysql)->info; +} + +my_bool STDCALL mysql_more_results(MYSQL *mysql) +{ + DBUG_ENTER("mysql_more_results"); + DBUG_RETURN(test(mysql->server_status & SERVER_MORE_RESULTS_EXIST)); +} + +int STDCALL mysql_next_result(MYSQL *mysql) +{ + DBUG_ENTER("mysql_next_result"); + + /* make sure communication is not blocking */ + if (mysql->status != MYSQL_STATUS_READY) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, 0); + DBUG_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) + { + DBUG_RETURN(mysql->methods->db_read_query_result(mysql)); + } + + DBUG_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(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); +} + +void STDCALL +myodbc_remove_escape(MYSQL *mysql,char *name) +{ + char *to; + my_bool use_mb_flag= (mysql->charset->char_maxlen > 1); + char *end= 0; + if (use_mb_flag) + for (end=name; *end ; end++) ; + + for (to=name ; *name ; name++) + { + int l; + if (use_mb_flag && (l = mysql->charset->mb_valid(name , end))) + { + while (l--) + *to++ = *name++; + name--; + continue; + } + if (*name == '\\' && name[1]) + name++; + *to++= *name; + } + *to=0; +} + +void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *cs) +{ + DBUG_ENTER("mysql_get_character_set_info"); + + if (!cs) + DBUG_VOID_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; + + DBUG_VOID_RETURN; +} + +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname) +{ + const CHARSET_INFO *cs; + DBUG_ENTER("mysql_set_character_set"); + + if (!csname) + goto error; + + if ((cs= mysql_find_charset_name(csname))) + { + char buff[64]; + + my_snprintf(buff, 63, "SET NAMES %s", cs->csname); + if (!mysql_real_query(mysql, buff, (uint)strlen(buff))) + { + mysql->charset= cs; + DBUG_RETURN(0); + } + } + +error: + my_set_error(mysql, CR_CANT_READ_CHARSET, SQLSTATE_UNKNOWN, + 0, csname, "compiled_in"); + DBUG_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; +} + +int STDCALL mysql_server_init(int argc __attribute__((unused)), + char **argv __attribute__((unused)), + char **groups __attribute__((unused))) +{ + int rc= 0; + + if (!mysql_client_init) + { + mysql_client_init=1; + my_init(); /* Will init threads */ + init_client_errs(); + if (mysql_client_plugin_init()) + return 1; + if (!mysql_port) + { + struct servent *serv_ptr; + char *env; + + mysql_port = MYSQL_PORT; + if ((serv_ptr = getservbyname("mysql", "tcp"))) + mysql_port = (uint) ntohs((ushort) serv_ptr->s_port); + if ((env = getenv("MYSQL_TCP_PORT"))) + mysql_port =(uint) atoi(env); + } + if (!mysql_unix_port) + { + char *env; +#ifdef _WIN32 + mysql_unix_port = (char*) MYSQL_NAMEDPIPE; +#else + mysql_unix_port = (char*) MYSQL_UNIX_ADDR; +#endif + if ((env = getenv("MYSQL_UNIX_PORT"))) + mysql_unix_port = env; + } + mysql_debug(NullS); +#if defined(SIGPIPE) && !defined(THREAD) && !defined(_WIN32) + (void) signal(SIGPIPE,SIG_IGN); +#endif + } +#ifdef THREAD + else + rc= mysql_thread_init(); +#endif + if (!mysql_ps_subsystem_initialized) + mysql_init_ps_subsystem(); + return(rc); +} + +void STDCALL mysql_server_end() +{ + if (!mysql_client_init) + return; +#ifdef HAVE_OPENSSL + my_ssl_end(); +#endif + + mysql_client_plugin_deinit(); + + if (my_init_done) + my_end(0); + mysql_client_init= 0; + my_init_done= 0; +} + +my_bool STDCALL mysql_thread_init() +{ +#ifdef THREAD + return my_thread_init(); +#endif + return 0; +} + +void STDCALL mysql_thread_end() +{ + #ifdef THREAD + my_thread_end(); + #endif +} + +int STDCALL mysql_set_server_option(MYSQL *mysql, + enum enum_mysql_set_option option) +{ + char buffer[2]; + DBUG_ENTER("mysql_set_server_option"); + int2store(buffer, (uint)option); + DBUG_RETURN(simple_command(mysql, MYSQL_COM_SET_OPTION, buffer, sizeof(buffer), 0, 0)); +} + +ulong STDCALL mysql_get_client_version(void) +{ + return MYSQL_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"; +} + +MYSQL_PARAMETERS *STDCALL +mysql_get_parameters(void) +{ + return &mariadb_internal_parameters; +} + +my_socket STDCALL +mysql_get_socket(const MYSQL *mysql) +{ + if (mysql->net.vio) + return vio_fd(mysql->net.vio); + return MARIADB_INVALID_SOCKET; +} + +/* + * 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_mysql_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 +}; diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/list.c b/mariadb-connector-c-v_2.3.7/libmariadb/list.c new file mode 100644 index 0000000..90f8f7d --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/list.c @@ -0,0 +1,116 @@ +/* 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 */ + +/* + Code for handling dubble-linked lists in C +*/ + +#include "mysys_priv.h" +#include + + + + /* Add a element to start of list */ + +LIST *list_add(LIST *root, LIST *element) +{ + DBUG_ENTER("list_add"); + DBUG_PRINT("enter",("root: %lx element: %lx", root, 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; + DBUG_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) + my_free(root->data); + my_free(root); + root=next; + } +} + + +LIST *list_cons(void *data, LIST *list) +{ + LIST *new_charset=(LIST*) my_malloc(sizeof(LIST),MYF(MY_FAE)); + 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=rest(list); + } + return 0; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/llstr.c b/mariadb-connector-c-v_2.3.7/libmariadb/llstr.c new file mode 100644 index 0000000..a57cf53 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/llstr.c @@ -0,0 +1,36 @@ +/* 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 */ + +/* + Defines: llstr(); + + llstr(value, buff); + + This function saves a longlong value in a buffer and returns the pointer to + the buffer. This is useful when trying to portable print longlong + variables with printf() as there is no usable printf() standard one can use. +*/ + + +#include +#include "m_string.h" + +char *llstr(longlong value,char *buff) +{ + longlong2str(value,buff,-10); + return buff; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/longlong2str.c b/mariadb-connector-c-v_2.3.7/libmariadb/longlong2str.c new file mode 100644 index 0000000..92b090f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/longlong2str.c @@ -0,0 +1,143 @@ +/* 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 */ + +/* + Defines: longlong2str(); + + longlong2str(dst, radix, val) + converts the (longlong) integer "val" to character form and moves it to + the destination string "dst" followed by a terminating NUL. The + result is normally a pointer to this NUL character, but if the radix + is dud the result will be NullS and nothing will be changed. + + If radix is -2..-36, val is taken to be SIGNED. + If radix is 2.. 36, val is taken to be UNSIGNED. + That is, val is signed if and only if radix is. You will normally + use radix -10 only through itoa and ltoa, for radix 2, 8, or 16 + unsigned is what you generally want. + + _dig_vec is public just in case someone has a use for it. + The definitions of itoa and ltoa are actually macros in m_string.h, + but this is where the code is. + + Note: The standard itoa() returns a pointer to the argument, when int2str + returns the pointer to the end-null. + itoa assumes that 10 -base numbers are allways signed and other arn't. +*/ + +#include +#include "m_string.h" + +#if defined(HAVE_LONG_LONG) && !defined(longlong2str) && !defined(HAVE_LONGLONG2STR) + +extern char NEAR _dig_vec[]; + +/* + This assumes that longlong multiplication is faster than longlong division. +*/ + +char *longlong2str(longlong 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 = -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; +} + +#endif + +#ifndef longlong10_to_str +char *longlong10_to_str(longlong val,char *dst,int radix) +{ + char buffer[65]; + register char *p; + long long_val; + + if (radix < 0) + { + if (val < 0) + { + *dst++ = '-'; + val = -val; + } + } + + 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) 10; + uint rem= (uint) (val- quo* (uint) 10); + *--p = _dig_vec[rem]; + val= quo; + } + long_val= (long) val; + while (long_val != 0) + { + long quo= long_val/10; + *--p = _dig_vec[(uchar) (long_val - quo*10)]; + long_val= quo; + } + while ((*dst++ = *p++) != 0) ; + return dst-1; +} +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/ma_dtoa.c b/mariadb-connector-c-v_2.3.7/libmariadb/ma_dtoa.c new file mode 100644 index 0000000..d6634a9 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/ma_dtoa.c @@ -0,0 +1,1925 @@ +/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + 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; 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 /* for EOVERFLOW on Windows */ +#include +#include +#include "m_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= 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= end - res; + + /* + Number of digits in the exponent from the 'e' conversion. + The sign of the exponent is taken into account separetely, 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 representaion 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= 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= 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(WORDS_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(char *)); + + 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(char *)); + 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; + } + 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 }; +static const double tinytens[]= +{ 1e-16, 1e-32, 1e-64, 1e-128, + 9007199254740992.*9007199254740992.e-256 /* = 2^106 * 1e-53 */ +}; +/* + 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 */ + if (((word0(&u) & Exp_mask) == Exp_mask && (*decpt= DTOA_OVERFLOW)) || + (!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; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits= 1; + ilim= ilim1= i= ndigits; + break; + case 3: + leftright= 0; + /* no break */ + 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: + 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/mariadb-connector-c-v_2.3.7/libmariadb/ma_dyncol.c b/mariadb-connector-c-v_2.3.7/libmariadb/ma_dyncol.c new file mode 100644 index 0000000..d8d2eec --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/ma_dyncol.c @@ -0,0 +1,4401 @@ +/* Copyright (c) 2011,2013 Monty Program Ab; + Copyright (c) 2011,2012 Oleksandr Byelkin + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + Numeric format: + =============== + * Fixed header part + 1 byte flags: + 0,1 bits - - 1 + 2-7 bits - 0 + 2 bytes column counter + * Columns directory sorted by column number, each entry contains of: + 2 bytes column number + 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 - - 2 + 2 bit - 1 (means format with names) + 3,4 bits - 00 (means - 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) + (2) bytes size of stored names pool + * Column directory sorted by names, each consists of + (2) bytes offset of name + 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 +#include "mysys_priv.h" +#include +#include +#include +#include +#include + + + +#ifndef LIBMARIADB +uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs, + const char *from, uint32 from_length, + 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, CHARSET_INFO *from_cs, + char *to, size_t *to_len, 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 (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 (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 (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; + } + 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 (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, + 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 (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 (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= mysql_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= my_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 (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 sould 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---->00000!<-hours--><---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---->00000!<-hours--><---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 (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--><---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--> + 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--><---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--> + 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 (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----> + 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----> + 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 */ + } + 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 (dynstr_realloc(str, + fmt->fixed_hdr + + hdr->header_size + + hdr->nmpool_size + + hdr->data_size + + DYNCOL_SYZERESERVE)) + goto err; + } + if (!column_count) + return ER_DYNCOL_OK; + + bzero(str->str, 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; + bzero(&header, sizeof(header)); + header.format= (string_keys ? 1 : 0); + + if (new_str) + { + /* to make dynstr_free() working in case of errors */ + bzero(str, 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) +{ + DBUG_ENTER("dynamic_column_create_many"); + DBUG_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) +{ + DBUG_ENTER("mariadb_dyncol_create_many"); + DBUG_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) +{ + DBUG_ENTER("mariadb_dyncol_create_many_named"); + DBUG_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) +{ + DBUG_ENTER("dynamic_column_create"); + DBUG_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 begining +*/ + +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 withing 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; + bzero(&header, 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; + bzero(&header, 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; + + bzero(array_of_uint, 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 (my_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) 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 *)my_malloc(sizeof(uint) * header.column_count, MYF(0)))) + 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 *)my_malloc(sizeof(LEX_STRING) * header.column_count + + DYNCOL_NUM_CHAR * header.column_count, MYF(0)); + else + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count + + header.nmpool_size + header.column_count, MYF(0)); + 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= + longlong2str(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; + } + bzero(tmp.str, 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 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= 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 nummber */ + 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 nummber */ + 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 nummber */ + 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; + + bzero(&header, sizeof(header)); + bzero(&new_header, 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 *)my_malloc(sizeof(PLAN) * (add_column_count + 1), MYF(0)))) + 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 skiped */ + } + 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: + my_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; + my_free(alloc_plan); + 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; + + DBUG_ENTER("dynamic_column_check"); + + if (str->length == 0) + { + DBUG_PRINT("info", ("empty string is OK")); + DBUG_RETURN(ER_DYNCOL_OK); + } + + bzero(&header, sizeof(header)); + + /* Check that header is OK */ + if (read_fixed_header(&header, str)) + { + DBUG_PRINT("info", ("Reading fixed string header failed")); + 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) + { + DBUG_PRINT("info", ("Fixed header: %u Header size: %u " + "Name pool size: %u but Strig length: %u", + (uint)fmt->fixed_hdr, + (uint)header.header_size, + (uint)header.nmpool_size, + (uint)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= # + 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)) + { + DBUG_PRINT("info", ("Reading name failed: Field order: %u" + " Name offset: %u" + " Name pool size: %u", + (uint) i, + uint2korr(header.entry), + (uint)header.nmpool_size)); + 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) + { + DBUG_PRINT("info", ("Field order: %u Data offset: %u" + " > Data pool size: %u", + (uint)i, + (uint)data_offset, + (uint)header.data_size)); + goto end; + } + if (prev_type != DYN_COL_NULL) + { + /* It is not first entry */ + if (prev_data_offset >= data_offset) + { + DBUG_PRINT("info", ("Field order: %u Previous data offset: %u" + " >= Current data offset: %u", + (uint)i, + (uint)prev_data_offset, + (uint)data_offset)); + goto end; + } + if (prev_name_offset > name_offset) + { + DBUG_PRINT("info", ("Field order: %u Previous name offset: %u" + " > Current name offset: %u", + (uint)i, + (uint)prev_data_offset, + (uint)data_offset)); + goto end; + } + if ((*fmt->column_sort)(&prev_key, &key) >= 0) + { + DBUG_PRINT("info", ("Field order: %u Previous key >= Current key", + (uint)i)); + 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 previouse 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); + DBUG_PRINT("info", ("Field order: %u Can't read data: %i", + (uint)i, (int) rc)); + goto end; + } + } + + rc= ER_DYNCOL_OK; +end: + DBUG_RETURN(rc); +} + +enum enum_dyncol_func_result +mariadb_dyncol_val_str(DYNAMIC_STRING *str, DYNAMIC_COLUMN_VALUE *val, + 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 (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 (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 (dynstr_realloc(str, len + (quote ? 2 : 0))) + return ER_DYNCOL_RESOURCE; + if (quote) + str->str[str->length++]= quote; + 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 (dynstr_realloc(str, bufflen)) + return ER_DYNCOL_RESOURCE; + + // guaranty UTF-8 string for value + if (!conv) + { +#ifndef LIBMARIADB + uint dummy_errors; +#else + int dummy_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, + &dummy_errors); +#else + mariadb_convert_string(from, &len, val->x.string.charset, + str->str, (size_t *)&bufflen, cs, &dummy_errors); +#endif + return ER_DYNCOL_OK; + } + if ((alloc= (char *)my_malloc(bufflen, MYF(0)))) + { + len= +#ifndef LIBMARIADB + copy_and_convert_extended(alloc, bufflen, cs, + from, (uint32)len, + val->x.string.charset, + &dummy_errors); +#else + mariadb_convert_string(from, &len, val->x.string.charset, + alloc, (size_t *)&bufflen, cs, &dummy_errors); +#endif + from= alloc; + } + else + return ER_DYNCOL_RESOURCE; + } + if (quote) + rc= dynstr_append_mem(str, "e, 1); + rc= dynstr_append_mem(str, from, len); + if (quote) + rc= dynstr_append_mem(str, "e, 1); + if (alloc) + my_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 (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 (dynstr_realloc(str, len + (quote ? 2 : 0))) + return ER_DYNCOL_RESOURCE; + if (quote) + str->str[str->length++]= '"'; + dynstr_append_mem(str, buff, len); + if (quote) + str->str[str->length++]= '"'; + break; + case DYN_COL_NULL: + if (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 (val->x.ulong_value > 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++; + } else if (*src == '-') + 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; + } +#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 (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 && 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 withing 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 (dynstr_realloc(json, DYNCOL_NUM_CHAR + 3)) + goto err; + json->str[json->length++]= '"'; + json->length+= (my_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 (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; + bzero(&dc, 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, + my_charset_utf8_general_ci, '"')) < 0) + goto err; + } + } + if (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 (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 *)my_malloc(sizeof(DYNAMIC_COLUMN_VALUE)* header.column_count, MYF(0)); + if (header.format == dyncol_fmt_num) + { + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count + + DYNCOL_NUM_CHAR * header.column_count, MYF(0)); + nm= (char *)((*names) + header.column_count); + } + else + { + *names= (LEX_STRING *)my_malloc(sizeof(LEX_STRING) * header.column_count, MYF(0)); + 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 withing 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) + { + my_free(*vals); + *vals= 0; + } + if (*names) + { + my_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) +{ + dynstr_free(str); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/ma_secure.c b/mariadb-connector-c-v_2.3.7/libmariadb/ma_secure.c new file mode 100644 index 0000000..5dfc981 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/ma_secure.c @@ -0,0 +1,700 @@ +/************************************************************************************ + 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 + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + *************************************************************************************/ +unsigned int mariadb_deinitialize_ssl= 1; +#ifdef HAVE_OPENSSL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static my_bool my_ssl_initialized= FALSE; +static SSL_CTX *SSL_context= NULL; + +#define MAX_SSL_ERR_LEN 100 + +extern pthread_mutex_t LOCK_ssl_config; +static pthread_mutex_t *LOCK_crypto= NULL; + +/* + SSL error handling +*/ +static void my_SSL_error(MYSQL *mysql) +{ + ulong ssl_errno= ERR_get_error(); + char ssl_error[MAX_SSL_ERR_LEN]; + const char *ssl_error_reason; + + DBUG_ENTER("my_SSL_error"); + + if (mysql_errno(mysql)) + DBUG_VOID_RETURN; + + if (!ssl_errno) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); + DBUG_VOID_RETURN; + } + if ((ssl_error_reason= ERR_reason_error_string(ssl_errno))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), ssl_error_reason); + DBUG_VOID_RETURN; + } + my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), ssl_error); + DBUG_VOID_RETURN; +} + +/* + thread safe callbacks for OpenSSL + Crypto call back functions will be + set during ssl_initialization + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000 +#if (OPENSSL_VERSION_NUMBER < 0x10000000) +static unsigned long my_cb_threadid(void) +{ + /* cast pthread_t to unsigned long */ + return (unsigned long) pthread_self(); +} +#else +static void my_cb_threadid(CRYPTO_THREADID *id) +{ + CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self()); +} +#endif + +static void my_cb_locking(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&LOCK_crypto[n]); + else + pthread_mutex_unlock(&LOCK_crypto[n]); +} + + +static int ssl_crypto_init() +{ + int i, rc= 1, max= CRYPTO_num_locks(); + +#if (OPENSSL_VERSION_NUMBER < 0x10000000) + CRYPTO_set_id_callback(my_cb_threadid); +#else + rc= CRYPTO_THREADID_set_callback(my_cb_threadid); +#endif + + /* if someone else already set callbacks + * there is nothing do */ + if (!rc) + return 0; + + if (LOCK_crypto == NULL) + { + if (!(LOCK_crypto= + (pthread_mutex_t *)my_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 + +/* + Initializes SSL and allocate global + context SSL_context + + SYNOPSIS + my_ssl_start + mysql connection handle + + RETURN VALUES + 0 success + 1 error +*/ +int my_ssl_start(MYSQL *mysql) +{ + int rc= 0; + DBUG_ENTER("my_ssl_start"); + /* lock mutex to prevent multiple initialization */ + pthread_mutex_lock(&LOCK_ssl_config); + if (!my_ssl_initialized) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000 + if (ssl_crypto_init()) + goto end; +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + SSL_library_init(); +#if SSLEAY_VERSION_NUMBER >= 0x00907000L + OPENSSL_config(NULL); +#endif +#endif + + /* load errors */ + SSL_load_error_strings(); + /* digests and ciphers */ + OpenSSL_add_all_algorithms(); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (!(SSL_context= SSL_CTX_new(TLS_client_method()))) +#else + if (!(SSL_context= SSL_CTX_new(SSLv23_client_method()))) +#endif + { + my_SSL_error(mysql); + rc= 1; + goto end; + } + /* use server preferences instead of client preferences: + client sends lowest tls version (=1.0) first, and will + update to the version the server sent in client hellp + response packet */ + SSL_CTX_set_options(SSL_context, SSL_OP_ALL); + my_ssl_initialized= TRUE; + } +end: + pthread_mutex_unlock(&LOCK_ssl_config); + DBUG_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 my_ssl_end() +{ + DBUG_ENTER("my_ssl_end"); + pthread_mutex_lock(&LOCK_ssl_config); + if (my_ssl_initialized) + { + int i; + + if (LOCK_crypto) + { + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + for (i=0; i < CRYPTO_num_locks(); i++) + pthread_mutex_destroy(&LOCK_crypto[i]); + + my_free(LOCK_crypto); + LOCK_crypto= NULL; + } + + if (SSL_context) + { + SSL_CTX_free(SSL_context); + SSL_context= FALSE; + } + if (mariadb_deinitialize_ssl) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000 + ERR_remove_state(0); +#endif + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + CONF_modules_free(); + CONF_modules_unload(1); + } + my_ssl_initialized= FALSE; + } + pthread_mutex_unlock(&LOCK_ssl_config); + pthread_mutex_destroy(&LOCK_ssl_config); + DBUG_VOID_RETURN; +} + +/* + Set certification stuff. +*/ +static int my_ssl_set_certs(MYSQL *mysql) +{ + char *certfile= mysql->options.ssl_cert, + *keyfile= mysql->options.ssl_key; + + DBUG_ENTER("my_ssl_set_certs"); + + /* Make sure that ssl was allocated and + ssl_system was initialized */ + DBUG_ASSERT(my_ssl_initialized == TRUE); + + /* add cipher */ + if ((mysql->options.ssl_cipher && + mysql->options.ssl_cipher[0] != 0) && + SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0) + goto error; + + /* ca_file and ca_path */ + if (SSL_CTX_load_verify_locations(SSL_context, + mysql->options.ssl_ca, + mysql->options.ssl_capath) == 0) + { + if (mysql->options.ssl_ca || mysql->options.ssl_capath) + goto error; + if (SSL_CTX_set_default_verify_paths(SSL_context) == 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_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1) + goto error; + + /* set key */ + if (keyfile && keyfile[0]) + { + if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) + goto error; + } + /* verify key */ + if (certfile && !SSL_CTX_check_private_key(SSL_context)) + 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(SSL_context))) + { + if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl, + mysql->options.extension->ssl_crlpath) == 0 || + X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0) + goto error; + } + } + + DBUG_RETURN(0); + +error: + my_SSL_error(mysql); + DBUG_RETURN(1); +} + +static unsigned int ma_get_cert_fingerprint(X509 *cert, EVP_MD *digest, + unsigned char *fingerprint, unsigned int *fp_length) +{ + if (*fp_length < EVP_MD_size(digest)) + return 0; + if (!X509_digest(cert, digest, fingerprint, fp_length)) + return 0; + return *fp_length; +} + +static my_bool ma_check_fingerprint(char *fp1, unsigned int fp1_len, + char *fp2, unsigned int fp2_len) +{ + /* SHA1 fingerprint (160 bit) / 8 * 2 + 1 */ + char hexstr[41]; + + fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); +#ifdef _WIN32 + if (_strnicmp(hexstr, fp2, fp1_len) != 0) +#else + if (strncasecmp(hexstr, fp2, fp1_len) != 0) +#endif + return 1; + return 0; +} + +/* + allocates a new ssl object + + SYNOPSIS + my_ssl_init + mysql connection object + + RETURN VALUES + NULL on error + SSL new SSL object +*/ +SSL *my_ssl_init(MYSQL *mysql) +{ + SSL *ssl= NULL; + + DBUG_ENTER("my_ssl_init"); + + DBUG_ASSERT(mysql->net.vio->ssl == NULL); + + if (!my_ssl_initialized) + my_ssl_start(mysql); + + pthread_mutex_lock(&LOCK_ssl_config); + if (my_ssl_set_certs(mysql)) + goto error; + + if (!(ssl= SSL_new(SSL_context))) + goto error; + + if (!SSL_set_app_data(ssl, mysql)) + goto error; + + pthread_mutex_unlock(&LOCK_ssl_config); + DBUG_RETURN(ssl); +error: + pthread_mutex_unlock(&LOCK_ssl_config); + if (ssl) + SSL_free(ssl); + DBUG_RETURN(NULL); +} + +/* + establish SSL connection between client + and server + + SYNOPSIS + my_ssl_connect + ssl ssl object + + RETURN VALUES + 0 success + 1 error +*/ +int my_ssl_connect(SSL *ssl) +{ + my_bool blocking; + MYSQL *mysql; + long rc; + my_bool try_connect= 1; + + DBUG_ENTER("my_ssl_connect"); + + DBUG_ASSERT(ssl != NULL); + + mysql= (MYSQL *)SSL_get_app_data(ssl); + CLEAR_CLIENT_ERROR(mysql); + + /* Set socket to non blocking */ + if (!(blocking= vio_is_blocking(mysql->net.vio))) + vio_blocking(mysql->net.vio, FALSE, 0); + + SSL_clear(ssl); + SSL_SESSION_set_timeout(SSL_get_session(ssl), + mysql->options.connect_timeout); + SSL_set_fd(ssl, mysql->net.vio->sd); + + while (try_connect && (rc= SSL_connect(ssl)) == -1) + { + switch(SSL_get_error(ssl, rc)) { + case SSL_ERROR_WANT_READ: + if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1) + try_connect= 0; + break; + case SSL_ERROR_WANT_WRITE: + if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1) + try_connect= 0; + break; + default: + try_connect= 0; + } + } + if (rc != 1) + { + my_SSL_error(mysql); + DBUG_RETURN(1); + } + + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) + { + rc= SSL_get_verify_result(ssl); + if (rc != X509_V_OK) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc)); + /* restore blocking mode */ + if (!blocking) + vio_blocking(mysql->net.vio, FALSE, 0); + + DBUG_RETURN(1); + } + } + + vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0); + mysql->net.vio->ssl= ssl; + DBUG_RETURN(0); +} + +int ma_ssl_verify_fingerprint(SSL *ssl) +{ + X509 *cert= SSL_get_peer_certificate(ssl); + MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl); + unsigned char fingerprint[EVP_MAX_MD_SIZE]; + EVP_MD *digest; + unsigned int fp_length; + + DBUG_ENTER("ma_ssl_verify_fingerprint"); + + if (!cert) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + DBUG_RETURN(1); + } + + digest= (EVP_MD *)EVP_sha1(); + fp_length= sizeof(fingerprint); + + if (!ma_get_cert_fingerprint(cert, digest, fingerprint, &fp_length)) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get finger print of server certificate"); + DBUG_RETURN(1); + } + + /* single finger print was specified */ + if (mysql->options.extension->ssl_fp) + { + if (ma_check_fingerprint(fingerprint, fp_length, mysql->options.extension->ssl_fp, + strlen(mysql->options.extension->ssl_fp))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "invalid finger print of server certificate"); + DBUG_RETURN(1); + } + } + + /* white list of finger prints was specified */ + if (mysql->options.extension->ssl_fp_list) + { + FILE *fp; + char buff[255]; + + if (!(fp = my_fopen(mysql->options.extension->ssl_fp_list ,O_RDONLY, MYF(0)))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Can't open finger print list"); + DBUG_RETURN(1); + } + + while (fgets(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_check_fingerprint(fingerprint, fp_length, buff, strlen(buff))) + { + /* finger print is valid: close file and exit */ + my_fclose(fp, MYF(0)); + DBUG_RETURN(0); + } + } + + /* No finger print matched - close file and return error */ + my_fclose(fp, MYF(0)); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "invalid finger print of server certificate"); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +/* + verify server certificate + + SYNOPSIS + my_ssl_verify_server_cert() + MYSQL mysql + mybool verify_server_cert; + + RETURN VALUES + 1 Error + 0 OK +*/ + +int my_ssl_verify_server_cert(SSL *ssl) +{ + X509 *cert; + MYSQL *mysql; + X509_NAME *x509sn; + int cn_pos; + X509_NAME_ENTRY *cn_entry; + ASN1_STRING *cn_asn1; + const char *cn_str; + + DBUG_ENTER("my_ssl_verify_server_cert"); + + mysql= (MYSQL *)SSL_get_app_data(ssl); + + if (!mysql->host) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Invalid (empty) hostname"); + DBUG_RETURN(1); + } + + if (!(cert= SSL_get_peer_certificate(ssl))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + DBUG_RETURN(1); + } + + 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; + + X509_free(cert); + + DBUG_RETURN(0); + +error: + X509_free(cert); + + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Validation of SSL server certificate failed"); + DBUG_RETURN(1); +} +/* + write to ssl socket + + SYNOPSIS + my_ssl_write() + vio vio + buf write buffer + size size of buffer + + RETURN VALUES + bytes written +*/ +size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size) +{ + size_t written; + DBUG_ENTER("my_ssl_write"); + if (vio->async_context && vio->async_context->active) + written= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl, buf, + size); + else + written= SSL_write((SSL*) vio->ssl, buf, size); + DBUG_RETURN(written); +} + +/* + read from ssl socket + + SYNOPSIS + my_ssl_read() + vio vio + buf read buffer + size_t max number of bytes to read + + RETURN VALUES + number of bytes read +*/ +size_t my_ssl_read(Vio *vio, uchar* buf, size_t size) +{ + size_t read; + DBUG_ENTER("my_ssl_read"); + + if (vio->async_context && vio->async_context->active) + read= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl, buf, size); + else + read= SSL_read((SSL*) vio->ssl, buf, size); + DBUG_RETURN(read); +} + +/* + close ssl connection and free + ssl object + + SYNOPSIS + my_ssl_close() + vio vio + + RETURN VALUES + 1 ok + 0 or -1 on error +*/ +int my_ssl_close(Vio *vio) +{ + int i, rc; + DBUG_ENTER("my_ssl_close"); + + if (!vio || !vio->ssl) + DBUG_RETURN(1); + + SSL_set_quiet_shutdown(vio->ssl, 1); + /* 2 x pending + 2 * data = 4 */ + for (i=0; i < 4; i++) + if ((rc= SSL_shutdown(vio->ssl))) + break; + + SSL_free(vio->ssl); + vio->ssl= NULL; + + DBUG_RETURN(rc); +} + +#endif /* HAVE_OPENSSL */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/ma_time.c b/mariadb-connector-c-v_2.3.7/libmariadb/ma_time.c new file mode 100644 index 0000000..0b75506 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/ma_time.c @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (C) 2013 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 + 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 "mysys_priv.h" +#include +#include +#include + + +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= MIN((tm->second_part) ? SEC_PART_DIGITS : 0, 15); + + 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/mariadb-connector-c-v_2.3.7/libmariadb/mf_dirname.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_dirname.c new file mode 100644 index 0000000..0e3602b --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_dirname.c @@ -0,0 +1,100 @@ +/* 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 */ + +#include "mysys_priv.h" +#include + + /* Functions definied in this file */ + +uint dirname_length(const char *name) +{ + register my_string pos,gpos; +#ifdef FN_DEVCHAR + if ((pos=(char*)strrchr(name,FN_DEVCHAR)) == 0) +#endif + pos=(char*) name-1; + + gpos= pos++; + for ( ; *pos ; pos++) /* Find last FN_LIBCHAR */ + if (*pos == FN_LIBCHAR || *pos == '/' +#ifdef FN_C_AFTER_DIR + || *pos == FN_C_AFTER_DIR || *pos == FN_C_AFTER_DIR_2 +#endif + ) + gpos=pos; + return ((uint) (uint) (gpos+1-(char*) name)); +} + + + /* Gives directory part of filename. Directory ends with '/' */ + /* Returns length of directory part */ + +uint dirname_part(my_string to, const char *name) +{ + uint length; + DBUG_ENTER("dirname_part"); + DBUG_PRINT("enter",("'%s'",name)); + + length=dirname_length(name); + (void) strmake(to,(char*) name,min(length,FN_REFLEN-2)); + convert_dirname(to); /* Convert chars */ + DBUG_RETURN(length); +} /* dirname */ + + + /* convert dirname to use under this system */ + /* If MSDOS converts '/' to '\' */ + /* If VMS converts '<' to '[' and '>' to ']' */ + /* Adds a '/' to end if there isn't one and the last isn't a dev_char */ + /* ARGSUSED */ + +#ifndef FN_DEVCHAR +#define FN_DEVCHAR '\0' /* For easier code */ +#endif + +char *convert_dirname(my_string to) +{ + reg1 char *pos; +#if FN_LIBCHAR != '/' + { + pos=to-1; /* Change from '/' */ + while ((pos=strchr(pos+1,'/')) != 0) + *pos=FN_LIBCHAR; + } +#endif +#ifdef FN_C_BEFORE_DIR_2 + { + for (pos=to ; *pos ; pos++) + { + if (*pos == FN_C_BEFORE_DIR_2) + *pos=FN_C_BEFORE_DIR; + if (*pos == FN_C_AFTER_DIR_2) + *pos=FN_C_AFTER_DIR; + } + } +#else + { /* Append FN_LIBCHAR if not there */ + pos=strend(to); + if (pos != to && (pos[-1] != FN_LIBCHAR && pos[-1] != FN_DEVCHAR)) + { + *pos++=FN_LIBCHAR; + *pos=0; + } + } +#endif + return pos; /* Pointer to end of dir */ +} /* convert_dirname */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_fn_ext.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_fn_ext.c new file mode 100644 index 0000000..72617e0 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_fn_ext.c @@ -0,0 +1,46 @@ +/* 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 */ + +/* Returnerar en pekare till filnamnets extension. */ + +#include "mysys_priv.h" +#include + + /* Return a pointerto the extension of the filename + The pointer points at the extension character (normally '.')) + If there isn't any extension, the pointer points at the end + NULL of the filename + */ + +my_string fn_ext(const char *name) +{ + register my_string pos,gpos; + DBUG_ENTER("fn_ext"); + DBUG_PRINT("mfunkt",("name: '%s'",name)); + +#if defined(FN_DEVCHAR) || defined(FN_C_AFTER_DIR) + { + char buff[FN_REFLEN]; + gpos=(my_string) name+dirname_part(buff,(char*) name); + } +#else + if (!(gpos=strrchr(name,FNLIBCHAR))) + gpos=name; +#endif + pos=strrchr(gpos,FN_EXTCHAR); + DBUG_RETURN (pos ? pos : strend(gpos)); +} /* fn_ext */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_format.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_format.c new file mode 100644 index 0000000..4e741e9 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_format.c @@ -0,0 +1,156 @@ +/* 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 */ + +#include "mysys_priv.h" +#include +#ifdef HAVE_REALPATH +#include +#include +#endif + + /* format a filename with replace of library and extension */ + /* params to and name may be identicall */ + /* function doesn't change name if name != to */ + /* Flag may be: 1 replace filenames library with 'dsk' */ + /* 2 replace extension with 'form' */ + /* 4 Unpack filename (replace ~ with home) */ + /* 8 Pack filename as short as possibly */ + /* 16 Resolve symbolic links for filename */ + /* 32 Resolve filename to full path */ + /* 64 Return NULL if too long path */ + +#ifdef SCO +#define BUFF_LEN 4097 +#else +#ifdef MAXPATHLEN +#define BUFF_LEN MAXPATHLEN +#else +#define BUFF_LEN FN_LEN +#endif +#endif + +my_string fn_format(my_string to, const char *name, const char *dsk, + const char *form, int flag) +{ + reg1 uint length; + char dev[FN_REFLEN], buff[BUFF_LEN], *pos, *startpos; + const char *ext; + DBUG_ENTER("fn_format"); + DBUG_PRINT("enter",("name: %s dsk: %s form: %s flag: %d", + name,dsk,form,flag)); + + /* Kopiera & skippa enheten */ + name+=(length=dirname_part(dev,(startpos=(my_string) name))); + if (length == 0 || flag & 1) + { + (void) strmake(dev,dsk, sizeof(dev) - 2); + /* Use given directory */ + convert_dirname(dev); /* Fix to this OS */ + } + if (flag & 8) + pack_dirname(dev,dev); /* Put in ./.. and ~/.. */ + if (flag & 4) + (void) unpack_dirname(dev,dev); /* Replace ~/.. with dir */ + if ((pos=(char*)strchr(name,FN_EXTCHAR)) != NullS) + { + if ((flag & 2) == 0) /* Skall vi byta extension ? */ + { + length=strlength(name); /* Old extension */ + ext = ""; + } + else + { + length=(uint) (pos-(char*) name); /* Change extension */ + ext= form; + } + } + else + { + length=strlength(name); /* Har ingen ext- tag nya */ + ext=form; + } + + if (strlen(dev)+length+strlen(ext) >= FN_REFLEN || length >= FN_LEN ) + { /* To long path, return original */ + uint tmp_length; + if (flag & 64) + return 0; + tmp_length=strlength(startpos); + DBUG_PRINT("error",("dev: '%s' ext: '%s' length: %d",dev,ext,length)); + (void) strmake(to,startpos,min(tmp_length,FN_REFLEN-1)); + } + else + { + if (to == startpos) + { + bmove(buff,(char*) name,length); /* Save name for last copy */ + name=buff; + } + pos=strmake(strmov(to,dev),name,length); +#ifdef FN_UPPER_CASE + caseup_str(to); +#endif +#ifdef FN_LOWER_CASE + casedn_str(to); +#endif + (void) strmov(pos,ext); /* Don't convert extension */ + } + /* Purify gives a lot of UMR errors when using realpath */ +#if defined(HAVE_REALPATH) && !defined(HAVE_purify) && !defined(HAVE_BROKEN_REALPATH) + if (flag & 16) + { + struct stat stat_buff; + if (flag & 32 || (!lstat(to,&stat_buff) && S_ISLNK(stat_buff.st_mode))) + { + if (realpath(to,buff)) + strmake(to,buff,FN_REFLEN-1); + } + } +#endif + DBUG_RETURN (to); +} /* fn_format */ + + + /* + strlength(const string str) + Return length of string with end-space:s not counted. + */ + +size_s strlength(const char *str) +{ + reg1 my_string pos; + reg2 my_string found; + DBUG_ENTER("strlength"); + + pos=found=(char*) str; + + while (*pos) + { + if (*pos != ' ') + { + while (*++pos && *pos != ' ') {}; + if (!*pos) + { + found=pos; /* String ends here */ + break; + } + } + found=pos; + while (*++pos == ' ') {}; + } + DBUG_RETURN((size_s) (found-(char*) str)); +} /* strlength */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_loadpath.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_loadpath.c new file mode 100644 index 0000000..01d523e --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_loadpath.c @@ -0,0 +1,54 @@ +/* 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 */ + +#include "mysys_priv.h" +#include + + /* Returns full load-path for a file. to may be = path */ + /* if path is a hard-path return path */ + /* if path starts with home-dir return path */ + /* if path starts with current dir or parent-dir unpack path */ + /* if there is no path, prepend with own_path_prefix if given */ + /* else unpack path according to current dir */ + +my_string my_load_path(my_string to, const char *path, + const char *own_path_prefix) +{ + char buff[FN_REFLEN]; + DBUG_ENTER("my_load_path"); + DBUG_PRINT("enter",("path: %s prefix: %s",path, + own_path_prefix ? own_path_prefix : "")); + + if ((path[0] == FN_HOMELIB && path[1] == FN_LIBCHAR) || + test_if_hard_path(path)) + VOID(strmov(buff,path)); + else if ((path[0] == FN_CURLIB && path[1] == FN_LIBCHAR) || + (is_prefix((gptr) path,FN_PARENTDIR) && + path[strlen(FN_PARENTDIR)] == FN_LIBCHAR) || + ! own_path_prefix) + { + if (! my_getwd(buff,(uint) (FN_REFLEN-strlen(path)),MYF(0))) + VOID(strcat(buff,path)); + else + VOID(strmov(buff,path)); + } + else + VOID(strxmov(buff,own_path_prefix,path,NullS)); + strmov(to,buff); + DBUG_PRINT("exit",("to: %s",to)); + DBUG_RETURN(to); +} /* my_load_path */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_pack.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_pack.c new file mode 100644 index 0000000..d97e93b --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_pack.c @@ -0,0 +1,532 @@ +/* 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 */ + +#include "mysys_priv.h" +#include +#ifdef HAVE_PWD_H +#include +#endif +#ifdef VMS +#include +#include +#include +#endif /* VMS */ + +static my_string NEAR_F expand_tilde(my_string *path); + + /* Pack a dirname ; Changes HOME to ~/ and current dev to ./ */ + /* from is a dirname (from dirname() ?) ending with FN_LIBCHAR */ + /* to may be == from */ + +void pack_dirname(my_string to, const char *from) +{ + int cwd_err; + uint d_length,length,buff_length= 0; + my_string start; + char buff[FN_REFLEN]; + DBUG_ENTER("pack_dirname"); + + (void) intern_filename(to,from); /* Change to intern name */ + +#ifdef FN_DEVCHAR + if ((start=strrchr(to,FN_DEVCHAR)) != 0) /* Skipp device part */ + start++; + else +#endif + start=to; + + LINT_INIT(buff_length); + if (!(cwd_err= my_getwd(buff,FN_REFLEN,MYF(0)))) + { + buff_length= (uint) strlen(buff); + d_length=(uint) (start-to); + if ((start == to || + (buff_length == d_length && !bcmp(buff,start,d_length))) && + *start != FN_LIBCHAR && *start) + { /* Put current dir before */ + bchange(to,d_length,buff,buff_length,(uint) strlen(to)+1); + } + } + + if ((d_length= cleanup_dirname(to,to)) != 0) + { + length=0; + if (home_dir) + { + length= (uint) strlen(home_dir); + if (home_dir[length-1] == FN_LIBCHAR) + length--; /* Don't test last '/' */ + } + if (length > 1 && length < d_length) + { /* test if /xx/yy -> ~/yy */ + if (bcmp(to,home_dir,length) == 0 && to[length] == FN_LIBCHAR) + { + to[0]=FN_HOMELIB; /* Filename begins with ~ */ + (void) strmov_overlapp(to+1,to+length); + } + } + if (! cwd_err) + { /* Test if cwd is ~/... */ + if (length > 1 && length < buff_length) + { + if (bcmp(buff,home_dir,length) == 0 && buff[length] == FN_LIBCHAR) + { + buff[0]=FN_HOMELIB; + (void) strmov_overlapp(buff+1,buff+length); + } + } + if (is_prefix(to,buff)) + { + length= (uint) strlen(buff); + if (to[length]) + (void) strmov_overlapp(to,to+length); /* Remove everything before */ + else + { + to[0]= FN_CURLIB; /* Put ./ instead of cwd */ + to[1]= FN_LIBCHAR; + to[2]= '\0'; + } + } + } + } + DBUG_PRINT("exit",("to: '%s'",to)); + DBUG_VOID_RETURN; +} /* pack_dirname */ + + + /* remove unwanted chars from dirname */ + /* if "/../" removes prev dir; "/~/" removes all before ~ */ + /* "//" is same as "/", except on Win32 at start of a file */ + /* "/./" is removed */ + /* Unpacks home_dir if "~/.." used */ + /* Unpacks current dir if if "./.." used */ + +uint cleanup_dirname(register my_string to, const char *from) + /* to may be == from */ + +{ + reg5 uint length; + reg2 my_string pos; + reg3 my_string from_ptr; + reg4 my_string start; + char parent[5], /* for "FN_PARENTDIR" */ + buff[FN_REFLEN+1],*end_parentdir; + DBUG_ENTER("cleanup_dirname"); + DBUG_PRINT("enter",("from: '%s'",from)); + + start=buff; + from_ptr=(my_string) from; +#ifdef FN_DEVCHAR + if ((pos=strrchr(from_ptr,FN_DEVCHAR)) != 0) + { /* Skipp device part */ + length=(uint) (pos-from_ptr)+1; + start=strnmov(buff,from_ptr,length); from_ptr+=length; + } +#endif + + parent[0]=FN_LIBCHAR; + length=(uint) (strmov(parent+1,FN_PARENTDIR)-parent); + for (pos=start ; (*pos= *from_ptr++) != 0 ; pos++) + { + if (*pos == '/') + *pos = FN_LIBCHAR; + if (*pos == FN_LIBCHAR) + { + if ((uint) (pos-start) > length && bcmp(pos-length,parent,length) == 0) + { /* If .../../; skipp prev */ + pos-=length; + if (pos != start) + { /* not /../ */ + pos--; + if (*pos == FN_HOMELIB && (pos == start || pos[-1] == FN_LIBCHAR)) + { + if (!home_dir) + { + pos+=length+1; /* Don't unpack ~/.. */ + continue; + } + pos=strmov(buff,home_dir)-1; /* Unpacks ~/.. */ + if (*pos == FN_LIBCHAR) + pos--; /* home ended with '/' */ + } + if (*pos == FN_CURLIB && (pos == start || pos[-1] == FN_LIBCHAR)) + { + if (my_getwd(curr_dir,FN_REFLEN,MYF(0))) + { + pos+=length+1; /* Don't unpack ./.. */ + continue; + } + pos=strmov(buff,curr_dir)-1; /* Unpacks ./.. */ + if (*pos == FN_LIBCHAR) + pos--; /* home ended with '/' */ + } + end_parentdir=pos; + while (pos >= start && *pos != FN_LIBCHAR) /* remove prev dir */ + pos--; + if (pos[1] == FN_HOMELIB || bcmp(pos,parent,length) == 0) + { /* Don't remove ~user/ */ + pos=strmov(end_parentdir+1,parent); + *pos=FN_LIBCHAR; + continue; + } + } + } + else if ((uint) (pos-start) == length-1 && + !bcmp(start,parent+1,length-1)) + start=pos; /* Starts with "../" */ + else if (pos-start > 0 && pos[-1] == FN_LIBCHAR) + { +#ifdef FN_NETWORK_DRIVES + if (pos-start != 1) +#endif + pos--; /* Remove dupplicate '/' */ + } + else if (pos-start > 1 && pos[-1] == FN_CURLIB && pos[-2] == FN_LIBCHAR) + pos-=2; /* Skipp /./ */ + else if (pos > buff+1 && pos[-1] == FN_HOMELIB && pos[-2] == FN_LIBCHAR) + { /* Found ..../~/ */ + buff[0]=FN_HOMELIB; + buff[1]=FN_LIBCHAR; + start=buff; pos=buff+1; + } + } + } + (void) strmov(to,buff); + DBUG_PRINT("exit",("to: '%s'",to)); + DBUG_RETURN((uint) (pos-buff)); +} /* cleanup_dirname */ + + + /* + On system where you don't have symbolic links, the following + code will allow you to create a file: + directory-name.lnk that should contain the real path + to the directory. This will be used if the directory name + doesn't exists + */ + + +my_bool my_use_symdir=0; /* Set this if you want to use symdirs */ + +#ifdef USE_SYMDIR +void symdirget(char *dir) +{ + char buff[FN_REFLEN]; + char *pos=strend(dir); + if (dir[0] && pos[-1] != FN_DEVCHAR && access(dir, F_OK)) + { + FILE *fp; + char temp= *(--pos); /* May be "/" or "\" */ + strmov(pos,".sym"); + fp = my_fopen(dir, O_RDONLY,MYF(0)); + *pos++=temp; *pos=0; /* Restore old filename */ + if (fp) + { + if (fgets(buff, sizeof(buff)-1, fp)) + { + for (pos=strend(buff); + pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ; + pos --); + + /* Ensure that the symlink ends with the directory symbol */ + if (pos == buff || pos[-1] != FN_LIBCHAR) + *pos++=FN_LIBCHAR; + + strmake(dir,buff, (uint) (pos-buff)); + } + my_fclose(fp,MYF(0)); + } + } +} +#endif /* USE_SYMDIR */ + + /* Unpacks dirname to name that can be used by open... */ + /* Make that last char of to is '/' if from not empty and + from doesn't end in FN_DEVCHAR */ + /* Uses cleanup_dirname and changes ~/.. to home_dir/.. */ + /* Returns length of new directory */ + +uint unpack_dirname(my_string to, const char *from) + + /* to may be == from */ +{ + uint length,h_length; + char buff[FN_REFLEN+1+4],*suffix,*tilde_expansion; + DBUG_ENTER("unpack_dirname"); + + (void) intern_filename(buff,from); /* Change to intern name */ + length= (uint) strlen(buff); /* Fix that '/' is last */ + if (length && +#ifdef FN_DEVCHAR + buff[length-1] != FN_DEVCHAR && +#endif + buff[length-1] != FN_LIBCHAR && buff[length-1] != '/') + { + buff[length]=FN_LIBCHAR; + buff[length+1]= '\0'; + } + + length=cleanup_dirname(buff,buff); + if (buff[0] == FN_HOMELIB) + { + suffix=buff+1; tilde_expansion=expand_tilde(&suffix); + if (tilde_expansion) + { + length-=(uint) (suffix-buff)-1; + if (length+(h_length= (uint) strlen(tilde_expansion)) <= FN_REFLEN) + { + if (tilde_expansion[h_length-1] == FN_LIBCHAR) + h_length--; + if (buff+h_length < suffix) + bmove(buff+h_length,suffix,length); + else + bmove_upp(buff+h_length+length,suffix+length,length); + bmove(buff,tilde_expansion,h_length); + } + } + } +#ifdef USE_SYMDIR + if (my_use_symdir) + symdirget(buff); +#endif + DBUG_RETURN(system_filename(to,buff)); /* Fix for open */ +} /* unpack_dirname */ + + + /* Expand tilde to home or user-directory */ + /* Path is reset to point at FN_LIBCHAR after ~xxx */ + +static my_string NEAR_F expand_tilde(my_string *path) +{ + if (path[0][0] == FN_LIBCHAR) + return home_dir; /* ~/ expanded to home */ +#ifdef HAVE_GETPWNAM + { + char *str,save; + struct passwd *user_entry; + + if (!(str=strchr(*path,FN_LIBCHAR))) + str=strend(*path); + save= *str; *str= '\0'; + user_entry=getpwnam(*path); + *str=save; + endpwent(); + if (user_entry) + { + *path=str; + return user_entry->pw_dir; + } + } +#endif + return (my_string) 0; +} + + /* fix filename so it can be used by open, create .. */ + /* to may be == from */ + /* Returns to */ + +my_string unpack_filename(my_string to, const char *from) +{ + uint length,n_length; + char buff[FN_REFLEN]; + DBUG_ENTER("unpack_filename"); + + length=dirname_part(buff,from); /* copy & convert dirname */ + n_length=unpack_dirname(buff,buff); + if (n_length+strlen(from+length) < FN_REFLEN) + { + (void) strmov(buff+n_length,from+length); + (void) system_filename(to,buff); /* Fix to usably filename */ + } + else + (void) system_filename(to,from); /* Fix to usably filename */ + DBUG_RETURN(to); +} /* unpack_filename */ + + + /* Convert filename (unix standard) to system standard */ + /* Used before system command's like open(), create() .. */ + /* Returns to */ + +uint system_filename(my_string to, const char *from) +{ +#ifndef FN_C_BEFORE_DIR + return (uint) (strmake(to,from,FN_REFLEN-1)-to); +#else /* VMS */ + + /* change 'dev:lib/xxx' to 'dev:[lib]xxx' */ + /* change 'dev:xxx' to 'dev:xxx' */ + /* change './xxx' to 'xxx' */ + /* change './lib/' or lib/ to '[.lib]' */ + /* change '/x/y/z to '[x.y]x' */ + /* change 'dev:/x' to 'dev:[000000]x' */ + + int libchar_found,length; + my_string to_pos,from_pos,pos; + char buff[FN_REFLEN]; + DBUG_ENTER("system_filename"); + + libchar_found=0; + (void) strmov(buff,from); /* If to == from */ + from_pos= buff; + if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */ + { + pos++; + to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos)); + from_pos=pos; + } + else + to_pos=to; + + if (from_pos[0] == FN_CURLIB && from_pos[1] == FN_LIBCHAR) + from_pos+=2; /* Skipp './' */ + if (strchr(from_pos,FN_LIBCHAR)) + { + *(to_pos++) = FN_C_BEFORE_DIR; + if (strinstr(from_pos,FN_ROOTDIR) == 1) + { + from_pos+=strlen(FN_ROOTDIR); /* Actually +1 but... */ + if (! strchr(from_pos,FN_LIBCHAR)) + { /* No dir, use [000000] */ + to_pos=strmov(to_pos,FN_C_ROOT_DIR); + libchar_found++; + } + } + else + *(to_pos++)=FN_C_DIR_SEP; /* '.' gives current dir */ + + while ((pos=strchr(from_pos,FN_LIBCHAR))) + { + if (libchar_found++) + *(to_pos++)=FN_C_DIR_SEP; /* Add '.' between dirs */ + if (strinstr(from_pos,FN_PARENTDIR) == 1 && + from_pos+strlen(FN_PARENTDIR) == pos) + to_pos=strmov(to_pos,FN_C_PARENT_DIR); /* Found '../' */ + else + to_pos=strnmov(to_pos,from_pos,(size_s) (pos-from_pos)); + from_pos=pos+1; + } + *(to_pos++)=FN_C_AFTER_DIR; + } + length=(int) (strmov(to_pos,from_pos)-to); + DBUG_PRINT("exit",("name: '%s'",to)); + DBUG_RETURN((uint) length); +#endif +} /* system_filename */ + + + /* Fix a filename to intern (UNIX format) */ + +my_string intern_filename(my_string to, const char *from) +{ +#ifndef VMS + { + uint length; + char buff[FN_REFLEN]; + if (from == to) + { /* Dirname may destroy from */ + strmov(buff,from); + from=buff; + } + length=dirname_part(to,from); /* Copy dirname & fix chars */ + (void) strcat(to,from+length); + return (to); + } +#else /* VMS */ + + /* change 'dev:[lib]xxx' to 'dev:lib/xxx' */ + /* change 'dev:xxx' to 'dev:xxx' */ + /* change 'dev:x/y/[.lib]' to 'dev:x/y/lib/ */ + /* change '[.lib]' to './lib/' */ + /* change '[x.y]' or '[x.][y]' or '[x][.y]' to '/x/y/' */ + /* change '[000000.x] or [x.000000]' to '/x/' */ + + int par_length,root_length; + my_string pos,from_pos,to_pos,end_pos; + char buff[FN_REFLEN]; + + (void) strmov(buff,from); + convert_dirname(buff); /* change '<>' to '[]' */ + from_pos=buff; + if ((pos=strrchr(from_pos,FN_DEVCHAR))) /* Skipp device part */ + { + pos++; + to_pos=strnmov(to,from_pos,(size_s) (pos-from_pos)); + from_pos=pos; + } + else + to_pos=to; + + root_length=strlen(FN_C_ROOT_DIR); + if ((pos = strchr(from_pos,FN_C_BEFORE_DIR)) && + (end_pos = strrchr(pos+1,FN_C_AFTER_DIR))) + { + to_pos=strnmov(to_pos,from_pos,(size_s) (pos-from_pos)); + /* Copy all between ':' and '[' */ + from_pos=pos+1; + if (strinstr(from_pos,FN_C_ROOT_DIR) == 1 && + (from_pos[root_length] == FN_C_DIR_SEP || + from_pos[root_length] == FN_C_AFTER_DIR)) + { + from_pos+=root_length+1; + } + else if (*from_pos == FN_C_DIR_SEP) + *(to_pos++) = FN_CURLIB; /* Set ./ first */ + *(to_pos++) = FN_LIBCHAR; + + par_length=strlen(FN_C_PARENT_DIR); + pos=to_pos; + for (; from_pos <= end_pos ; from_pos++) + { + switch (*from_pos) { + case FN_C_DIR_SEP: + case FN_C_AFTER_DIR: + if (pos != to_pos) + { + if ((int) (to_pos-pos) == root_length && + is_suffix(pos,FN_C_ROOT_DIR)) + to_pos=pos; /* remove root-pos */ + else + { + *(to_pos++)=FN_LIBCHAR; /* Find lib */ + pos=to_pos; + } + } + break; + case FN_C_BEFORE_DIR: + break; + case '-': /* *(FN_C_PARENT_DIR): */ + if (to_pos[-1] == FN_LIBCHAR && + strncmp(from_pos,FN_C_PARENT_DIR,par_length) == 0) + { /* Change '-' to '..' */ + to_pos=strmov(to_pos,FN_PARENTDIR); + *(to_pos++)=FN_LIBCHAR; + pos=to_pos; + from_pos+=par_length-1; + break; + } + /* Fall through */ + default: + *(to_pos++)= *from_pos; + break; + } + } + } + (void) strmov(to_pos,from_pos); + return (to); +#endif /* VMS */ +} /* intern_filename */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_path.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_path.c new file mode 100644 index 0000000..489ccc6 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_path.c @@ -0,0 +1,120 @@ +/* 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 */ + +#include "mysys_priv.h" +#include + +static char *find_file_in_path(char *to,const char *name); + + /* Finds where program can find it's files. + pre_pathname is found by first locking at progname (argv[0]). + if progname contains path the path is returned. + else if progname is found in path, return it + else if progname is given and POSIX environment variable "_" is set + then path is taken from "_". + If filename doesn't contain a path append MY_BASEDIR_VERSION or + MY_BASEDIR if defined, else append "/my/running". + own_path_name_part is concatinated to result. + my_path puts result in to and returns to */ + +my_string my_path(my_string to, const char *progname, + const char *own_pathname_part) +{ + my_string start,end,prog; + DBUG_ENTER("my_path"); + + start=to; /* Return this */ + if (progname && (dirname_part(to, progname) || + find_file_in_path(to,progname) || + ((prog=getenv("_")) != 0 && dirname_part(to,prog)))) + { + VOID(intern_filename(to,to)); + if (!test_if_hard_path(to)) + { + if (!my_getwd(curr_dir,FN_REFLEN,MYF(0))) + bchange(to,0,curr_dir, (uint) strlen(curr_dir), (uint) strlen(to)+1); + } + } + else + { + if ((end = getenv("MY_BASEDIR_VERSION")) == 0 && + (end = getenv("MY_BASEDIR")) == 0) + { +#ifdef DEFAULT_BASEDIR + end= (char*) DEFAULT_BASEDIR; +#else + end= (char*) "/my/"; +#endif + } + VOID(intern_filename(to,end)); + to=strend(to); + if (to != start && to[-1] != FN_LIBCHAR) + *to++ = FN_LIBCHAR; + VOID(strmov(to,own_pathname_part)); + } + DBUG_PRINT("exit",("to: '%s'",start)); + DBUG_RETURN(start); +} /* my_path */ + + + /* test if file without filename is found in path */ + /* Returns to if found and to has dirpart if found, else NullS */ + +#if defined(MSDOS) || defined(_WIN32) || defined(__EMX__) || defined(OS2) +#define F_OK 0 +#define PATH_SEP ';' +#define PROGRAM_EXTENSION ".exe" +#else +#define PATH_SEP ':' +#endif + +static char *find_file_in_path(char *to, const char *name) +{ + char *path,*pos,dir[2]; + const char *ext=""; + + if (!(path=getenv("PATH"))) + return NullS; + dir[0]=FN_LIBCHAR; dir[1]=0; +#ifdef PROGRAM_EXTENSION + if (!fn_ext(name)[0]) + ext=PROGRAM_EXTENSION; +#endif + + for (pos=path ; (pos=strchr(pos,PATH_SEP)) ; path= ++pos) + { + if (path != pos) + { + strxmov(strnmov(to,path,(uint) (pos-path)),dir,name,ext,NullS); + if (!access(to,F_OK)) + { + to[(uint) (pos-path)+1]=0; /* Return path only */ + return to; + } + } + } +#ifdef _WIN32 + to[0]=FN_CURLIB; + strxmov(to+1,dir,name,ext,NullS); + if (!access(to,F_OK)) /* Test in current dir */ + { + to[2]=0; /* Leave ".\" */ + return to; + } +#endif + return NullS; /* File not found */ +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_unixpath.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_unixpath.c new file mode 100644 index 0000000..10c152c --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_unixpath.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 */ + +#include "mysys_priv.h" +#include + + /* convert filename to unix style filename */ + /* If MSDOS converts '\' to '/' */ + +void to_unix_path(my_string to __attribute__((unused))) +{ +#if FN_LIBCHAR != '/' + { + to--; + while ((to=strchr(to+1,FN_LIBCHAR)) != 0) + *to='/'; + } +#endif +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mf_wcomp.c b/mariadb-connector-c-v_2.3.7/libmariadb/mf_wcomp.c new file mode 100644 index 0000000..822c009 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mf_wcomp.c @@ -0,0 +1,68 @@ +/* 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 */ + +/* Funktions for comparing with wild-cards */ + +#include "mysys_priv.h" + + /* Test if a string is "comparable" to a wild-card string */ + /* returns 0 if the strings are "comparable" */ + +char wild_many='*'; +char wild_one='?'; +char wild_prefix=0; + +int wild_compare(register const char *str, register const char *wildstr) +{ + reg3 int flag; + DBUG_ENTER("wild_compare"); + + while (*wildstr) + { + while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) + { + if (*wildstr == wild_prefix && wildstr[1]) + wildstr++; + if (*wildstr++ != *str++) DBUG_RETURN(1); + } + if (! *wildstr ) DBUG_RETURN (*str != 0); + if (*wildstr++ == wild_one) + { + if (! *str++) DBUG_RETURN (1); /* One char; skipp */ + } + else + { /* Found '*' */ + if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */ + flag=(*wildstr != wild_many && *wildstr != wild_one); + do + { + if (flag) + { + char cmp; + if ((cmp= *wildstr) == wild_prefix && wildstr[1]) + cmp=wildstr[1]; + while (*str && *str != cmp) + str++; + if (!*str) DBUG_RETURN (1); + } + if (wild_compare(str,wildstr) == 0) DBUG_RETURN (0); + } while (*str++ && wildstr[0] != wild_many); + DBUG_RETURN(1); + } + } + DBUG_RETURN (*str != '\0'); +} /* wild_compare */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mulalloc.c b/mariadb-connector-c-v_2.3.7/libmariadb/mulalloc.c new file mode 100644 index 0000000..84f101a --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mulalloc.c @@ -0,0 +1,53 @@ +/* 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 */ + + /* Malloc many pointers at the same time */ + /* format myFlags,ptr,length,ptr,length ... until null ptr */ + +#include "mysys_priv.h" +#include + +gptr my_multi_malloc(myf myFlags, ...) +{ + va_list args; + char **ptr,*start,*res; + uint tot_length,length; + DBUG_ENTER("my_multi_malloc"); + + va_start(args,myFlags); + tot_length=0; + while ((ptr=va_arg(args, char **))) + { + length=va_arg(args,uint); + tot_length+=ALIGN_SIZE(length); + } + va_end(args); + + if (!(start=(char *) my_malloc(tot_length,myFlags))) + DBUG_RETURN(0); /* purecov: inspected */ + + va_start(args,myFlags); + res=start; + while ((ptr=va_arg(args, char **))) + { + *ptr=res; + length=va_arg(args,uint); + res+=ALIGN_SIZE(length); + } + va_end(args); + DBUG_RETURN((gptr) start); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_alloc.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_alloc.c new file mode 100644 index 0000000..8d31d5d --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_alloc.c @@ -0,0 +1,169 @@ +/* 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 +#include +#include + +void init_alloc_root(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(USED_MEM)+8); + mem_root->error_handler=0; + mem_root->block_num= 4; + 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= + (USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)), + MYF(0)))) + { + mem_root->free->size=pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM)); + mem_root->free->left=pre_alloc_size; + mem_root->free->next=0; + } + } +#endif +} + +gptr alloc_root(MEM_ROOT *mem_root, size_t Size) +{ +#if defined(HAVE_purify) && defined(EXTRA_DEBUG) + reg1 USED_MEM *next; + Size+=ALIGN_SIZE(sizeof(USED_MEM)); + + if (!(next = (USED_MEM*) my_malloc(Size,MYF(MY_WME)))) + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + return((gptr) 0); /* purecov: inspected */ + } + next->next=mem_root->used; + mem_root->used=next; + return (gptr) (((char*) next)+ALIGN_SIZE(sizeof(USED_MEM))); +#else + size_t get_size,max_left; + gptr point; + reg1 USED_MEM *next= 0; + reg2 USED_MEM **prev; + + Size= ALIGN_SIZE(Size); + max_left=0; + + 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(USED_MEM)), + (mem_root->block_size & ~1) * (mem_root->block_num >> 2)); + + if (!(next = (USED_MEM*) my_malloc(get_size,MYF(MY_WME)))) + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + return((gptr) 0); /* purecov: inspected */ + } + mem_root->block_num++; + next->next= *prev; + next->size= get_size; + next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); + *prev=next; + } + point= (gptr) ((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 free_root(MEM_ROOT *root, myf MyFlags) +{ + reg1 USED_MEM *next,*old; + DBUG_ENTER("free_root"); + + if (!root) + DBUG_VOID_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) + my_free(old); + } + for (next= root->free ; next ; ) + { + old=next; next= next->next ; + if (old != root->pre_alloc) + my_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(USED_MEM)); + root->free->next=0; + } + root->block_num= 4; + root->first_block_usage= 0; + DBUG_VOID_RETURN; +} + + +char *strdup_root(MEM_ROOT *root,const char *str) +{ + size_t len= strlen(str)+1; + char *pos; + if ((pos=alloc_root(root,len))) + memcpy(pos,str,len); + return pos; +} + + +char *memdup_root(MEM_ROOT *root, const char *str, size_t len) +{ + char *pos; + if ((pos=alloc_root(root,len))) + memcpy(pos,str,len); + return pos; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_auth.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_auth.c new file mode 100644 index 0000000..546316f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_auth.c @@ -0,0 +1,742 @@ +/************************************************************************************ + Copyright (C) 2012-2015 Monty Program 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 see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net + + Originally written by Sergei Golubchik +*************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_OPENSSL +#include +#endif + +typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; +static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t); +static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +extern void read_user_name(char *name); +extern uchar *ma_send_connect_attr(MYSQL *mysql, uchar *buffer); + +static auth_plugin_t native_password_client_plugin= +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + native_password_plugin_name, + "R.J.Silk, Sergei Golubchik", + "Native MySQL authentication", + {1, 0, 0}, + "LGPL", + NULL, + NULL, + NULL, + NULL, + native_password_auth_client +}; + +static auth_plugin_t old_password_client_plugin= +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + old_password_plugin_name, + "R.J.Silk, Sergei Golubchik", + "Old MySQL-3.23 authentication", + {1, 0, 0}, + "LGPL", + NULL, + NULL, + NULL, + NULL, + old_password_auth_client +}; +typedef struct st_mariadb_client_plugin_DBAPI dbapi_plugin_t; + +#ifdef HAVE_SQLITE +extern dbapi_plugin_t sqlite3_plugin; +#endif + +struct st_mysql_client_plugin *mysql_client_builtins[]= +{ + (struct st_mysql_client_plugin *)&old_password_client_plugin, + (struct st_mysql_client_plugin *)&native_password_client_plugin, +#ifdef HAVE_SQLITE + (struct st_mysql_client_plugin *)&sqlite3_plugin, +#endif + 0 +}; + +typedef struct { + int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); + int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, size_t pkt_len); + void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); + /* -= end of MYSQL_PLUGIN_VIO =- */ + MYSQL *mysql; + auth_plugin_t *plugin; /**< what plugin we're under */ + const char *db; + struct { + uchar *pkt; /**< pointer into NET::buff */ + uint pkt_len; + } cached_server_reply; + uint packets_read, packets_written; /**< counters for send/received packets */ + my_bool mysql_change_user; /**< if it's mysql_change_user() */ + int last_read_packet_len; /**< the length of the last *read* packet */ +} MCPVIO_EXT; + +static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + int pkt_len; + uchar *pkt; + + if (((MCPVIO_EXT *)vio)->mysql_change_user) + { + /* + in mysql_change_user() the client sends the first packet. + we use the old scramble. + */ + pkt= (uchar*)mysql->scramble_buff; + pkt_len= SCRAMBLE_LENGTH + 1; + } + else + { + /* read the scramble */ + if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) + return CR_ERROR; + + if (pkt_len != SCRAMBLE_LENGTH + 1) + return CR_SERVER_HANDSHAKE_ERR; + + /* save it in MYSQL */ + memmove(mysql->scramble_buff, pkt, SCRAMBLE_LENGTH); + mysql->scramble_buff[SCRAMBLE_LENGTH] = 0; + } + + if (mysql->passwd[0]) + { + char scrambled[SCRAMBLE_LENGTH + 1]; + my_scramble_41((uchar *)scrambled, (char*)pkt, mysql->passwd); + if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH)) + return CR_ERROR; + } + else + if (vio->write_packet(vio, 0, 0)) /* no password */ + return CR_ERROR; + + return CR_OK; +} + + +/** + client authentication plugin that does old MySQL authentication + using an 8-byte (4.0-) scramble +*/ + +static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + uchar *pkt; + int pkt_len; + + if (((MCPVIO_EXT *)vio)->mysql_change_user) + { + /* + in mysql_change_user() the client sends the first packet. + we use the old scramble. + */ + pkt= (uchar*)mysql->scramble_buff; + pkt_len= SCRAMBLE_LENGTH_323 + 1; + } + else + { + /* read the scramble */ + if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) + return CR_ERROR; + + if (pkt_len != SCRAMBLE_LENGTH_323 + 1 && + pkt_len != SCRAMBLE_LENGTH + 1) + return CR_SERVER_HANDSHAKE_ERR; + + /* save it in MYSQL */ + memcpy(mysql->scramble_buff, pkt, pkt_len); + mysql->scramble_buff[pkt_len] = 0; + } + + if (mysql->passwd[0]) + { + char scrambled[SCRAMBLE_LENGTH_323 + 1]; + scramble_323(scrambled, (char*)pkt, mysql->passwd); + if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1)) + return CR_ERROR; + } + else + if (vio->write_packet(vio, 0, 0)) /* no password */ + return CR_ERROR; + + return CR_OK; +} + +static int send_change_user_packet(MCPVIO_EXT *mpvio, + const uchar *data, int data_len) +{ + MYSQL *mysql= mpvio->mysql; + char *buff, *end; + int res= 1; + size_t conn_attr_len= (mysql->options.extension) ? + mysql->options.extension->connect_attrs_len : 0; + + buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1 + 9 + conn_attr_len); + + end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1; + + if (!data_len) + *end++= 0; + else + { + if (mysql->client_flag & CLIENT_SECURE_CONNECTION) + { + DBUG_ASSERT(data_len <= 255); + if (data_len > 255) + { + my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); + goto error; + } + *end++= data_len; + } + else + { + DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); + DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0); + } + memcpy(end, data, data_len); + end+= data_len; + } + end= strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1; + + if (mysql->server_capabilities & CLIENT_PROTOCOL_41) + { + int2store(end, (ushort) mysql->charset->nr); + end+= 2; + } + + if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1; + + end= ma_send_connect_attr(mysql, end); + + res= simple_command(mysql, MYSQL_COM_CHANGE_USER, + buff, (ulong)(end-buff), 1, NULL); + +error: + my_afree(buff); + return res; +} + + + +static int send_client_reply_packet(MCPVIO_EXT *mpvio, + const uchar *data, int data_len) +{ + MYSQL *mysql= mpvio->mysql; + NET *net= &mysql->net; + char *buff, *end; + size_t conn_attr_len= (mysql->options.extension) ? + mysql->options.extension->connect_attrs_len : 0; + + /* see end= buff+32 below, fixed size of the packet is 32 bytes */ + buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9); + + mysql->client_flag|= mysql->options.client_flag; + mysql->client_flag|= CLIENT_CAPABILITIES; + + if (mysql->client_flag & CLIENT_MULTI_STATEMENTS) + mysql->client_flag|= CLIENT_MULTI_RESULTS; + +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + if (mysql->options.ssl_key || mysql->options.ssl_cert || + mysql->options.ssl_ca || mysql->options.ssl_capath || + mysql->options.ssl_cipher) + mysql->options.use_ssl= 1; + if (mysql->options.use_ssl) + mysql->client_flag|= CLIENT_SSL; + + /* if server doesn't support SSL and verification of server certificate + was set to mandatory, we need to return an error */ + if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL)) + { + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) || + (mysql->options.extension && (mysql->options.extension->ssl_fp || + mysql->options.extension->ssl_fp_list))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Server doesn't support SSL"); + goto error; + } + } + +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/ + if (mpvio->db) + mysql->client_flag|= CLIENT_CONNECT_WITH_DB; + + /* Remove options that server doesn't support */ + mysql->client_flag= mysql->client_flag & + (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) + | mysql->server_capabilities); + +#ifndef HAVE_COMPRESS + mysql->client_flag&= ~CLIENT_COMPRESS; +#endif + + if (mysql->client_flag & CLIENT_PROTOCOL_41) + { + /* 4.1 server and 4.1 client has a 32 byte option flag */ + int4store(buff,mysql->client_flag); + int4store(buff+4, net->max_packet_size); + buff[8]= (char) mysql->charset->nr; + bzero(buff+9, 32-9); + end= buff+32; + } + else + { + int2store(buff, mysql->client_flag); + int3store(buff+2, net->max_packet_size); + end= buff+5; + } +#ifdef HAVE_OPENSSL + if (mysql->options.ssl_key || + mysql->options.ssl_cert || + mysql->options.ssl_ca || + mysql->options.ssl_capath || + mysql->options.ssl_cipher +#ifdef CRL_IMPLEMENTED + || (mysql->options.extension && + (mysql->options.extension->ssl_crl || + mysql->options.extension->ssl_crlpath)) +#endif + ) + mysql->options.use_ssl= 1; + + if (mysql->options.use_ssl && + (mysql->client_flag & CLIENT_SSL)) + { + SSL *ssl; + /* + Send mysql->client_flag, max_packet_size - unencrypted otherwise + the server does not know we want to do SSL + */ + if (my_net_write(net, (char*)buff, (size_t) (end-buff)) || net_flush(net)) + { + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "sending connection information to server", + errno); + goto error; + } + + /* Create SSL */ + if (!(ssl= my_ssl_init(mysql))) + goto error; + + /* Connect to the server */ + if (my_ssl_connect(ssl)) + { + SSL_free(ssl); + goto error; + } + + if (mysql->options.extension && + (mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list)) + { + if (ma_ssl_verify_fingerprint(ssl)) + goto error; + } + + if ((mysql->options.ssl_ca || mysql->options.ssl_capath) && + (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + my_ssl_verify_server_cert(ssl)) + goto error; + } +#endif /* HAVE_OPENSSL */ + + DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu", + mysql->server_version, mysql->server_capabilities, + mysql->server_status, mysql->client_flag)); + + /* This needs to be changed as it's not useful with big packets */ + if (mysql->user[0]) + strmake(end, mysql->user, USERNAME_LENGTH); + else + read_user_name(end); + + /* We have to handle different version of handshake here */ + DBUG_PRINT("info",("user: %s",end)); + end= strend(end) + 1; + if (data_len) + { + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + *end++= data_len; + memcpy(end, data, data_len); + end+= data_len; + } + else + { + DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */ + memcpy(end, data, data_len); + end+= data_len; + } + } + else + *end++= 0; + + /* Add database if needed */ + if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) + { + end= strmake(end, mpvio->db, NAME_LEN) + 1; + mysql->db= my_strdup(mpvio->db, MYF(MY_WME)); + } + + if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1; + + end= ma_send_connect_attr(mysql, end); + + /* Write authentication package */ + if (my_net_write(net, buff, (size_t) (end-buff)) || net_flush(net)) + { + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "sending authentication information", + errno); + goto error; + } + my_afree(buff); + return 0; + +error: + my_afree(buff); + return 1; +} + +/** + vio->read_packet() callback method for client authentication plugins + + This function is called by a client authentication plugin, when it wants + to read data from the server. +*/ + +static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) +{ + MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; + MYSQL *mysql= mpvio->mysql; + ulong pkt_len; + + /* there are cached data left, feed it to a plugin */ + if (mpvio->cached_server_reply.pkt) + { + *buf= mpvio->cached_server_reply.pkt; + mpvio->cached_server_reply.pkt= 0; + mpvio->packets_read++; + return mpvio->cached_server_reply.pkt_len; + } + + if (mpvio->packets_read == 0) + { + /* + the server handshake packet came from the wrong plugin, + or it's mysql_change_user(). Either way, there is no data + for a plugin to read. send a dummy packet to the server + to initiate a dialog. + */ + if (client_mpvio_write_packet(mpv, 0, 0)) + return (int)packet_error; + } + + /* otherwise read the data */ + pkt_len= net_safe_read(mysql); + mpvio->last_read_packet_len= pkt_len; + *buf= mysql->net.read_pos; + + /* was it a request to change plugins ? */ + if (**buf == 254) + return (int)packet_error; /* if yes, this plugin shan't continue */ + + /* + the server sends \1\255 or \1\254 instead of just \255 or \254 - + for us to not confuse it with an error or "change plugin" packets. + We remove this escaping \1 here. + + See also server_mpvio_write_packet() where the escaping is done. + */ + if (pkt_len && **buf == 1) + { + (*buf)++; + pkt_len--; + } + mpvio->packets_read++; + return pkt_len; +} + +/** + vio->write_packet() callback method for client authentication plugins + + This function is called by a client authentication plugin, when it wants + to send data to the server. + + It transparently wraps the data into a change user or authentication + handshake packet, if neccessary. +*/ + +static int client_mpvio_write_packet(struct st_plugin_vio *mpv, + const uchar *pkt, size_t pkt_len) +{ + int res; + MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; + + if (mpvio->packets_written == 0) + { + if (mpvio->mysql_change_user) + res= send_change_user_packet(mpvio, pkt, (int)pkt_len); + else + res= send_client_reply_packet(mpvio, pkt, (int)pkt_len); + } + else + { + NET *net= &mpvio->mysql->net; + if (mpvio->mysql->thd) + res= 1; /* no chit-chat in embedded */ + else + res= my_net_write(net, (char *)pkt, pkt_len) || net_flush(net); + if (res) + my_set_error(mpvio->mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "sending authentication information", + errno); + } + mpvio->packets_written++; + return res; +} + +/** + fills MYSQL_PLUGIN_VIO_INFO structure with the information about the + connection +*/ + +void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) +{ + bzero(info, sizeof(*info)); + switch (vio->type) { + case VIO_TYPE_TCPIP: + info->protocol= MYSQL_VIO_TCP; + info->socket= vio->sd; + return; + case VIO_TYPE_SOCKET: + info->protocol= MYSQL_VIO_SOCKET; + info->socket= vio->sd; + return; + case VIO_TYPE_SSL: + { + struct sockaddr addr; + SOCKET_SIZE_TYPE addrlen= sizeof(addr); + if (getsockname(vio->sd, &addr, &addrlen)) + return; + info->protocol= addr.sa_family == AF_UNIX ? + MYSQL_VIO_SOCKET : MYSQL_VIO_TCP; + info->socket= vio->sd; + return; + } +#ifdef _WIN32 + case VIO_TYPE_NAMEDPIPE: + info->protocol= MYSQL_VIO_PIPE; + info->handle= vio->hPipe; + return; +/* not supported yet + case VIO_TYPE_SHARED_MEMORY: + info->protocol= MYSQL_VIO_MEMORY; + info->handle= vio->handle_file_map; + return; +*/ +#endif + default: DBUG_ASSERT(0); + } +} + +static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, + MYSQL_PLUGIN_VIO_INFO *info) +{ + MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio; + mpvio_info(mpvio->mysql->net.vio, info); +} + +/** + Client side of the plugin driver authentication. + + @note this is used by both the mysql_real_connect and mysql_change_user + + @param mysql mysql + @param data pointer to the plugin auth data (scramble) in the + handshake packet + @param data_len the length of the data + @param data_plugin a plugin that data were prepared for + or 0 if it's mysql_change_user() + @param db initial db to use, can be 0 + + @retval 0 ok + @retval 1 error +*/ + +int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + const char *data_plugin, const char *db) +{ + const char *auth_plugin_name; + auth_plugin_t *auth_plugin; + MCPVIO_EXT mpvio; + ulong pkt_length; + int res; + + /* determine the default/initial plugin to use */ + if (mysql->options.extension && mysql->options.extension->default_auth && + mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + { + auth_plugin_name= mysql->options.extension->default_auth; + if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, + auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) + return 1; /* oops, not found */ + } + else + { + auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ? + &native_password_client_plugin : &old_password_client_plugin; + auth_plugin_name= auth_plugin->name; + } + + mysql->net.last_errno= 0; /* just in case */ + + if (data_plugin && strcmp(data_plugin, auth_plugin_name)) + { + /* data was prepared for a different plugin, don't show it to this one */ + data= 0; + data_len= 0; + } + + mpvio.mysql_change_user= data_plugin == 0; + mpvio.cached_server_reply.pkt= (uchar*)data; + mpvio.cached_server_reply.pkt_len= data_len; + mpvio.read_packet= client_mpvio_read_packet; + mpvio.write_packet= client_mpvio_write_packet; + mpvio.info= client_mpvio_info; + mpvio.mysql= mysql; + mpvio.packets_read= mpvio.packets_written= 0; + mpvio.db= db; + mpvio.plugin= auth_plugin; + + res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + + if (res > CR_OK && mysql->net.read_pos[0] != 254) + { + /* + the plugin returned an error. write it down in mysql, + unless the error code is CR_ERROR and mysql->net.last_errno + is already set (the plugin has done it) + */ + if (res > CR_ERROR) + my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0); + else + if (!mysql->net.last_errno) + my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + return 1; + } + + /* read the OK packet (or use the cached value in mysql->net.read_pos */ + if (res == CR_OK) + pkt_length= net_safe_read(mysql); + else /* res == CR_OK_HANDSHAKE_COMPLETE */ + pkt_length= mpvio.last_read_packet_len; + + if (pkt_length == packet_error) + { + if (mysql->net.last_errno == CR_SERVER_LOST) + my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, + ER(CR_SERVER_LOST_EXTENDED), + "reading authorization packet", + errno); + return 1; + } + + if (mysql->net.read_pos[0] == 254) + { + /* The server asked to use a different authentication plugin */ + if (pkt_length == 1) + { + /* old "use short scramble" packet */ + auth_plugin_name= old_password_plugin_name; + mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble_buff; + mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; + } + else + { + /* new "use different plugin" packet */ + uint len; + auth_plugin_name= (char*)mysql->net.read_pos + 1; + len= (uint)strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */ + mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; + mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; + } + + if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql, + auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) + return 1; + + mpvio.plugin= auth_plugin; + res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + + if (res > CR_OK) + { + if (res > CR_ERROR) + my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0); + else + if (!mysql->net.last_errno) + my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); + return 1; + } + + if (res != CR_OK_HANDSHAKE_COMPLETE) + { + /* Read what server thinks about out new auth message report */ + if (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), + "reading final connect information", + errno); + return 1; + } + } + } + /* + net->read_pos[0] should always be 0 here if the server implements + the protocol correctly + */ + return mysql->net.read_pos[0] != 0; +} + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_charset.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_charset.c new file mode 100644 index 0000000..a36ccc2 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_charset.c @@ -0,0 +1,1466 @@ +/**************************************************************************** + 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 + 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ +*/ + +#ifndef _WIN32 +#include +#include +#else +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include "../win-iconv/iconv.h" +#else +#include +#endif + +extern int my_snprintf(char* to, size_t n, const char* fmt, ...); +/* + +----------------------------------------------------------------------+ + | 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ +*/ + +/* {{{ 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(*(start)) && (end - start) > 1 && valid_big5tail(*(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 utf8m4, 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 compilastion dependable. +*/ + +#define UTF8_MB4 "utf8mb4" +#define UTF8_MB3 "utf8" + +/* {{{ mysql_charsets */ +const CHARSET_INFO compiled_charsets[] = +{ + { 1, 1, "big5","big5_chinese_ci", "", 950, "BIG5", 1, 2, mysql_mbcharlen_big5, check_mb_big5}, + { 3, 1, "dec8", "dec8_swedisch_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", "", 878, "KOI8R", 1, 1, NULL, NULL}, + { 8, 1, "latin1", "latin1_swedish_ci", "", 850, "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", "", 20866, "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_german_ci", "", 850, "LATIN1", 1, 1, NULL, NULL}, + { 14, 1, "cp1251", "cp1251_bulgarian_ci", "", 1251, "CP1251", 1, 1, NULL, NULL}, + { 15, 1, "latin1", "latin1_danish_ci", "", 850, "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_lithunian_ci", "", 1257, "CP1257", 1, 1, NULL, NULL}, + { 31, 1, "latin1", "latin1_german2_ci", "", 850, "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_lithunian_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_lithunian_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"_lithunian_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"_german_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} +}; +/* }}} */ + + +/* {{{ mysql_find_charset_nr */ +const CHARSET_INFO * mysql_find_charset_nr(unsigned int charsetnr) +{ + const CHARSET_INFO * c = compiled_charsets; + DBUG_ENTER("mysql_find_charset_nr"); + + do { + if (c->nr == charsetnr) { + DBUG_PRINT("info", ("found character set %d %s", c->nr, c->csname)); + DBUG_RETURN(c); + } + ++c; + } while (c[0].nr != 0); + DBUG_RETURN(NULL); +} +/* }}} */ + + +/* {{{ mysql_find_charset_name */ +CHARSET_INFO * mysql_find_charset_name(const char *name) +{ + CHARSET_INFO *c = (CHARSET_INFO *)compiled_charsets; + DBUG_ENTER("mysql_find_charset_name"); + + do { + if (!strcasecmp(c->csname, name)) { + DBUG_PRINT("info", ("found character set %d %s", c->nr, c->csname)); + DBUG_RETURN(c); + } + ++c; + } while (c[0].nr != 0); + DBUG_RETURN(NULL); +} +/* }}} */ + + +/* {{{ mysql_cset_escape_quotes */ +size_t mysql_cset_escape_quotes(const 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; + + DBUG_ENTER("mysql_cset_escape_quotes"); + + 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) { + DBUG_RETURN((size_t)~0); + } + DBUG_RETURN((size_t)(newstr - newstr_s)); +} +/* }}} */ + + +/* {{{ mysql_cset_escape_slashes */ +size_t mysql_cset_escape_slashes(const 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; + + DBUG_ENTER("mysql_cset_escape_slashes"); + DBUG_PRINT("info", ("charset=%s", cset->name)); + + 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) { + DBUG_RETURN((size_t)~0); + } + DBUG_RETURN((size_t)(newstr - newstr_s)); +} +/* }}} */ + +/* {{{ MADB_OS_CHARSET */ +struct st_madb_os_charset { + char *identifier; + char *description; + char *charset; + 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)", "hewbrew", 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 */ +char *madb_get_os_character_set() +{ + unsigned int i= 0; + char *p= NULL; +#ifdef _WIN32 + char codepage[FN_REFLEN]; + my_snprintf(codepage, FN_REFLEN, "%u", GetConsoleWindow() ? + 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 && + strcmp(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 +/* }}} */ + + +/* {{{ 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 *ptr= buffer, 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 */ + ptr= strnmov(ptr, "UTF-", buff_len); + ptr= strnmov(ptr, digits, buff_len - (ptr - buffer)); + ptr= strnmov(ptr, endianness, buff_len - (ptr - buffer)); + } + else + { + /* Not our client - copy as is*/ + ptr= strnmov(ptr, cs_name, buff_len); + } + + if (target_cs) + { + strnmov(ptr, "//TRANSLIT", buff_len - (ptr - buffer)); + } +} +/* }}} */ + +/* {{{ 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, size_t *from_len, CHARSET_INFO *from_cs, + char *to, size_t *to_len, CHARSET_INFO *to_cs, int *errorcode) +{ + 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, (char **)&from, from_len, &to, to_len)) == -1) + { + *errorcode= errno; + goto error; + } + rc= save_len - *to_len; +error: + if (conv != (iconv_t)-1) + iconv_close(conv); + return rc; +} +/* }}} */ + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_compress.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_compress.c new file mode 100644 index 0000000..71685cd --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_compress.c @@ -0,0 +1,89 @@ +/* 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 */ + +/* Written by Sinisa Milivojevic */ + +#include +#ifdef HAVE_COMPRESS +#include +#include +#include + +/* +** This replaces the packet with a compressed packet +** Returns 1 on error +** *complen is 0 if the packet wasn't compressed +*/ + +my_bool my_compress(unsigned char *packet, size_t *len, size_t *complen) +{ + if (*len < MIN_COMPRESS_LENGTH) + *complen=0; + else + { + unsigned char *compbuf=my_compress_alloc(packet,len,complen); + if (!compbuf) + return *complen ? 0 : 1; + memcpy(packet,compbuf,*len); + my_free(compbuf); + } + return 0; +} + + +unsigned char *my_compress_alloc(const unsigned char *packet, size_t *len, size_t *complen) +{ + unsigned char *compbuf; + *complen = *len * 120 / 100 + 12; + if (!(compbuf = (unsigned char *) my_malloc(*complen,MYF(MY_WME)))) + return 0; /* Not enough memory */ + if (compress((Bytef*) compbuf,(ulong *) complen, (Bytef*) packet, + (uLong) *len ) != Z_OK) + { + my_free(compbuf); + return 0; + } + if (*complen >= *len) + { + *complen=0; + my_free(compbuf); + return 0; + } + swap(ulong,*len,*complen); /* *len is now packet length */ + return compbuf; +} + +my_bool my_uncompress (unsigned char *packet, size_t *len, size_t *complen) +{ + if (*complen) /* If compressed */ + { + unsigned char *compbuf = (unsigned char *) my_malloc (*complen,MYF(MY_WME)); + if (!compbuf) + return 1; /* Not enough memory */ + if (uncompress((Bytef*) compbuf, (uLongf *)complen, (Bytef*) packet, (uLongf)*len) != Z_OK) + { /* Probably wrong packet */ + my_free (compbuf); + return 1; + } + *len = *complen; + memcpy(packet,compbuf,*len); + my_free(compbuf); + } + else *complen= *len; + return 0; +} +#endif /* HAVE_COMPRESS */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_context.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_context.c new file mode 100644 index 0000000..4bc62a8 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_context.c @@ -0,0 +1,760 @@ +/* + Copyright 2011, 2012 Kristian Nielsen and Monty Program 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 . +*/ + +/* + Implementation of async context spawning using Posix ucontext and + swapcontext(). +*/ + +#include "mysys_priv.h" +#include "m_string.h" +#include "my_context.h" + +#ifdef HAVE_VALGRIND +#include +#endif + +#ifdef MY_CONTEXT_USE_UCONTEXT +/* + 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(i0, i1) +int i0, 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; + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + err= swapcontext(&c->base_context, &c->spawned_context); + DBUG_SWAP_CODE_STATE(&c->dbug_state); + 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, 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 + + bzero(c, 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); + } + DBUG_FREE_CODE_STATE(&c->dbug_state); +} + +#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 +#include + +/* + 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; + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + /* + 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" + ); + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + return ret; +} + +int +my_context_continue(struct my_context *c) +{ + int ret; + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + __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" + ); + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + 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) +{ + bzero(c, 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); + bzero(c->stack_top, 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 + } + DBUG_FREE_CODE_STATE(&c->dbug_state); +} + +#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 +#include + +/* + 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; + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + /* + 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" + ); + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + return ret; +} + +int +my_context_continue(struct my_context *c) +{ + int ret; + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + __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" + ); + + DBUG_SWAP_CODE_STATE(&c->dbug_state); + + 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) +{ + bzero(c, 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); + bzero(c->stack_top, 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 + } + DBUG_FREE_CODE_STATE(&c->dbug_state); +} + +#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 asynchroneous + 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) +{ + bzero(c, 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) +{ + DBUG_FREE_CODE_STATE(&c->dbug_state); + 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) +{ + void *current_fiber; + c->user_func= f; + c->user_arg= d; + /* + This seems to be a common trick to run ConvertThreadToFiber() only on the + first occurence in a thread, in a way that works on multiple Windows + versions. + */ + current_fiber= GetCurrentFiber(); + if (current_fiber == NULL || current_fiber == (void *)0x1e00) + current_fiber= ConvertThreadToFiber(c); + c->app_fiber= current_fiber; + DBUG_SWAP_CODE_STATE(&c->dbug_state); + SwitchToFiber(c->lib_fiber); + DBUG_SWAP_CODE_STATE(&c->dbug_state); + return c->return_value; +} + +int +my_context_continue(struct my_context *c) +{ + DBUG_SWAP_CODE_STATE(&c->dbug_state); + SwitchToFiber(c->lib_fiber); + DBUG_SWAP_CODE_STATE(&c->dbug_state); + 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/mariadb-connector-c-v_2.3.7/libmariadb/my_div.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_div.c new file mode 100644 index 0000000..c1cf992 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_div.c @@ -0,0 +1,31 @@ +/* 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 */ + +#include "mysys_priv.h" + +my_string my_filename(File fd) +{ + DBUG_ENTER("my_filename"); + if (fd >= MY_NFILE) + DBUG_RETURN((char*) "UNKNOWN"); + if (fd >= 0 && my_file_info[fd].type != UNOPEN) + { + DBUG_RETURN(my_file_info[fd].name); + } + else + DBUG_RETURN((char*) "UNOPENED"); /* Debug message */ +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_error.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_error.c new file mode 100644 index 0000000..f88c4be --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_error.c @@ -0,0 +1,124 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include +#include + +/* Define some external variables for error handling */ + +const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0}; +char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; + +/* Error message to user */ +/*VARARGS2*/ + +int my_error(int nr,myf MyFlags, ...) +{ + va_list ap; + uint olen, plen; + reg1 const char *tpos; + reg2 char *endpos; + char * par; + char ebuff[ERRMSGSIZE+20]; + DBUG_ENTER("my_error"); + + va_start(ap,MyFlags); + DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno)); + + if (nr / ERRMOD == GLOB && my_errmsg[GLOB] == 0) + init_glob_errs(); + + olen=(uint) strlen(tpos=my_errmsg[nr / ERRMOD][nr % ERRMOD - EE_FIRSTERROR]); + endpos=ebuff; + + while (*tpos) + { + if (tpos[0] != '%') + { + *endpos++= *tpos++; /* Copy ordinary char */ + olen++; + continue; + } + if (*++tpos == '%') /* test if %% */ + { + olen--; + } + else + { + /* Skipp if max size is used (to be compatible with printf) */ + while (isdigit(*tpos) || *tpos == '.' || *tpos == '-') + tpos++; + if (*tpos == 'l') /* Skipp 'l' argument */ + tpos++; + if (*tpos == 's') /* String parameter */ + { + par = va_arg(ap, char *); + plen = (uint) strlen(par); + if (olen + plen < ERRMSGSIZE+2) /* Replace if possible */ + { + endpos=strmov(endpos,par); + tpos++; + olen+=plen-2; + continue; + } + } + else if (*tpos == 'd' || *tpos == 'u') /* Integer parameter */ + { + register int iarg; + iarg = va_arg(ap, int); + if (*tpos == 'd') + plen= (uint) (int2str((long) iarg,endpos, -10) - endpos); + else + plen= (uint) (int2str((long) (uint) iarg,endpos,10)- endpos); + if (olen + plen < ERRMSGSIZE+2) /* Replace parameter if possible */ + { + endpos+=plen; + tpos++; + olen+=plen-2; + continue; + } + } + } + *endpos++='%'; /* % used as % or unknown code */ + } + *endpos='\0'; /* End of errmessage */ + va_end(ap); + DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); +} + + /* Error as printf */ + +int my_printf_error (uint error, const char *format, myf MyFlags, ...) +{ + va_list args; + char ebuff[ERRMSGSIZE+20]; + + va_start(args,MyFlags); + (void) vsprintf (ebuff,format,args); + va_end(args); + return (*error_handler_hook)(error, ebuff, MyFlags); +} + + /* Give message using error_handler_hook */ + +int my_message(uint error, const char *str, register myf MyFlags) +{ + return (*error_handler_hook)(error, str, MyFlags); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_fopen.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_fopen.c new file mode 100644 index 0000000..c99b753 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_fopen.c @@ -0,0 +1,178 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "my_static.h" +#include +#include "mysys_err.h" + +static void make_ftype(my_string to,int flag); + + /* Open a file as stream */ + +FILE *my_fopen(const char *FileName, int Flags, myf MyFlags) + /* Path-name of file */ + /* Read | write .. */ + /* Special flags */ +{ + FILE *fd; + char type[5]; + DBUG_ENTER("my_fopen"); + DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d", + FileName, Flags, MyFlags)); + + make_ftype(type,Flags); +#ifdef _WIN32 + if (fopen_s(&fd, FileName, type) == 0) +#else + if ((fd = fopen(FileName, type)) != 0) +#endif + { + /* + The test works if MY_NFILE < 128. The problem is that fileno() is char + on some OS (SUNOS). Actually the filename save isn't that important + so we can ignore if this doesn't work. + */ + if ((uint) fileno(fd) >= MY_NFILE) + { + thread_safe_increment(my_stream_opened,&THR_LOCK_open); + DBUG_RETURN(fd); /* safeguard */ + } + pthread_mutex_lock(&THR_LOCK_open); + if ((my_file_info[fileno(fd)].name = (char*) + my_strdup(FileName,MyFlags))) + { + my_stream_opened++; + my_file_info[fileno(fd)].type = STREAM_BY_FOPEN; + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("stream: %lx",fd)); + DBUG_RETURN(fd); + } + pthread_mutex_unlock(&THR_LOCK_open); + (void) my_fclose(fd,MyFlags); + my_errno=ENOMEM; + } + else + my_errno=errno; + DBUG_PRINT("error",("Got error %d on open",my_errno)); + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + my_error((Flags & O_RDONLY) || (Flags == O_RDONLY ) ? EE_FILENOTFOUND : + EE_CANTCREATEFILE, + MYF(ME_BELL+ME_WAITTANG), FileName,my_errno); + DBUG_RETURN((FILE*) 0); +} /* my_fopen */ + + + /* Close a stream */ + +int my_fclose(FILE *fd, myf MyFlags) +{ + int err,file; + DBUG_ENTER("my_fclose"); + DBUG_PRINT("my",("stream: %lx MyFlags: %d",fd, MyFlags)); + + pthread_mutex_lock(&THR_LOCK_open); + file=fileno(fd); + if ((err = fclose(fd)) < 0) + { + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG), + my_filename(file),errno); + } + else + my_stream_opened--; + if ((uint) file < MY_NFILE && my_file_info[file].type != UNOPEN) + { + my_file_info[file].type = UNOPEN; + my_free(my_file_info[file].name); + } + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_RETURN(err); +} /* my_fclose */ + + + /* Make a stream out of a file handle */ + /* Name may be 0 */ + +FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) +{ + FILE *fd; + char type[5]; + DBUG_ENTER("my_fdopen"); + DBUG_PRINT("my",("Fd: %d Flags: %d MyFlags: %d", + Filedes, Flags, MyFlags)); + + make_ftype(type,Flags); + if ((fd = fdopen(Filedes, type)) == 0) + { + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_CANT_OPEN_STREAM, MYF(ME_BELL+ME_WAITTANG),errno); + } + else + { + pthread_mutex_lock(&THR_LOCK_open); + my_stream_opened++; + if (Filedes < MY_NFILE) + { + if (my_file_info[Filedes].type != UNOPEN) + { + my_file_opened--; /* File is opened with my_open ! */ + } + else + { + my_file_info[Filedes].name= my_strdup(name,MyFlags); + } + my_file_info[Filedes].type = STREAM_BY_FDOPEN; + } + pthread_mutex_unlock(&THR_LOCK_open); + } + + DBUG_PRINT("exit",("stream: %lx",fd)); + DBUG_RETURN(fd); +} /* my_fdopen */ + + + /* Make a filehandler-open-typestring from ordinary inputflags */ + +static void make_ftype(register my_string to, register int flag) +{ +#if FILE_BINARY /* If we have binary-files */ + reg3 int org_flag=flag; +#endif + flag&= ~FILE_BINARY; /* remove binary bit */ + if (flag == O_RDONLY) + *to++= 'r'; + else if (flag == O_WRONLY) + *to++= 'w'; + else + { /* Add '+' after theese */ + if (flag == O_RDWR) + *to++= 'r'; + else if (flag & O_APPEND) + *to++= 'a'; + else + *to++= 'w'; /* Create file */ + *to++= '+'; + } +#if FILE_BINARY /* If we have binary-files */ + if (org_flag & FILE_BINARY) + *to++='b'; +#endif + *to='\0'; +} /* make_ftype */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_fstream.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_fstream.c new file mode 100644 index 0000000..93f482e --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_fstream.c @@ -0,0 +1,171 @@ +/* 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 */ + +/* USE_MY_STREAM isn't set because we can't thrust my_fclose! */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include + +#ifdef HAVE_FSEEKO +#undef ftell +#undef fseek +#define ftell(A) ftello(A) +#define fseek(A,B,C) fseeko((A),(B),(C)) +#endif + + /* Read a chunk of bytes from a file */ + /* Returns (uint) -1 if error as my_read() */ + +uint my_fread(FILE *stream, unsigned char *Buffer, uint Count, myf MyFlags) + /* File descriptor */ + /* Buffer must be at least count bytes */ + /* Max number of bytes returnd */ + /* Flags on what to do on error */ +{ + uint readbytes; + DBUG_ENTER("my_fread"); + DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d", + stream, Buffer, Count, MyFlags)); + + if ((readbytes = (uint) fread(Buffer,sizeof(char),(size_t) Count,stream)) + != Count) + { + DBUG_PRINT("error",("Read only %d bytes",readbytes)); + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + if (ferror(stream)) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(fileno(stream)),errno); + else + if (MyFlags & (MY_NABP | MY_FNABP)) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(fileno(stream)),errno); + } + my_errno=errno ? errno : -1; + if (ferror(stream) || MyFlags & (MY_NABP | MY_FNABP)) + DBUG_RETURN((uint) -1); /* Return with error */ + } + if (MyFlags & (MY_NABP | MY_FNABP)) + DBUG_RETURN(0); /* Read ok */ + DBUG_RETURN(readbytes); +} /* my_fread */ + + +/* +** Write a chunk of bytes to a stream +** Returns (uint) -1 if error as my_write() +** Does retries if interrupted +*/ + +uint my_fwrite(FILE *stream, const unsigned char *Buffer, uint Count, myf MyFlags) +{ + uint writenbytes=0; + off_t seekptr; +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) + uint errors; +#endif + DBUG_ENTER("my_fwrite"); + DBUG_PRINT("my",("stream: %lx Buffer: %lx Count: %u MyFlags: %d", + stream, Buffer, Count, MyFlags)); + +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) + errors=0; +#endif + seekptr=ftell(stream); + for (;;) + { + uint writen; + if ((writen = (uint) fwrite((char*) Buffer,sizeof(char), + (size_t) Count, stream)) != Count) + { + DBUG_PRINT("error",("Write only %d bytes",writenbytes)); + my_errno=errno; + if (writen != (uint) -1) + { + seekptr+=writen; + Buffer+=writen; + writenbytes+=writen; + Count-=writen; + } +#ifdef EINTR + if (errno == EINTR) + { + VOID(my_fseek(stream,seekptr,MY_SEEK_SET,MYF(0))); + continue; + } +#endif +#if !defined(NO_BACKGROUND) && defined(USE_MY_STREAM) +#ifdef THREAD + if (my_thread_var->abort) + MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ +#endif + if (errno == ENOSPC && (MyFlags & MY_WAIT_IF_FULL)) + { + if (!(errors++ % MY_WAIT_GIVE_USER_A_MESSAGE)) + my_error(EE_DISK_FULL,MYF(ME_BELL | ME_NOREFRESH)); + sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC); + VOID(my_fseek(stream,seekptr,MY_SEEK_SET,MYF(0))); + continue; + } +#endif + if (ferror(stream) || (MyFlags & (MY_NABP | MY_FNABP))) + { + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG), + my_filename(fileno(stream)),errno); + } + writenbytes=(uint) -1; /* Return that we got error */ + break; + } + } + if (MyFlags & (MY_NABP | MY_FNABP)) + writenbytes=0; /* Everything OK */ + else + writenbytes+=writen; + break; + } + DBUG_RETURN(writenbytes); +} /* my_fwrite */ + + /* Seek to position in file */ + /* ARGSUSED */ + +my_off_t my_fseek(FILE *stream, my_off_t pos, int whence, myf MyFlags) +{ + DBUG_ENTER("my_fseek"); + DBUG_PRINT("my",("stream: %lx pos: %lu whence: %d MyFlags: %d", + stream, pos, whence, MyFlags)); + DBUG_RETURN(fseek(stream, (off_t) pos, whence) ? + MY_FILEPOS_ERROR : (my_off_t) ftell(stream)); +} /* my_seek */ + + + /* Tell current position of file */ + /* ARGSUSED */ + +my_off_t my_ftell(FILE *stream, myf MyFlags) +{ + off_t pos; + DBUG_ENTER("my_ftell"); + DBUG_PRINT("my",("stream: %lx MyFlags: %d",stream, MyFlags)); + pos=ftell(stream); + DBUG_PRINT("exit",("ftell: %lu",(ulong) pos)); + DBUG_RETURN((my_off_t) pos); +} /* my_ftell */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_getwd.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_getwd.c new file mode 100644 index 0000000..7b7a6b5 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_getwd.c @@ -0,0 +1,202 @@ +/* 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 */ + +/* my_setwd() and my_getwd() works with intern_filenames !! */ + +#include "mysys_priv.h" +#include +#include "mysys_err.h" +#ifdef HAVE_GETWD +#include +#endif +#if defined(MSDOS) || defined(_WIN32) +#include +#include +#include +#endif +#if defined(OS2) +#include +#endif + +#ifdef __EMX__ +// chdir2 support also drive change +#define chdir _chdir2 +#endif + + /* Gets current working directory in buff. Directory is allways ended + with FN_LIBCHAR */ + /* One must pass a buffer to my_getwd. One can allways use + curr_dir[] */ + +int my_getwd(my_string buf, uint size, myf MyFlags) +{ + my_string pos; + DBUG_ENTER("my_getwd"); + DBUG_PRINT("my",("buf: %lx size: %d MyFlags %d", buf,size,MyFlags)); + +#if ! defined(MSDOS) + if (curr_dir[0]) /* Current pos is saved here */ + VOID(strmake(buf,&curr_dir[0],size-1)); + else +#endif + { +#if defined(HAVE_GETCWD) +#ifdef _WIN32 + if (!(_getcwd(buf,size-2)) && MyFlags & MY_WME) +#else + if (!(getcwd(buf,size-2)) && MyFlags & MY_WME) +#endif + { + my_errno=errno; + my_error(EE_GETWD,MYF(ME_BELL+ME_WAITTANG),errno); + return(-1); + } +#elif defined(HAVE_GETWD) + { + char pathname[MAXPATHLEN]; + getwd(pathname); + strmake(buf,pathname,size-1); + } +#elif defined(VMS) + if (!getcwd(buf,size-2,1) && MyFlags & MY_WME) + { + my_errno=errno; + my_error(EE_GETWD,MYF(ME_BELL+ME_WAITTANG),errno); + return(-1); + } + intern_filename(buf,buf); +#else +#error "No way to get current directory" +#endif + if (*((pos=strend(buf))-1) != FN_LIBCHAR) /* End with FN_LIBCHAR */ + { + pos[0]= FN_LIBCHAR; + pos[1]=0; + } + (void) strmake(&curr_dir[0],buf,(size_s) (FN_REFLEN-1)); + } + DBUG_RETURN(0); +} /* my_getwd */ + + + /* Set new working directory */ + +int my_setwd(const char *dir, myf MyFlags) +{ + int res; + size_s length; + my_string start,pos; +#if defined(VMS) || defined(MSDOS) || defined(OS2) + char buff[FN_REFLEN]; +#endif + DBUG_ENTER("my_setwd"); + DBUG_PRINT("my",("dir: '%s' MyFlags %d", dir, MyFlags)); + + start=(my_string) dir; +#if defined(MSDOS) || defined(OS2) /* OS2/MSDOS chdir can't change drive */ +#if !defined(_DDL) && !defined(WIN32) + if ((pos=(char*) strchr(dir,FN_DEVCHAR)) != 0) + { + uint drive,drives; + + pos++; /* Skipp FN_DEVCHAR */ + drive=(uint) (toupper(dir[0])-'A'+1); drives= (uint) -1; + if ((pos-(byte*) dir) == 2 && drive > 0 && drive < 32) + { +#ifdef OS2 + _chdrive(drive); + drives = _getdrive(); +#else + _dos_setdrive(drive,&drives); + _dos_getdrive(&drives); +#endif + } + if (drive != drives) + { + *pos='\0'; /* Dir is now only drive */ + my_errno=errno; + my_error(EE_SETWD,MYF(ME_BELL+ME_WAITTANG),dir,ENOENT); + DBUG_RETURN(-1); + } + dir=pos; /* drive changed, change now path */ + } +#endif + if (*((pos=strend(dir)-1)) == FN_LIBCHAR && pos != dir) + { + strmov(buff,dir)[-1]=0; /* Remove last '/' */ + dir=buff; + } +#endif /* MSDOS*/ + if (! dir[0] || (dir[0] == FN_LIBCHAR && dir[1] == 0)) + dir=FN_ROOTDIR; +#ifdef VMS + { + pos=strmov(buff,dir); + if (pos[-1] != FN_LIBCHAR) + { + pos[0]=FN_LIBCHAR; /* Mark as directory */ + pos[1]=0; + } + system_filename(buff,buff); /* Change to VMS format */ + dir=buff; + } +#endif /* VMS */ +#ifdef _WIN32 + if ((res=_chdir((char*) dir)) != 0) +#else + if ((res=chdir((char*) dir)) != 0) +#endif + { + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_SETWD,MYF(ME_BELL+ME_WAITTANG),start,errno); + } + else + { + if (test_if_hard_path(start)) + { /* Hard pathname */ + pos=strmake(&curr_dir[0],start,(size_s) FN_REFLEN-1); + if (pos[-1] != FN_LIBCHAR) + { + length=(uint) (pos-(char*) curr_dir); + curr_dir[length]=FN_LIBCHAR; /* must end with '/' */ + curr_dir[length+1]='\0'; + } + } + else + curr_dir[0]='\0'; /* Don't save name */ + } + DBUG_RETURN(res); +} /* my_setwd */ + + + + /* Test if hard pathname */ + /* Returns 1 if dirname is a hard path */ + +int test_if_hard_path(register const char *dir_name) +{ + if (dir_name[0] == FN_HOMELIB && dir_name[1] == FN_LIBCHAR) + return (home_dir != NullS && test_if_hard_path(home_dir)); + if (dir_name[0] == FN_LIBCHAR) + return (TRUE); +#ifdef FN_DEVCHAR + return (strchr(dir_name,FN_DEVCHAR) != 0); +#else + return FALSE; +#endif +} /* test_if_hard_path */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_init.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_init.c new file mode 100644 index 0000000..7d70f22 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_init.c @@ -0,0 +1,273 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "my_static.h" +#include "mysys_err.h" +#include "m_ctype.h" +#include +#include +#ifdef HAVE_GETRUSAGE +#include +/* extern int getrusage(int, struct rusage *); */ +#endif +#include +#ifdef VMS +#include +#include +#endif +#ifdef _WIN32 +#ifdef _MSC_VER +#include +#include +#endif +my_bool have_tcpip=0; +static void my_win_init(void); +static my_bool win32_have_tcpip(void); +static my_bool win32_init_tcp_ip(); +#else +#define my_win_init() +#endif + +my_bool my_init_done=0; + + + +static ulong atoi_octal(const char *str) +{ + long int tmp; + while (*str && isspace(*str)) + str++; + str2int(str, + (*str == '0' ? 8 : 10), /* Octalt or decimalt */ + 0, INT_MAX, &tmp); + return (ulong) tmp; +} + + + /* Init my_sys functions and my_sys variabels */ + +void my_init(void) +{ + my_string str; + if (my_init_done) + return; + my_init_done=1; +#ifdef THREAD +#if defined(HAVE_PTHREAD_INIT) + pthread_init(); /* Must be called before DBUG_ENTER */ +#endif + my_thread_global_init(); +#ifndef _WIN32 + sigfillset(&my_signals); /* signals blocked by mf_brkhant */ +#endif +#endif /* THREAD */ +#ifdef UNIXWARE_7 + (void) isatty(0); /* Go around connect() bug in UW7 */ +#endif + { + DBUG_ENTER("my_init"); + DBUG_PROCESS(my_progname ? my_progname : (char*) "unknown"); + if (!home_dir) + { /* Don't initialize twice */ + if ((home_dir=getenv("HOME")) != 0) + home_dir=intern_filename(home_dir_buff,home_dir); +#ifndef VMS + /* Default creation of new files */ + if ((str=getenv("UMASK")) != 0) + my_umask=(int) (atoi_octal(str) | 0600); + /* Default creation of new dir's */ + if ((str=getenv("UMASK_DIR")) != 0) + my_umask_dir=(int) (atoi_octal(str) | 0700); +#endif +#ifdef VMS + init_ctype(); /* Stupid linker don't link _ctype.c */ +#endif + DBUG_PRINT("exit",("home: '%s'",home_dir)); + } +#ifdef _WIN32 + my_win_init(); +#endif + DBUG_VOID_RETURN; + } +} /* my_init */ + + + /* End my_sys */ + +void my_end(int infoflag) +{ + FILE *info_file; + if (!(info_file=DBUG_FILE)) + info_file=stderr; + if (infoflag & MY_CHECK_ERROR || info_file != stderr) + { /* Test if some file is left open */ + if (my_file_opened | my_stream_opened) + { + sprintf(errbuff[0],EE(EE_OPEN_WARNING),my_file_opened,my_stream_opened); + (void) my_message_no_curses(EE_OPEN_WARNING,errbuff[0],ME_BELL); + DBUG_PRINT("error",("%s",errbuff[0])); + } + } + if (infoflag & MY_GIVE_INFO || info_file != stderr) + { +#ifdef HAVE_GETRUSAGE + struct rusage rus; + if (!getrusage(RUSAGE_SELF, &rus)) + fprintf(info_file,"\n\ +User time %.2f, System time %.2f\n\ +Maximum resident set size %ld, Integral resident set size %ld\n\ +Non-physical pagefaults %ld, Physical pagefaults %ld, Swaps %ld\n\ +Blocks in %ld out %ld, Messages in %ld out %ld, Signals %ld\n\ +Voluntary context switches %ld, Involuntary context switches %ld\n", + (rus.ru_utime.tv_sec * SCALE_SEC + + rus.ru_utime.tv_usec / SCALE_USEC) / 100.0, + (rus.ru_stime.tv_sec * SCALE_SEC + + rus.ru_stime.tv_usec / SCALE_USEC) / 100.0, + rus.ru_maxrss, rus.ru_idrss, + rus.ru_minflt, rus.ru_majflt, + rus.ru_nswap, rus.ru_inblock, rus.ru_oublock, + rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals, + rus.ru_nvcsw, rus.ru_nivcsw); +#endif +#if defined(MSDOS) && !defined(_WIN32) + fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC); +#endif +#if defined(SAFEMALLOC) + TERMINATE(stderr); /* Give statistic on screen */ +#elif defined(_WIN32) && defined(_MSC_VER) + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); + _CrtCheckMemory(); + _CrtDumpMemoryLeaks(); +#endif + } +#ifdef THREAD + pthread_mutex_destroy(&THR_LOCK_malloc); + pthread_mutex_destroy(&THR_LOCK_open); + pthread_mutex_destroy(&THR_LOCK_net); + DBUG_END(); /* Must be done before my_thread_end */ + my_thread_end(); + my_thread_global_end(); +#endif +#ifdef _WIN32 + if (have_tcpip); + WSACleanup( ); +#endif /* _WIN32 */ + my_init_done=0; +} /* my_end */ + +#ifdef _WIN32 + +/* + This code is specially for running MySQL, but it should work in + other cases too. + + Inizializzazione delle variabili d'ambiente per Win a 32 bit. + + Vengono inserite nelle variabili d'ambiente (utilizzando cosi' + le funzioni getenv e putenv) i valori presenti nelle chiavi + del file di registro: + + HKEY_LOCAL_MACHINE\software\MySQL + + Se la kiave non esiste nonn inserisce nessun valore +*/ + +/* Crea la stringa d'ambiente */ + +void setEnvString(char *ret, const char *name, const char *value) +{ + DBUG_ENTER("setEnvString"); + strxmov(ret, name,"=",value,NullS); + DBUG_VOID_RETURN ; +} + +static void my_win_init(void) +{ + DBUG_ENTER("my_win_init"); + win32_init_tcp_ip(); + DBUG_VOID_RETURN ; +} + + +/*------------------------------------------------------------------ +** Name: CheckForTcpip| Desc: checks if tcpip has been installed on system +** According to Microsoft Developers documentation the first registry +** entry should be enough to check if TCP/IP is installed, but as expected +** this doesn't work on all Win32 machines :( +------------------------------------------------------------------*/ + +#define TCPIPKEY "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" +#define WINSOCK2KEY "SYSTEM\\CurrentControlSet\\Services\\Winsock2\\Parameters" +#define WINSOCKKEY "SYSTEM\\CurrentControlSet\\Services\\Winsock\\Parameters" + +static my_bool win32_have_tcpip(void) +{ + HKEY hTcpipRegKey; + if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TCPIPKEY, 0, KEY_READ, + &hTcpipRegKey) != ERROR_SUCCESS) + { + if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCK2KEY, 0, KEY_READ, + &hTcpipRegKey) != ERROR_SUCCESS) + { + if (RegOpenKeyEx ( HKEY_LOCAL_MACHINE, WINSOCKKEY, 0, KEY_READ, + &hTcpipRegKey) != ERROR_SUCCESS) + if (!getenv("HAVE_TCPIP") || have_tcpip) /* Provide a workaround */ + return (FALSE); + } + } + RegCloseKey ( hTcpipRegKey); + return (TRUE); +} + +static my_bool win32_init_tcp_ip() +{ + if (win32_have_tcpip()) + { + WORD wVersionRequested = MAKEWORD( 2, 0 ); + WSADATA wsaData; + /* Be a good citizen: maybe another lib has already initialised + sockets, so dont clobber them unless necessary */ + if (WSAStartup( wVersionRequested, &wsaData )) + { + /* Load failed, maybe because of previously loaded + incompatible version; try again */ + WSACleanup( ); + if (!WSAStartup( wVersionRequested, &wsaData )) + have_tcpip=1; + } + else + { + if (wsaData.wVersion != wVersionRequested) + { + /* Version is no good, try again */ + WSACleanup( ); + if (!WSAStartup( wVersionRequested, &wsaData )) + have_tcpip=1; + } + else + have_tcpip=1; + } + } + return(0); +} +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_lib.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_lib.c new file mode 100644 index 0000000..c5e1dbd --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_lib.c @@ -0,0 +1,614 @@ +/* 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 */ + +/* TODO: check for overun of memory for names. */ +/* Convert MSDOS-TIME to standar time_t */ + +#define USES_TYPES /* sys/types is included */ +#include "mysys_priv.h" +#include +#include /* Structs used by my_dir,includes sys/types */ +#include "mysys_err.h" +#if defined(HAVE_DIRENT_H) +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#ifndef OS2 +# define dirent direct +#endif +# define NAMLEN(dirent) (dirent)->d_namlen +# if defined(HAVE_SYS_NDIR_H) +# include +# endif +# if defined(HAVE_SYS_DIR_H) +# include +# endif +# if defined(HAVE_NDIR_H) +# include +# endif +# if defined(MSDOS) || defined(_WIN32) +# include +# ifdef __BORLANDC__ +# include +# endif +# endif +#endif +#ifdef VMS +#include +#include +#include +#endif + +#ifdef OS2 +#include "my_os2dirsrch.h" +#endif + +#if defined(THREAD) && defined(HAVE_READDIR_R) +#define READDIR(A,B,C) ((errno=readdir_r(A,B,&C)) != 0 || !C) +#else +#define READDIR(A,B,C) (!(C=readdir(A))) +#endif + + +#define STARTSIZE ONCE_ALLOC_INIT*8 /* some mallocmargin */ + +static int comp_names(struct fileinfo *a,struct fileinfo *b); + + + /* We need this because program don't know with malloc we used */ + +void my_dirend(MY_DIR *buffer) +{ + DBUG_ENTER("my_dirend"); + if (buffer) + my_free(buffer); + DBUG_VOID_RETURN; +} /* my_dirend */ + + + /* Compare in sort of filenames */ + +static int comp_names(struct fileinfo *a, struct fileinfo *b) +{ + return (strcmp(a->name,b->name)); +} /* comp_names */ + + +#if !defined(MSDOS) && !defined(_WIN32) + +MY_DIR *my_dir(const char *path, myf MyFlags) +{ + DIR *dirp; + struct dirent *dp; + struct fileinfo *fnames; + char *buffer, *obuffer, *tempptr; + uint fcnt,i,size,firstfcnt, maxfcnt,length; + char tmp_path[FN_REFLEN+1],*tmp_file; + my_ptrdiff_t diff; + bool eof; +#ifdef THREAD + char dirent_tmp[sizeof(struct dirent)+_POSIX_PATH_MAX+1]; +#endif + DBUG_ENTER("my_dir"); + DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags)); + +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_lock(&THR_LOCK_open); +#endif + + dirp = opendir(directory_file_name(tmp_path,(my_string) path)); + size = STARTSIZE; + if (dirp == NULL || ! (buffer = (char *) my_malloc(size, MyFlags))) + goto error; + + fcnt = 0; + tmp_file=strend(tmp_path); + firstfcnt = maxfcnt = (size - sizeof(MY_DIR)) / + (sizeof(struct fileinfo) + FN_LEN); + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr = (char *) (fnames + maxfcnt); + +#ifdef THREAD + dp= (struct dirent*) dirent_tmp; +#else + dp=0; +#endif + eof=0; + for (;;) + { + while (fcnt < maxfcnt && + !(eof= READDIR(dirp,(struct dirent*) dirent_tmp,dp))) + { + bzero((gptr) (fnames+fcnt),sizeof(fnames[0])); /* for purify */ + fnames[fcnt].name = tempptr; + tempptr = strmov(tempptr,dp->d_name) + 1; + if (MyFlags & MY_WANT_STAT) + { + (void)strmov(tmp_file,dp->d_name); + (void)my_stat(tmp_path, &fnames[fcnt].mystat, MyFlags); + } + ++fcnt; + } + if (eof) + break; + size += STARTSIZE; obuffer = buffer; + if (!(buffer = (char *) my_realloc((gptr) buffer, size, + MyFlags | MY_FREE_ON_ERROR))) + goto error; /* No memory */ + length= (uint) (sizeof(struct fileinfo ) * firstfcnt); + diff= PTR_BYTE_DIFF(buffer , obuffer) + (int) length; + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr= ADD_TO_PTR(tempptr,diff,char*); + for (i = 0; i < maxfcnt; i++) + fnames[i].name = ADD_TO_PTR(fnames[i].name,diff,char*); + + /* move filenames upp a bit */ + maxfcnt += firstfcnt; + bmove_upp(tempptr,tempptr-length, + (uint) (tempptr- (char*) (fnames+maxfcnt))); + } + + (void) closedir(dirp); + { + MY_DIR * s = (MY_DIR *) buffer; + s->number_off_files = (uint) fcnt; + s->dir_entry = fnames; + } + if (!(MyFlags & MY_DONT_SORT)) + qsort((void *) fnames, (size_s) fcnt, sizeof(struct fileinfo), + (qsort_cmp) comp_names); +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_unlock(&THR_LOCK_open); +#endif + DBUG_RETURN((MY_DIR *) buffer); + + error: +#if defined(THREAD) && !defined(HAVE_READDIR_R) + pthread_mutex_unlock(&THR_LOCK_open); +#endif + my_errno=errno; + if (dirp) + (void) closedir(dirp); + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,my_errno); + DBUG_RETURN((MY_DIR *) NULL); +} /* my_dir */ + + +/* + * Convert from directory name to filename. + * On VMS: + * xyzzy:[mukesh.emacs] => xyzzy:[mukesh]emacs.dir.1 + * xyzzy:[mukesh] => xyzzy:[000000]mukesh.dir.1 + * On UNIX, it's simple: just make sure there is a terminating / + + * Returns pointer to dst; + */ + +my_string directory_file_name (my_string dst, const char *src) +{ +#ifndef VMS + + /* Process as Unix format: just remove test the final slash. */ + + my_string end; + + if (src[0] == 0) + src= (char*) "."; /* Use empty as current */ + end=strmov(dst, src); + if (end[-1] != FN_LIBCHAR) + { + end[0]=FN_LIBCHAR; /* Add last '/' */ + end[1]='\0'; + } + return dst; + +#else /* VMS */ + + long slen; + long rlen; + my_string ptr, rptr; + char bracket; + struct FAB fab = cc$rms_fab; + struct NAM nam = cc$rms_nam; + char esa[NAM$C_MAXRSS]; + + if (! src[0]) + src="[.]"; /* Empty is == current dir */ + + slen = strlen (src) - 1; + if (src[slen] == FN_C_AFTER_DIR || src[slen] == FN_C_AFTER_DIR_2 || + src[slen] == FN_DEVCHAR) + { + /* VMS style - convert [x.y.z] to [x.y]z, [x] to [000000]x */ + fab.fab$l_fna = src; + fab.fab$b_fns = slen + 1; + fab.fab$l_nam = &nam; + fab.fab$l_fop = FAB$M_NAM; + + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + nam.nam$b_nop |= NAM$M_SYNCHK; + + /* We call SYS$PARSE to handle such things as [--] for us. */ + if (SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL) + { + slen = nam.nam$b_esl - 1; + if (esa[slen] == ';' && esa[slen - 1] == '.') + slen -= 2; + esa[slen + 1] = '\0'; + src = esa; + } + if (src[slen] != FN_C_AFTER_DIR && src[slen] != FN_C_AFTER_DIR_2) + { + /* what about when we have logical_name:???? */ + if (src[slen] == FN_DEVCHAR) + { /* Xlate logical name and see what we get */ + VOID(strmov(dst,src)); + dst[slen] = 0; /* remove colon */ + if (!(src = getenv (dst))) + return dst; /* Can't translate */ + + /* should we jump to the beginning of this procedure? + Good points: allows us to use logical names that xlate + to Unix names, + Bad points: can be a problem if we just translated to a device + name... + For now, I'll punt and always expect VMS names, and hope for + the best! */ + + slen = strlen (src) - 1; + if (src[slen] != FN_C_AFTER_DIR && src[slen] != FN_C_AFTER_DIR_2) + { /* no recursion here! */ + VOID(strmov(dst, src)); + return(dst); + } + } + else + { /* not a directory spec */ + VOID(strmov(dst, src)); + return(dst); + } + } + + bracket = src[slen]; /* End char */ + if (!(ptr = strchr (src, bracket - 2))) + { /* no opening bracket */ + VOID(strmov (dst, src)); + return dst; + } + if (!(rptr = strrchr (src, '.'))) + rptr = ptr; + slen = rptr - src; + VOID(strmake (dst, src, slen)); + + if (*rptr == '.') + { /* Put bracket and add */ + dst[slen++] = bracket; /* (rptr+1) after this */ + } + else + { + /* If we have the top-level of a rooted directory (i.e. xx:[000000]), + then translate the device and recurse. */ + + if (dst[slen - 1] == ':' + && dst[slen - 2] != ':' /* skip decnet nodes */ + && strcmp(src + slen, "[000000]") == 0) + { + dst[slen - 1] = '\0'; + if ((ptr = getenv (dst)) + && (rlen = strlen (ptr) - 1) > 0 + && (ptr[rlen] == FN_C_AFTER_DIR || ptr[rlen] == FN_C_AFTER_DIR_2) + && ptr[rlen - 1] == '.') + { + VOID(strmov(esa,ptr)); + esa[rlen - 1] = FN_C_AFTER_DIR; + esa[rlen] = '\0'; + return (directory_file_name (dst, esa)); + } + else + dst[slen - 1] = ':'; + } + VOID(strmov(dst+slen,"[000000]")); + slen += 8; + } + VOID(strmov(strmov(dst+slen,rptr+1)-1,".DIR.1")); + return dst; + } + VOID(strmov(dst, src)); + if (dst[slen] == '/' && slen > 1) + dst[slen] = 0; + return dst; +#endif /* VMS */ +} /* directory_file_name */ + +#elif defined(_WIN32) + +/* +***************************************************************************** +** Read long filename using windows rutines +***************************************************************************** +*/ + +MY_DIR *my_dir(const char *path, myf MyFlags) +{ + struct fileinfo *fnames; + char *buffer, *obuffer, *tempptr; + int eof,i,fcnt,firstfcnt,length,maxfcnt; + uint size; +#ifdef __BORLANDC__ + struct ffblk find; +#else + struct _finddata_t find; +#endif + ushort mode; + char tmp_path[FN_REFLEN],*tmp_file,attrib; + my_ptrdiff_t diff; +#ifdef _WIN64 + __int64 handle; +#else + long handle; +#endif + DBUG_ENTER("my_dir"); + DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags)); + + /* Put LIB-CHAR as last path-character if not there */ + + tmp_file=tmp_path; + if (!*path) + *tmp_file++ ='.'; /* From current dir */ + tmp_file= strmov(tmp_file,path); + if (tmp_file[-1] == FN_DEVCHAR) + *tmp_file++= '.'; /* From current dev-dir */ + if (tmp_file[-1] != FN_LIBCHAR) + *tmp_file++ =FN_LIBCHAR; + tmp_file[0]='*'; /* MSDOS needs this !??? */ + tmp_file[1]='.'; + tmp_file[2]='*'; + tmp_file[3]='\0'; + +#ifdef __BORLANDC__ + if ((handle= findfirst(tmp_path,&find,0)) == -1L) + goto error; +#else + if ((handle=_findfirst(tmp_path,&find)) == -1L) + goto error; +#endif + + size = STARTSIZE; + firstfcnt = maxfcnt = (size - sizeof(MY_DIR)) / + (sizeof(struct fileinfo) + FN_LEN); + if ((buffer = (char *) my_malloc(size, MyFlags)) == 0) + goto error; + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr = (char *) (fnames + maxfcnt); + + fcnt = 0; + for (;;) + { + do + { + fnames[fcnt].name = tempptr; +#ifdef __BORLANDC__ + tempptr = strmov(tempptr,find.ff_name) + 1; + fnames[fcnt].mystat.st_size=find.ff_fsize; + fnames[fcnt].mystat.st_uid=fnames[fcnt].mystat.st_gid=0; + mode=MY_S_IREAD; attrib=find.ff_attrib; +#else + tempptr = strmov(tempptr,find.name) + 1; + fnames[fcnt].mystat.st_size=find.size; + fnames[fcnt].mystat.st_uid=fnames[fcnt].mystat.st_gid=0; + mode=MY_S_IREAD; attrib=find.attrib; +#endif + if (!(attrib & _A_RDONLY)) + mode|=MY_S_IWRITE; + if (attrib & _A_SUBDIR) + mode|=MY_S_IFDIR; + fnames[fcnt].mystat.st_mode=mode; +#ifdef __BORLANDC__ + fnames[fcnt].mystat.st_mtime=((uint32) find.ff_ftime); +#else + fnames[fcnt].mystat.st_mtime=((uint32) find.time_write); +#endif + ++fcnt; +#ifdef __BORLANDC__ + } while ((eof= findnext(&find)) == 0 && fcnt < maxfcnt); +#else + } while ((eof= _findnext(handle,&find)) == 0 && fcnt < maxfcnt); +#endif + + DBUG_PRINT("test",("eof: %d errno: %d",eof,errno)); + if (eof) + break; + size += STARTSIZE; obuffer = buffer; + if (!(buffer = (char *) my_realloc((gptr) buffer, size, + MyFlags | MY_FREE_ON_ERROR))) + goto error; + length= sizeof(struct fileinfo ) * firstfcnt; + diff= PTR_BYTE_DIFF(buffer , obuffer) +length; + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr= ADD_TO_PTR(tempptr,diff,char*); + for (i = 0; i < maxfcnt; i++) + fnames[i].name = ADD_TO_PTR(fnames[i].name,diff,char*); + + /* move filenames upp a bit */ + maxfcnt += firstfcnt; + bmove_upp(tempptr,ADD_TO_PTR(tempptr,-length,char*), + (int) PTR_BYTE_DIFF(tempptr,fnames+maxfcnt)); + } + { + MY_DIR * s = (MY_DIR *) buffer; + s->number_off_files = (uint) fcnt; + s->dir_entry = fnames; + } + if (!(MyFlags & MY_DONT_SORT)) + qsort(fnames,fcnt,sizeof(struct fileinfo),(qsort_cmp) comp_names); +#ifndef __BORLANDC__ + _findclose(handle); +#endif + DBUG_RETURN((MY_DIR *) buffer); + +error: + my_errno=errno; +#ifndef __BORLANDC__ + if (handle != -1) + _findclose(handle); +#endif + if (MyFlags & MY_FAE+MY_WME) + my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,errno); + DBUG_RETURN((MY_DIR *) NULL); +} /* my_dir */ + +#else /* MSDOS and not WIN32 */ + + +/****************************************************************************** +** At MSDOS you always get stat of files, but time is in packed MSDOS-format +******************************************************************************/ + +MY_DIR *my_dir(const char* path, myf MyFlags) +{ + struct fileinfo *fnames; + char *buffer, *obuffer, *tempptr; + int eof,i,fcnt,firstfcnt,length,maxfcnt; + uint size; + struct find_t find; + ushort mode; + char tmp_path[FN_REFLEN],*tmp_file,attrib; + my_ptrdiff_t diff; + DBUG_ENTER("my_dir"); + DBUG_PRINT("my",("path: '%s' stat: %d MyFlags: %d",path,MyFlags)); + + /* Put LIB-CHAR as last path-character if not there */ + + tmp_file=tmp_path; + if (!*path) + *tmp_file++ ='.'; /* From current dir */ + tmp_file= strmov(tmp_file,path); + if (tmp_file[-1] == FN_DEVCHAR) + *tmp_file++= '.'; /* From current dev-dir */ + if (tmp_file[-1] != FN_LIBCHAR) + *tmp_file++ =FN_LIBCHAR; + tmp_file[0]='*'; /* MSDOS needs this !??? */ + tmp_file[1]='.'; + tmp_file[2]='*'; + tmp_file[3]='\0'; + + if (_dos_findfirst(tmp_path,_A_NORMAL | _A_SUBDIR, &find)) + goto error; + + size = STARTSIZE; + firstfcnt = maxfcnt = (size - sizeof(MY_DIR)) / + (sizeof(struct fileinfo) + FN_LEN); + if ((buffer = (char *) my_malloc(size, MyFlags)) == 0) + goto error; + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr = (char *) (fnames + maxfcnt); + + fcnt = 0; + for (;;) + { + do + { + fnames[fcnt].name = tempptr; + tempptr = strmov(tempptr,find.name) + 1; + fnames[fcnt].mystat.st_size=find.size; + fnames[fcnt].mystat.st_uid=fnames[fcnt].mystat.st_gid=0; + mode=MY_S_IREAD; attrib=find.attrib; + if (!(attrib & _A_RDONLY)) + mode|=MY_S_IWRITE; + if (attrib & _A_SUBDIR) + mode|=MY_S_IFDIR; + fnames[fcnt].mystat.st_mode=mode; + fnames[fcnt].mystat.st_mtime=((uint32) find.wr_date << 16) + + find.wr_time; + ++fcnt; + } while ((eof= _dos_findnext(&find)) == 0 && fcnt < maxfcnt); + + DBUG_PRINT("test",("eof: %d errno: %d",eof,errno)); + if (eof) + break; + size += STARTSIZE; obuffer = buffer; + if (!(buffer = (char *) my_realloc((gptr) buffer, size, + MyFlags | MY_FREE_ON_ERROR))) + goto error; + length= sizeof(struct fileinfo ) * firstfcnt; + diff= PTR_BYTE_DIFF(buffer , obuffer) +length; + fnames= (struct fileinfo *) (buffer + sizeof(MY_DIR)); + tempptr= ADD_TO_PTR(tempptr,diff,char*); + for (i = 0; i < maxfcnt; i++) + fnames[i].name = ADD_TO_PTR(fnames[i].name,diff,char*); + + /* move filenames upp a bit */ + maxfcnt += firstfcnt; + bmove_upp(tempptr,ADD_TO_PTR(tempptr,-length,char*), + (int) PTR_BYTE_DIFF(tempptr,fnames+maxfcnt)); + } + { + MY_DIR * s = (MY_DIR *) buffer; + s->number_off_files = (uint) fcnt; + s->dir_entry = fnames; + } + if (!(MyFlags & MY_DONT_SORT)) + qsort(fnames,fcnt,sizeof(struct fileinfo),(qsort_cmp) comp_names); + DBUG_RETURN((MY_DIR *) buffer); + +error: + if (MyFlags & MY_FAE+MY_WME) + my_error(EE_DIR,MYF(ME_BELL+ME_WAITTANG),path,errno); + DBUG_RETURN((MY_DIR *) NULL); +} /* my_dir */ + +#endif /* WIN32 && MSDOS */ + +/**************************************************************************** +** File status +** Note that MY_STAT is assumed to be same as struct stat +****************************************************************************/ + +int my_fstat(int Filedes, MY_STAT *stat_area, myf MyFlags ) +{ + DBUG_ENTER("my_fstat"); + DBUG_PRINT("my",("fd: %d MyFlags: %d",Filedes,MyFlags)); + DBUG_RETURN(fstat(Filedes, (struct stat *) stat_area)); +} + +MY_STAT *my_stat(const char *path, MY_STAT *stat_area, myf my_flags) +{ + int m_used; + DBUG_ENTER("my_stat"); + DBUG_PRINT("my", ("path: '%s', stat_area: %lx, MyFlags: %d", path, + (unsigned char *) stat_area, my_flags)); + + if ((m_used= (stat_area == NULL))) + if (!(stat_area = (MY_STAT *) my_malloc(sizeof(MY_STAT), my_flags))) + goto error; + if ( ! stat((my_string) path, (struct stat *) stat_area) ) + DBUG_RETURN(stat_area); + my_errno=errno; + if (m_used) /* Free if new area */ + my_free(stat_area); + +error: + if (my_flags & (MY_FAE+MY_WME)) + { + my_error(EE_STAT, MYF(ME_BELL+ME_WAITTANG),path,my_errno); + DBUG_RETURN((MY_STAT *) NULL); + } + DBUG_RETURN((MY_STAT *) NULL); +} /* my_stat */ + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_loaddata.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_loaddata.c new file mode 100644 index 0000000..1411721 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_loaddata.c @@ -0,0 +1,304 @@ +/************************************************************************************ + 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 + 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ +*/ + +#include "my_global.h" +#include +#include +#include +#include "errmsg.h" +#include "mysql.h" +#include +#ifdef _WIN32 +#include +#endif + +typedef struct st_mysql_infile_info +{ + int fd; + 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; + int CodePage= -1; +#ifdef _WIN32 + MYSQL *mysql= (MYSQL *)userdata; + wchar_t *w_filename= NULL; + int Length; +#endif + DBUG_ENTER("mysql_local_infile_init"); + + info = (MYSQL_INFILE_INFO *)my_malloc(sizeof(MYSQL_INFILE_INFO), MYF(MY_ZEROFILL)); + if (!info) { + DBUG_RETURN(1); + } + + *ptr = info; + + info->filename = filename; + +#ifdef _WIN32 + if (mysql) + CodePage= madb_get_windows_cp(mysql->charset->csname); +#endif + if (CodePage == -1) + { +#ifdef _WIN32 + info->fd= sopen(info->filename, _O_RDONLY | _O_BINARY, _SH_DENYNO , _S_IREAD | _S_IWRITE); +#else + info->fd = open(info->filename, O_RDONLY | O_BINARY, my_umask); +#endif + my_errno= errno; + } +#ifdef _WIN32 + else + { + if ((Length= MultiByteToWideChar(CodePage, 0, info->filename, (int)strlen(info->filename), NULL, 0))) + { + if (!(w_filename= (wchar_t *)my_malloc((Length + 1) * sizeof(wchar_t), MYF(MY_ZEROFILL)))) + { + info->error_no= CR_OUT_OF_MEMORY; + my_snprintf((char *)info->error_msg, sizeof(info->error_msg), + ER(CR_OUT_OF_MEMORY)); + DBUG_RETURN(1); + } + Length= MultiByteToWideChar(CodePage, 0, info->filename, (int)strlen(info->filename), w_filename, (int)Length); + } + if (Length == 0) + { + my_free(w_filename); + info->error_no= CR_UNKNOWN_ERROR; + my_snprintf((char *)info->error_msg, sizeof(info->error_msg), + "Character conversion error: %d", GetLastError()); + DBUG_RETURN(1); + } + info->fd= _wsopen(w_filename, _O_RDONLY | _O_BINARY, _SH_DENYNO , _S_IREAD | _S_IWRITE); + my_errno= errno; + my_free(w_filename); + } +#endif + + if (info->fd < 0) + { + info->error_no = my_errno; + my_snprintf((char *)info->error_msg, sizeof(info->error_msg), + EE(EE_FILENOTFOUND), filename, info->error_no); + DBUG_RETURN(1); + } + DBUG_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; + int count; + + DBUG_ENTER("mysql_local_infile_read"); + + count= read(info->fd, (void *)buf, (size_t)buf_len); + + if (count < 0) + { + strcpy(info->error_msg, "Error reading file"); + info->error_no = EE_READ; + } + DBUG_RETURN(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; + + DBUG_ENTER("mysql_local_infile_error"); + + if (info) { + strncpy(error_buf, info->error_msg, error_buf_len); + DBUG_RETURN(info->error_no); + } + + strncpy(error_buf, "Unknown error", error_buf_len); + DBUG_RETURN(CR_UNKNOWN_ERROR); +} +/* }}} */ + + +/* {{{ mysql_local_infile_end */ +static +void mysql_local_infile_end(void *ptr) +{ + MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr; + + DBUG_ENTER("mysql_local_infile_end"); + + if (info) + { + if (info->fd >= 0) + close(info->fd); + my_free(ptr); + } + DBUG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysql_local_infile_default */ +void mysql_set_local_infile_default(MYSQL *conn) +{ + DBUG_ENTER("mysql_local_infile_default"); + 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; + DBUG_VOID_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) +{ + DBUG_ENTER("mysql_set_local_infile_handler"); + 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[0] = userdata; + DBUG_VOID_RETURN; +} +/* }}} */ + +/* {{{ mysql_handle_local_infile */ +my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename) +{ + unsigned int buflen= 4096; + int bufread; + unsigned char *buf= NULL; + void *info= NULL; + my_bool result= 1; + + DBUG_ENTER("mysql_handle_local_infile"); + + /* 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[0]= conn; + mysql_set_local_infile_default(conn); + } + + if (!(conn->options.client_flag & CLIENT_LOCAL_FILES)) { + my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden"); + /* write empty packet to server */ + my_net_write(&conn->net, "", 0); + net_flush(&conn->net); + goto infile_error; + } + + /* allocate buffer for reading data */ + buf = (uchar *)my_malloc(buflen, MYF(0)); + + /* init handler: allocate read buffer and open file */ + if (conn->options.local_infile_init(&info, filename, + conn->options.local_infile_userdata[0])) + { + 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); + my_net_write(&conn->net, "", 0); + net_flush(&conn->net); + goto infile_error; + } + + /* read data */ + while ((bufread= conn->options.local_infile_read(info, (char *)buf, buflen)) > 0) + { + if (my_net_write(&conn->net, (char *)buf, bufread)) + { + my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL); + goto infile_error; + } + } + + /* send empty packet for eof */ + if (my_net_write(&conn->net, "", 0) || net_flush(&conn->net)) + { + my_set_error(conn, CR_SERVER_LOST, SQLSTATE_UNKNOWN, NULL); + goto infile_error; + } + + /* error during read occured */ + 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); + my_free(buf); + DBUG_RETURN(result); +} +/* }}} */ + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_malloc.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_malloc.c new file mode 100644 index 0000000..25d5e16 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_malloc.c @@ -0,0 +1,101 @@ +/* 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 */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "mysys_err.h" +#include + + /* My memory allocator */ + +gptr my_malloc(size_t Size, myf MyFlags) +{ + gptr point; + DBUG_ENTER("my_malloc"); + DBUG_PRINT("my",("Size: %u MyFlags: %d",Size, MyFlags)); + + if (!Size) + Size=1; /* Safety */ + if ((point = (char*)malloc(Size)) == NULL) + { + my_errno=errno; + if (MyFlags & MY_FAE) + error_handler_hook=fatal_error_handler_hook; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size); + if (MyFlags & MY_FAE) + exit(1); + } + else if (MyFlags & MY_ZEROFILL) + bzero(point,Size); + DBUG_PRINT("exit",("ptr: %lx",point)); + DBUG_RETURN(point); +} /* my_malloc */ + + + /* Free memory allocated with my_malloc */ + /*ARGSUSED*/ + +void my_no_flags_free(void *ptr) +{ + DBUG_ENTER("my_free"); + DBUG_PRINT("my",("ptr: %lx",ptr)); + if (ptr) + free(ptr); + DBUG_VOID_RETURN; +} /* my_free */ + + + /* malloc and copy */ + +gptr my_memdup(const unsigned char *from, size_t length, myf MyFlags) +{ + gptr ptr; + if ((ptr=my_malloc(length,MyFlags)) != 0) + memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length); + return(ptr); +} + + +my_string my_strdup(const char *from, myf MyFlags) +{ + gptr ptr; + uint length; + + if ((MyFlags & MY_ALLOW_ZERO_PTR) && !from) + return NULL; + + length=(uint) strlen(from)+1; + if ((ptr=my_malloc(length,MyFlags)) != 0) + memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length); + return((my_string) ptr); +} + +my_string my_strndup(const char *src, size_t length, myf MyFlags) +{ + gptr ptr; + + if ((ptr= my_malloc(length+1, MyFlags))) { + memcpy(ptr, src, length); + ptr[length] = 0; + } + return ptr; +} +/* }}} */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_messnc.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_messnc.c new file mode 100644 index 0000000..e847e1c --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_messnc.c @@ -0,0 +1,36 @@ +/* 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 */ + +#include "mysys_priv.h" + +int my_message_no_curses(uint error __attribute__((unused)), + const char *str, myf MyFlags) +{ + DBUG_ENTER("my_message_no_curses"); + DBUG_PRINT("enter",("message: %s",str)); + (void) fflush(stdout); + if (MyFlags & ME_BELL) + (void) fputc('\007',stderr); /* Bell */ + if (my_progname) + { + (void)fputs(my_progname,stderr); (void)fputs(": ",stderr); + } + (void)fputs(str,stderr); + (void)fputc('\n',stderr); + (void)fflush(stderr); + DBUG_RETURN(0); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_net.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_net.c new file mode 100644 index 0000000..cf24775 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_net.c @@ -0,0 +1,44 @@ +/* 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 */ + +/* thread safe version of some common functions */ + +#include "mysys_priv.h" +#include + +/* for thread safe my_inet_ntoa */ +#if !defined(MSDOS) && !defined(_WIN32) +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#endif /* !defined(MSDOS) && !defined(_WIN32) */ + +void my_inet_ntoa(struct in_addr in, char *buf) +{ + char *ptr; + pthread_mutex_lock(&THR_LOCK_net); + ptr=inet_ntoa(in); + strmov(buf,ptr); + pthread_mutex_unlock(&THR_LOCK_net); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_once.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_once.c new file mode 100644 index 0000000..4b64efd --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_once.c @@ -0,0 +1,88 @@ +/* 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 */ + +/* Not MT-SAFE */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "my_static.h" +#include "mysys_err.h" + + /* alloc for things we don't nead to free */ + /* No DBUG_ENTER... here to get smaller dbug-startup */ + +gptr my_once_alloc(unsigned int Size, myf MyFlags) +{ + size_t get_size,max_left; + gptr point; + reg1 USED_MEM *next; + reg2 USED_MEM **prev; + + Size= ALIGN_SIZE(Size); + prev= &my_once_root_block; + max_left=0; + for (next=my_once_root_block ; next && next->left < Size ; next= next->next) + { + if (next->left > max_left) + max_left=next->left; + prev= &next->next; + } + if (! next) + { /* Time to alloc new block */ + get_size= Size+ALIGN_SIZE(sizeof(USED_MEM)); + if (max_left*4 < my_once_extra && get_size < my_once_extra) + get_size=my_once_extra; /* Normal alloc */ + + if ((next = (USED_MEM*) malloc(get_size)) == 0) + { + my_errno=errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),get_size); + return((gptr) 0); + } + DBUG_PRINT("test",("my_once_malloc %u byte malloced",get_size)); + next->next= 0; + next->size= get_size; + next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); + *prev=next; + } + point= (gptr) ((char*) next+ (next->size-next->left)); + next->left-= Size; + + return(point); +} /* my_once_alloc */ + + + /* deallocate everything used by my_once_alloc */ + +void my_once_free(void) +{ + reg1 USED_MEM *next,*old; + DBUG_ENTER("my_once_free"); + + for (next=my_once_root_block ; next ; ) + { + old=next; next= next->next ; + free((gptr) old); + } + my_once_root_block=0; + + DBUG_VOID_RETURN; +} /* my_once_free */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_open.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_open.c new file mode 100644 index 0000000..75fd76c --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_open.c @@ -0,0 +1,126 @@ +/* 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 */ + +#define USES_TYPES +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include +#if defined(MSDOS) || defined(_WIN32) || defined(__EMX__) || defined(OS2) +#include +#endif + + /* Open a file */ + +File my_open(const char *FileName, int Flags, myf MyFlags) + /* Path-name of file */ + /* Read | write .. */ + /* Special flags */ +{ + File fd; + DBUG_ENTER("my_open"); + DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d", + FileName, Flags, MyFlags)); +#if defined(MSDOS) || defined(_WIN32) || defined(__EMX__) || defined(OS2) + if (Flags & O_SHARE) + fd = sopen((my_string) FileName, (Flags & ~O_SHARE) | O_BINARY, SH_DENYNO, + MY_S_IREAD | MY_S_IWRITE); + else + fd = open((my_string) FileName, Flags | O_BINARY, + MY_S_IREAD | MY_S_IWRITE); +#elif !defined(NO_OPEN_3) + fd = open(FileName, Flags, my_umask); /* Normal unix */ +#else + fd = open((my_string) FileName, Flags); +#endif + DBUG_RETURN(my_register_filename(fd, FileName, FILE_BY_OPEN, + EE_FILENOTFOUND, MyFlags)); +} /* my_open */ + + + /* Close a file */ + +int my_close(File fd, myf MyFlags) +{ + int err; + DBUG_ENTER("my_close"); + DBUG_PRINT("my",("fd: %d MyFlags: %d",fd, MyFlags)); + + pthread_mutex_lock(&THR_LOCK_open); + if ((err = close(fd))) + { + DBUG_PRINT("error",("Got error %d on close",err)); + my_errno=errno; + if (MyFlags & (MY_FAE | MY_WME)) + my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG),my_filename(fd),errno); + } + if ((uint) fd < MY_NFILE && my_file_info[fd].type != UNOPEN) + { + my_free(my_file_info[fd].name); +#if defined(THREAD) && !defined(HAVE_PREAD) + pthread_mutex_destroy(&my_file_info[fd].mutex); +#endif + my_file_info[fd].type = UNOPEN; + } + my_file_opened--; + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_RETURN(err); +} /* my_close */ + + +File my_register_filename(File fd, const char *FileName, enum file_type + type_of_file, uint error_message_number, myf MyFlags) +{ + if ((int) fd >= 0) + { + if ((int) fd >= MY_NFILE) + { +#if defined(THREAD) && !defined(HAVE_PREAD) + (void) my_close(fd,MyFlags); + my_errno=EMFILE; + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + my_error(EE_OUT_OF_FILERESOURCES, MYF(ME_BELL+ME_WAITTANG), + FileName, my_errno); + return(-1); +#endif + thread_safe_increment(my_file_opened,&THR_LOCK_open); + return(fd); /* safeguard */ + } + pthread_mutex_lock(&THR_LOCK_open); + if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags))) + { + my_file_opened++; + my_file_info[fd].type = type_of_file; +#if defined(THREAD) && !defined(HAVE_PREAD) + pthread_mutex_init(&my_file_info[fd].mutex,MY_MUTEX_INIT_FAST); +#endif + pthread_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("fd: %d",fd)); + return(fd); + } + pthread_mutex_unlock(&THR_LOCK_open); + (void) my_close(fd, MyFlags); + my_errno=ENOMEM; + } + else + my_errno=errno; + DBUG_PRINT("error",("Got error %d on open",my_errno)); + if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) + my_error(error_message_number, MYF(ME_BELL+ME_WAITTANG), + FileName, my_errno); + return(fd); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_port.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_port.c new file mode 100644 index 0000000..9d19e40 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_port.c @@ -0,0 +1,40 @@ +/* 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 */ + +/* + Small functions to make code portable +*/ + +#include "mysys_priv.h" + +#ifdef _AIX + +/* + On AIX, at least with gcc 3.1, the expression + '(double) (ulonglong) var' doesn't always work for big unsigned + integers like '18446744073709551615'. The end result is that the + high bit is simply dropped. (probably bug in gcc optimizations) + Handling the conversion in a sub function seems to work. +*/ + + + +double my_ulonglong2double(unsigned long long nr) +{ + return (double) nr; +} +#endif /* _AIX */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_pthread.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_pthread.c new file mode 100644 index 0000000..ea804c1 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_pthread.c @@ -0,0 +1,555 @@ +/* Copyright (C) 2000 MySQL 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 */ + +/* Functions to get threads more portable */ + +#define DONT_REMAP_PTHREAD_FUNCTIONS + +#include "mysys_priv.h" +#ifdef THREAD +#include +#include +#include +#include + +#ifdef _WIN32 + +int +pthread_cond_init (pthread_cond_t *cv, const pthread_condattr_t *attr) +{ + DBUG_ENTER("pthread_cond_init"); + /* Initialize the count to 0 */ + InitializeCriticalSection(&cv->waiters_count_lock); + cv->waiting = 0; + + /* Create an auto-reset and manual-reset event */ + if (!(cv->events[SIGNAL] = CreateEvent (NULL, FALSE, FALSE, NULL)) || + !(cv->events[BROADCAST] = CreateEvent (NULL, TRUE, FALSE, NULL))) + { + DBUG_RETURN(GetLastError()); + } + DBUG_RETURN(0); +} + +int pthread_cond_timedwait(pthread_cond_t *cond, + pthread_mutex_t *mutex, + struct timespec *abstime) +{ + int result= 0; + return result == WAIT_TIMEOUT ? ETIMEDOUT : 0; +} + +int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) +{ + return pthread_cond_timedwait(cv,mutex,NULL); +} + +int pthread_cond_destroy(pthread_cond_t *cv) +{ + DeleteCriticalSection(&cv->waiters_count_lock); + + if (CloseHandle(cv->events[SIGNAL]) == 0 || + CloseHandle(cv->events[BROADCAST]) == 0) + return EINVAL; + return 0; +} + +#endif + +#if (defined(__BSD__) || defined(_BSDI_VERSION)) && !defined(HAVE_mit_thread) +#define SCHED_POLICY SCHED_RR +#else +#define SCHED_POLICY SCHED_OTHER +#endif + +#ifndef my_pthread_setprio +void my_pthread_setprio(pthread_t thread_id,int prior) +{ +#ifdef HAVE_PTHREAD_SETSCHEDPARAM + struct sched_param tmp_sched_param; + bzero((char*) &tmp_sched_param,sizeof(tmp_sched_param)); + tmp_sched_param.sched_priority=prior; + VOID(pthread_setschedparam(thread_id,SCHED_POLICY,&tmp_sched_param)); +#endif +} +#endif + +#ifndef my_pthread_getprio +int my_pthread_getprio(pthread_t thread_id) +{ +#ifdef HAVE_PTHREAD_SETSCHEDPARAM + struct sched_param tmp_sched_param; + int policy; + if (!pthread_getschedparam(thread_id,&policy,&tmp_sched_param)) + { + DBUG_PRINT("thread",("policy: %d priority: %d", + policy,tmp_sched_param.sched_priority)); + return tmp_sched_param.sched_priority; + } +#endif + return -1; +} +#endif + +#ifndef my_pthread_attr_setprio +void my_pthread_attr_setprio(pthread_attr_t *attr, int priority) +{ +#ifdef HAVE_PTHREAD_SETSCHEDPARAM + struct sched_param tmp_sched_param; + bzero((char*) &tmp_sched_param,sizeof(tmp_sched_param)); + tmp_sched_param.sched_priority=priority; + VOID(pthread_attr_setschedparam(attr,&tmp_sched_param)); +#endif +} +#endif + + +/* To allow use of pthread_getspecific with two arguments */ + +#ifdef HAVE_NONPOSIX_PTHREAD_GETSPECIFIC +#undef pthread_getspecific +#ifdef HAVE_UNIXWARE7_THREADS +#define pthread_getspecific thr_getspecific +#endif + +void *my_pthread_getspecific_imp(pthread_key_t key) +{ + void *value; + if (pthread_getspecific(key,(void *) &value)) + return 0; + return value; +} +#endif + + +/* Some functions for RTS threads, AIX, Siemens Unix and UnixWare 7 + (and DEC OSF/1 3.2 too) */ + +int my_pthread_create_detached=1; + +#if defined(HAVE_NONPOSIX_SIGWAIT) || defined(HAVE_DEC_3_2_THREADS) + +int my_sigwait(const sigset_t *set,int *sig) +{ + int signal=sigwait((sigset_t*) set); + if (signal < 0) + return errno; + *sig=signal; + return 0; +} +#endif + +/* localtime_r for SCO 3.2V4.2 */ + +#ifndef HAVE_LOCALTIME_R + +extern pthread_mutex_t LOCK_localtime_r; + +struct tm *localtime_r(const time_t *clock, struct tm *res) +{ + struct tm *tmp; + pthread_mutex_lock(&LOCK_localtime_r); + tmp=localtime(clock); + *res= *tmp; + pthread_mutex_unlock(&LOCK_localtime_r); + return res; +} +#endif + + +/**************************************************************************** +** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) +** +** Note: +** This version of sigwait() is assumed to called in a loop so the signalmask +** is permanently modified to reflect the signal set. This is done to get +** a much faster implementation. +** +** This implementation isn't thread safe: It assumes that only one +** thread is using sigwait. +** +** If one later supplies a different signal mask, all old signals that +** was used before are unblocked and set to SIGDFL. +** +** Author: Gary Wisniewski , much modified by Monty +****************************************************************************/ + +#if !defined(HAVE_SIGWAIT) && !defined(HAVE_mit_thread) && !defined(sigwait) && !defined(_WIN32) && !defined(HAVE_rts_threads) && !defined(HAVE_NONPOSIX_SIGWAIT) && !defined(HAVE_DEC_3_2_THREADS) && !defined(OS2) + +#if !defined(DONT_USE_SIGSUSPEND) + +static sigset_t sigwait_set,rev_sigwait_set,px_recd; + +void px_handle_sig(int sig) +{ + sigaddset(&px_recd, sig); +} + + +void sigwait_setup(sigset_t *set) +{ + int i; + struct sigaction sact,sact1; + sigset_t unblock_mask; + + sact.sa_flags = 0; + sact.sa_handler = px_handle_sig; + memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ + sigemptyset(&unblock_mask); + pthread_sigmask(SIG_UNBLOCK,(sigset_t*) 0,&rev_sigwait_set); + + for (i = 1; i <= sizeof(sigwait_set)*8; i++) + { + if (sigismember(set,i)) + { + sigdelset(&rev_sigwait_set,i); + if (!sigismember(&sigwait_set,i)) + sigaction(i, &sact, (struct sigaction*) 0); + } + else + { + sigdelset(&px_recd,i); /* Don't handle this */ + if (sigismember(&sigwait_set,i)) + { /* Remove the old handler */ + sigaddset(&unblock_mask,i); + sigdelset(&rev_sigwait_set,i); + sact1.sa_flags = 0; + sact1.sa_handler = SIG_DFL; + sigemptyset(&sact1.sa_mask); + sigaction(i, &sact1, 0); + } + } + } + memcpy_fixed(&sigwait_set,set,sizeof(*set)); + pthread_sigmask(SIG_BLOCK,(sigset_t*) set,(sigset_t*) 0); + pthread_sigmask(SIG_UNBLOCK,&unblock_mask,(sigset_t*) 0); +} + + +int sigwait(sigset_t *setp, int *sigp) +{ + if (memcmp(setp,&sigwait_set,sizeof(sigwait_set))) + sigwait_setup(setp); /* Init or change of set */ + + for (;;) + { + /* + This is a fast, not 100% portable implementation to find the signal. + Because the handler is blocked there should be at most 1 bit set, but + the specification on this is somewhat shady so we use a set instead a + single variable. + */ + + ulong *ptr= (ulong*) &px_recd; + ulong *end=ptr+sizeof(px_recd)/sizeof(ulong); + + for ( ; ptr != end ; ptr++) + { + if (*ptr) + { + ulong set= *ptr; + int found= (int) ((char*) ptr - (char*) &px_recd)*8+1; + while (!(set & 1)) + { + found++; + set>>=1; + } + *sigp=found; + sigdelset(&px_recd,found); + return 0; + } + } + sigsuspend(&rev_sigwait_set); + } + return 0; +} +#else /* !DONT_USE_SIGSUSPEND */ + +/**************************************************************************** +** Replacement of sigwait if the system doesn't have one (like BSDI 3.0) +** +** Note: +** This version of sigwait() is assumed to called in a loop so the signalmask +** is permanently modified to reflect the signal set. This is done to get +** a much faster implementation. +** +** This implementation uses a extra thread to handle the signals and one +** must always call sigwait() with the same signal mask! +** +** BSDI 3.0 NOTE: +** +** pthread_kill() doesn't work on a thread in a select() or sleep() loop? +** After adding the sleep to sigwait_thread, all signals are checked and +** delivered every second. This isn't that terrible performance vice, but +** someone should report this to BSDI and ask for a fix! +** Another problem is that when the sleep() ends, every select() in other +** threads are interrupted! +****************************************************************************/ + +static sigset_t pending_set; +static bool inited=0; +static pthread_cond_t COND_sigwait; +static pthread_mutex_t LOCK_sigwait; + + +void sigwait_handle_sig(int sig) +{ + pthread_mutex_lock(&LOCK_sigwait); + sigaddset(&pending_set, sig); + VOID(pthread_cond_signal(&COND_sigwait)); /* inform sigwait() about signal */ + pthread_mutex_unlock(&LOCK_sigwait); +} + +extern pthread_t alarm_thread; + +void *sigwait_thread(void *set_arg) +{ + sigset_t *set=(sigset_t*) set_arg; + + int i; + struct sigaction sact; + sact.sa_flags = 0; + sact.sa_handler = sigwait_handle_sig; + memcpy_fixed(&sact.sa_mask,set,sizeof(*set)); /* handler isn't thread_safe */ + sigemptyset(&pending_set); + + for (i = 1; i <= sizeof(pending_set)*8; i++) + { + if (sigismember(set,i)) + { + sigaction(i, &sact, (struct sigaction*) 0); + } + } + sigaddset(set,THR_CLIENT_ALARM); + pthread_sigmask(SIG_UNBLOCK,(sigset_t*) set,(sigset_t*) 0); + alarm_thread=pthread_self(); /* For thr_alarm */ + + for (;;) + { /* Wait for signals */ +#ifdef HAVE_NOT_BROKEN_SELECT + fd_set fd; + FD_ZERO(&fd); + select(0,&fd,0,0,0); +#else + sleep(1); /* Because of broken BSDI */ +#endif + } +} + + +int sigwait(sigset_t *setp, int *sigp) +{ + if (!inited) + { + pthread_attr_t thr_attr; + pthread_t sigwait_thread_id; + inited=1; + sigemptyset(&pending_set); + pthread_mutex_init(&LOCK_sigwait,MY_MUTEX_INIT_FAST); + pthread_cond_init(&COND_sigwait,NULL); + + pthread_attr_init(&thr_attr); + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thr_attr,8196); + my_pthread_attr_setprio(&thr_attr,100); /* Very high priority */ + VOID(pthread_create(&sigwait_thread_id,&thr_attr,sigwait_thread,setp)); + VOID(pthread_attr_destroy(&thr_attr)); + } + + pthread_mutex_lock(&LOCK_sigwait); + for (;;) + { + ulong *ptr= (ulong*) &pending_set; + ulong *end=ptr+sizeof(pending_set)/sizeof(ulong); + + for ( ; ptr != end ; ptr++) + { + if (*ptr) + { + ulong set= *ptr; + int found= (int) ((char*) ptr - (char*) &pending_set)*8+1; + while (!(set & 1)) + { + found++; + set>>=1; + } + *sigp=found; + sigdelset(&pending_set,found); + pthread_mutex_unlock(&LOCK_sigwait); + return 0; + } + } + VOID(pthread_cond_wait(&COND_sigwait,&LOCK_sigwait)); + } + return 0; +} + +#endif /* DONT_USE_SIGSUSPEND */ +#endif /* HAVE_SIGWAIT */ + +/***************************************************************************** +** Implement pthread_signal for systems that can't use signal() with threads +** Currently this is only used with BSDI 3.0 +*****************************************************************************/ + +#ifdef USE_PTHREAD_SIGNAL + +int pthread_signal(int sig, void (*func)()) +{ + struct sigaction sact; + sact.sa_flags= 0; + sact.sa_handler= func; + sigemptyset(&sact.sa_mask); + sigaction(sig, &sact, (struct sigaction*) 0); + return 0; +} +#endif + +/**************************************************************************** + The following functions fixes that all pthread functions should work + according to latest posix standard +****************************************************************************/ + +/* Undefined wrappers set my_pthread.h so that we call os functions */ +#undef pthread_mutex_init +#undef pthread_mutex_lock +#undef pthread_mutex_unlock +#undef pthread_mutex_destroy +#undef pthread_mutex_wait +#undef pthread_mutex_timedwait +#undef pthread_mutex_trylock +#undef pthread_mutex_t +#undef pthread_cond_init +#undef pthread_cond_wait +#undef pthread_cond_timedwait +#undef pthread_cond_t + + +/***************************************************************************** +** Patches for AIX and DEC OSF/1 3.2 +*****************************************************************************/ + +#if (defined(HAVE_NONPOSIX_PTHREAD_MUTEX_INIT) && !defined(HAVE_UNIXWARE7_THREADS)) || defined(HAVE_DEC_3_2_THREADS) + +#include + +int my_pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *attr) +{ + int error; + if (!attr) + error=pthread_mutex_init(mp,pthread_mutexattr_default); + else + error=pthread_mutex_init(mp,*attr); + return error; +} + +int my_pthread_cond_init(pthread_cond_t *mp, const pthread_condattr_t *attr) +{ + int error; + if (!attr) + error=pthread_cond_init(mp,pthread_condattr_default); + else + error=pthread_cond_init(mp,*attr); + return error; +} + +#endif + + +/***************************************************************************** + Patches for HPUX + We need these because the pthread_mutex.. code returns -1 on error, + instead of the error code. + + Note that currently we only remap pthread_ functions used by MySQL. + If we are depending on the value for some other pthread_xxx functions, + this has to be added here. +****************************************************************************/ + +#if defined(HPUX) || defined(HAVE_BROKEN_PTHREAD_COND_TIMEDWAIT) + +int my_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + struct timespec *abstime) +{ + int error=pthread_cond_timedwait(cond, mutex, abstime); + if (error == -1) /* Safety if the lib is fixed */ + { + if (!(error=errno)) + error= ETIMEDOUT; /* Can happen on HPUX */ + } + if (error == EAGAIN) /* Correct errno to Posix */ + error= ETIMEDOUT; + return error; +} +#endif + + +#ifdef HAVE_POSIX1003_4a_MUTEX +/* + In HP-UX-10.20 and other old Posix 1003.4a Draft 4 implementations + pthread_mutex_trylock returns 1 on success, not 0 like + pthread_mutex_lock + + From the HP-UX-10.20 man page: + RETURN VALUES + If the function fails, errno may be set to one of the following + values: + Return | Error | Description + _______|__________|_________________________________________ + 1 | | Successful completion. + 0 | | The mutex is locked; therefore, it was + | | not acquired. + -1 | [EINVAL] | The value specified by mutex is invalid. + +*/ + +/* + Convert pthread_mutex_trylock to return values according to latest POSIX + + RETURN VALUES + 0 If we are able successfully lock the mutex. + EBUSY Mutex was locked by another thread + # Other error number returned by pthread_mutex_trylock() + (Not likely) +*/ + +int my_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + int error= pthread_mutex_trylock(mutex); + if (error == 1) + return 0; /* Got lock on mutex */ + if (error == 0) /* Someon else is locking mutex */ + return EBUSY; + if (error == -1) /* Safety if the lib is fixed */ + error= errno; /* Probably invalid parameter */ + return error; +} +#endif /* HAVE_POSIX1003_4a_MUTEX */ + +/* Some help functions */ + +int pthread_no_free(void *not_used __attribute__((unused))) +{ + return 0; +} + +int pthread_dummy(int ret) +{ + return ret; +} +#endif /* THREAD */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_read.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_read.c new file mode 100644 index 0000000..53cc5ac --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_read.c @@ -0,0 +1,66 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include + + + /* Read a chunk of bytes from a file */ + +uint my_read(File Filedes, unsigned char *Buffer, uint Count, myf MyFlags) + /* File descriptor */ + /* Buffer must be at least count bytes */ + /* Max number of bytes returnd */ + /* Flags on what to do on error */ +{ + uint readbytes; + DBUG_ENTER("my_read"); + DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %u MyFlags: %d", + Filedes, Buffer, Count, MyFlags)); + + for (;;) + { + errno=0; /* Linux doesn't reset this */ + if ((readbytes = (uint) read(Filedes, Buffer, Count)) != Count) + { + my_errno=errno ? errno : -1; + DBUG_PRINT("warning",("Read only %ld bytes off %ld from %d, errno: %d", + readbytes,Count,Filedes,my_errno)); +#ifdef THREAD + if (readbytes == 0 && errno == EINTR) + continue; /* Interrupted */ +#endif + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + if ((int) readbytes == -1) + my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + else if (MyFlags & (MY_NABP | MY_FNABP)) + my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + } + if ((int) readbytes == -1 || (MyFlags & (MY_FNABP | MY_NABP))) + DBUG_RETURN(MY_FILE_ERROR); /* Return with error */ + } + + if (MyFlags & (MY_NABP | MY_FNABP)) + readbytes=0; /* Ok on read */ + break; + } + DBUG_RETURN(readbytes); +} /* my_read */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_realloc.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_realloc.c new file mode 100644 index 0000000..0c37961 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_realloc.c @@ -0,0 +1,65 @@ +/* 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 */ + +#ifdef SAFEMALLOC /* We don't need SAFEMALLOC here */ +#undef SAFEMALLOC +#endif + +#include "mysys_priv.h" +#include "mysys_err.h" + + /* My memory re allocator */ + +gptr my_realloc(gptr oldpoint, size_t Size, myf MyFlags) +{ + gptr point; + DBUG_ENTER("my_realloc"); + DBUG_PRINT("my",("ptr: %lx Size: %u MyFlags: %d",oldpoint, Size, MyFlags)); + + if (!oldpoint && (MyFlags & MY_ALLOW_ZERO_PTR)) + DBUG_RETURN(my_malloc(Size,MyFlags)); +#ifdef USE_HALLOC + if (!(point = malloc(Size))) + { + if (MyFlags & MY_FREE_ON_ERROR) + my_free(oldpoint); + if (MyFlags & MY_HOLD_ON_ERROR) + DBUG_RETURN(oldpoint); + my_errno=errno; + if (MyFlags & MY_FAE+MY_WME) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG),Size); + } + else + { + memcpy(point,oldpoint,Size); + free(oldpoint); + } +#else + if ((point = (char*)realloc(oldpoint,Size)) == NULL) + { + if (MyFlags & MY_FREE_ON_ERROR) + my_free(oldpoint); + if (MyFlags & MY_HOLD_ON_ERROR) + DBUG_RETURN(oldpoint); + my_errno=errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG), Size); + } +#endif + DBUG_PRINT("exit",("ptr: %lx",point)); + DBUG_RETURN(point); +} /* my_realloc */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_seek.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_seek.c new file mode 100644 index 0000000..6bbdea1 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_seek.c @@ -0,0 +1,58 @@ +/* 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 */ + +#include "mysys_priv.h" + + /* Seek to position in file */ + /*ARGSUSED*/ + +my_off_t my_seek(File fd, my_off_t pos, int whence, + myf MyFlags __attribute__((unused))) +{ + reg1 os_off_t newpos; + DBUG_ENTER("my_seek"); + DBUG_PRINT("my",("Fd: %d Hpos: %lu Pos: %lu Whence: %d MyFlags: %d", + fd, ((ulonglong) pos) >> 32, (ulong) pos, whence, MyFlags)); + newpos=lseek(fd, pos, whence); + if (newpos == (os_off_t) -1) + { + my_errno=errno; + DBUG_PRINT("error",("lseek: %lu, errno: %d",newpos,errno)); + DBUG_RETURN(MY_FILEPOS_ERROR); + } + DBUG_RETURN((my_off_t) newpos); +} /* my_seek */ + + + /* Tell current position of file */ + /* ARGSUSED */ + +my_off_t my_tell(File fd, myf MyFlags __attribute__((unused))) +{ + os_off_t pos; + DBUG_ENTER("my_tell"); + DBUG_PRINT("my",("Fd: %d MyFlags: %d",fd, MyFlags)); +#ifdef HAVE_TELL + pos=tell(fd); +#else + pos=lseek(fd, 0L, MY_SEEK_CUR); +#endif + if (pos == (os_off_t) -1) + my_errno=errno; + DBUG_PRINT("exit",("pos: %lu",pos)); + DBUG_RETURN((my_off_t) pos); +} /* my_tell */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_static.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_static.c new file mode 100644 index 0000000..501de3c --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_static.c @@ -0,0 +1,101 @@ +/* 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 */ + +/* + Static variables for mysys library. All definied here for easy making of + a shared library +*/ + +#if !defined(stdin) || defined(OS2) +#include "mysys_priv.h" +#include "my_static.h" +#include "my_alarm.h" +#endif + + /* from my_init */ +my_string home_dir=0,my_progname=0; +char NEAR curr_dir[FN_REFLEN]= {0}, + NEAR home_dir_buff[FN_REFLEN]= {0}; +ulong my_stream_opened=0,my_file_opened=0, my_tmp_file_created=0; +int NEAR my_umask=0664, NEAR my_umask_dir=0777; +#ifndef THREAD +int NEAR my_errno=0; +#endif +struct my_file_info my_file_info[MY_NFILE]= {{0,UNOPEN}}; + + /* From mf_brkhant */ +int NEAR my_dont_interrupt=0; +volatile int _my_signals=0; +struct st_remember _my_sig_remember[MAX_SIGNALS]={{0,0}}; +#ifdef THREAD +sigset_t my_signals; /* signals blocked by mf_brkhant */ +#endif + + /* from mf_keycache.c */ +my_bool key_cache_inited=0; + + /* from mf_reccache.c */ +ulong my_default_record_cache_size=RECORD_CACHE_SIZE; + + /* from soundex.c */ + /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */ + /* :::::::::::::::::::::::::: */ +const char *soundex_map= "01230120022455012623010202"; + + /* from my_malloc */ +USED_MEM* my_once_root_block=0; /* pointer to first block */ +uint my_once_extra=ONCE_ALLOC_INIT; /* Memory to alloc / block */ + + /* from my_tempnam */ +#if !defined(HAVE_TEMPNAM) || defined(HPUX11) +int _my_tempnam_used=0; +#endif + + /* from safe_malloc */ +uint sf_malloc_prehunc=0, /* If you have problem with core- */ + sf_malloc_endhunc=0, /* dump when malloc-message.... */ + /* set theese to 64 or 128 */ + sf_malloc_quick=0; /* set if no calls to sanity */ +size_t lCurMemory = 0L; /* Current memory usage */ +size_t lMaxMemory = 0L; /* Maximum memory usage */ +uint cNewCount = 0; /* Number of times NEW() was called */ +unsigned char *sf_min_adress= (unsigned char*) ~(unsigned long) 0L, + *sf_max_adress= (unsigned char*) 0L; + +/* Root of the linked list of remembers */ +struct remember *pRememberRoot = NULL; + + /* from my_alarm */ +int volatile my_have_got_alarm=0; /* declare variable to reset */ +ulong my_time_to_wait_for_lock=2; /* In seconds */ + + /* from errors.c */ +#ifdef SHARED_LIBRARY +char * NEAR globerrs[GLOBERRS]; /* my_error_messages is here */ +#endif +void (*my_abort_hook)(int) = (void(*)(int)) exit; +int (*error_handler_hook)(uint error,const char *str,myf MyFlags)= + my_message_no_curses; +int (*fatal_error_handler_hook)(uint error,const char *str,myf MyFlags)= + my_message_no_curses; + + /* How to disable options */ +my_bool NEAR my_disable_locking=0; +my_bool NEAR my_disable_async_io=0; +my_bool NEAR my_disable_flush_key_blocks=0; +my_bool NEAR my_disable_symlinks=0; +my_bool NEAR mysys_uses_curses=0; diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_static.h b/mariadb-connector-c-v_2.3.7/libmariadb/my_static.h new file mode 100644 index 0000000..bb17a16 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_static.h @@ -0,0 +1,70 @@ +/* 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 */ + +/* + Static variables for mysys library. All definied here for easy making of + a shared library +*/ + +#include "mysys_priv.h" +#include + +#define MAX_SIGNALS 10 /* Max signals under a dont-allow */ +#define MIN_KEYBLOCK (min(IO_SIZE,1024)) +#define MAX_KEYBLOCK 8192 /* Max keyblocklength == 8*IO_SIZE */ +#define MAX_BLOCK_TYPES MAX_KEYBLOCK/MIN_KEYBLOCK + +struct st_remember { + int number; + sig_handler (*func)(int number); +}; + +struct irem { + struct remember *_pNext; /* Linked list of structures */ + struct remember *_pPrev; /* Other link */ + my_string _sFileName; /* File in which memory was new'ed */ + uint _uLineNum; /* Line number in above file */ + uint _uDataSize; /* Size requested */ + long _lSpecialValue; /* Underrun marker value */ +}; + +struct remember { + struct irem tInt; + char aData[1]; +}; + +extern char NEAR curr_dir[FN_REFLEN],NEAR home_dir_buff[FN_REFLEN]; + +extern volatile int _my_signals; +extern struct st_remember _my_sig_remember[MAX_SIGNALS]; + +extern const char *soundex_map; + +extern USED_MEM* my_once_root_block; +extern uint my_once_extra; + +#if !defined(HAVE_TEMPNAM) || defined(HPUX11) +extern int _my_tempnam_used; +#endif + +extern unsigned char *sf_min_adress,*sf_max_adress; +extern uint cNewCount; +extern struct remember *pRememberRoot; + +#if defined(THREAD) && !defined(__WIN__) +extern sigset_t my_signals; /* signals blocked by mf_brkhant */ +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c new file mode 100644 index 0000000..a978e63 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt.c @@ -0,0 +1,1921 @@ +/**************************************************************************** + 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 + 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ + */ + +#include "my_global.h" +#include +#include +#include +#include +#include "mysql.h" +#include "mysql_priv.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "errmsg.h" +#include +#include +#include +#include +#include + +#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 +{ + MEM_ROOT fields_alloc_root; +} MADB_STMT_EXTENSION; + +static my_bool is_not_null= 0; +static my_bool is_null= 1; + +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_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 int stmt_unbuffered_eof(MYSQL_STMT *stmt, uchar **row) +{ + return MYSQL_NO_DATA; +} + +static int stmt_unbuffered_fetch(MYSQL_STMT *stmt, uchar **row) +{ + ulong pkt_len; + + DBUG_ENTER("stmt_unbuffered_fetch"); + + pkt_len= net_safe_read(stmt->mysql); + DBUG_PRINT("info",("packet_length= %ld",pkt_len)); + + if (pkt_len == packet_error) + { + stmt->fetch_row_func= stmt_unbuffered_eof; + DBUG_RETURN(1); + } + + if (stmt->mysql->net.read_pos[0] == 254) + { + *row = NULL; + stmt->fetch_row_func= stmt_unbuffered_eof; + DBUG_RETURN(MYSQL_NO_DATA); + } + else + *row = stmt->mysql->net.read_pos; + stmt->result.rows++; + DBUG_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; + + DBUG_ENTER("stmt_read_all_rows"); + + pprevious= &result->data; + + while ((packet_len = 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 *)alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + packet_len))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + current->data= (MYSQL_ROW)(current + 1); + *pprevious= current; + pprevious= ¤t->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].max_length) + stmt->fields[i].max_length= mysql_ps_fetch_functions[stmt->fields[i].type].max_len; + 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 */ + { + *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); + stmt->result_cursor= result->data; + DBUG_RETURN(0); + } + } + stmt->result_cursor= 0; + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + DBUG_RETURN(1); +} + +static int stmt_cursor_fetch(MYSQL_STMT *stmt, uchar **row) +{ + uchar buf[STMT_ID_LENGTH + 4]; + MYSQL_DATA *result= &stmt->result; + + DBUG_ENTER("stmt_cursor_fetch"); + + if (stmt->state < MYSQL_STMT_USE_OR_STORE_CALLED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* do we have some prefetched rows available ? */ + if (stmt->result_cursor) + DBUG_RETURN(stmt_buffered_fetch(stmt, row)); + if (stmt->mysql->server_status & SERVER_STATUS_LAST_ROW_SENT) + stmt->mysql->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + if (!(stmt->upsert_status.server_status & SERVER_STATUS_LAST_ROW_SENT)) + { + int4store(buf, stmt->stmt_id); + int4store(buf + STMT_ID_LENGTH, stmt->prefetch_rows); + + if (simple_command(stmt->mysql, MYSQL_COM_STMT_FETCH, (char *)buf, sizeof(buf), 1, stmt)) + DBUG_RETURN(1); + + /* free previously allocated buffer */ + free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); + result->data= 0; + result->rows= 0; + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + DBUG_RETURN(1); + + DBUG_RETURN(stmt_buffered_fetch(stmt, row)); + } + /* no more cursor data available */ + *row= NULL; + DBUG_RETURN(MYSQL_NO_DATA); +} + +void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt) +{ + ulong packet_len; + while ((packet_len = net_safe_read(stmt->mysql)) != packet_error) + if (packet_len < 8 && stmt->mysql->net.read_pos[0] == 254) + return; +} + +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; + + DBUG_ENTER("stmt_fetch_to_bind"); + + 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->bind[i].is_null) + stmt->bind[i].is_null= &stmt->bind[i].is_null_value; + *stmt->bind[i].is_null= 1; + stmt->bind[i].row_ptr= NULL; + } else + { + stmt->bind[i].row_ptr= row; + if (!stmt->bind_result_done || + stmt->bind[i].flags & MADB_BIND_DUMMY) + { + unsigned long length; + + 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++; + } + } + DBUG_RETURN((truncations) ? MYSQL_DATA_TRUNCATED : 0); +} + +MYSQL_RES *_mysql_stmt_use_result(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + + DBUG_ENTER("mysql_stmt_use_result"); + + if (!stmt->field_count || + (!stmt->cursor_exists && mysql->status != MYSQL_STATUS_GET_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); + DBUG_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; + + DBUG_RETURN(NULL); +} + +unsigned char *mysql_net_store_length(unsigned char *packet, size_t length) +{ + if (length < (my_ulonglong) L64(251)) { + *packet = (unsigned char) length; + return packet + 1; + } + + if (length < (my_ulonglong) L64(65536)) { + *packet++ = 252; + int2store(packet,(uint) length); + return packet + 2; + } + + if (length < (my_ulonglong) L64(16777216)) { + *packet++ = 253; + int3store(packet,(ulong) length); + return packet + 3; + } + *packet++ = 254; + int8store(packet, length); + return packet + 8; +} + +int store_param(MYSQL_STMT *stmt, int column, unsigned char **p) +{ + DBUG_ENTER("store_param"); + DBUG_PRINT("info", ("column: %d type: x%x", column, stmt->params[column].buffer_type)); + switch (stmt->params[column].buffer_type) { + case MYSQL_TYPE_TINY: + int1store(*p, *(uchar *)stmt->params[column].buffer); + (*p) += 1; + break; + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_YEAR: + int2store(*p, *(short *)stmt->params[column].buffer); + (*p) += 2; + break; + case MYSQL_TYPE_FLOAT: + float4store(*p, *(float *)stmt->params[column].buffer); + (*p) += 4; + break; + case MYSQL_TYPE_DOUBLE: + float8store(*p, *(double *)stmt->params[column].buffer); + (*p) += 8; + break; + case MYSQL_TYPE_LONGLONG: + int8store(*p, *(my_ulonglong *)stmt->params[column].buffer); + (*p) += 8; + break; + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_INT24: + int4store(*p, *(int32 *)stmt->params[column].buffer); + (*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 ninute + 8 1 second; + 9-13 4 second_part + */ + MYSQL_TIME *t= (MYSQL_TIME *)stmt->params[column].buffer; + char t_buffer[MAX_TIME_STR_LEN]; + uint len= 0; + memset(t_buffer, 0, MAX_TIME_STR_LEN); + + 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 *)stmt->params[column].buffer; + char t_buffer[MAX_DATETIME_STR_LEN]; + uint len= 0; + + memset(t_buffer, 0, MAX_DATETIME_STR_LEN); + + 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= 12; + } + else if (t->hour || t->minute || t->second) + len= 7; + else if (t->year || t->month || t->day) + len= 4; + 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_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + { + ulong len= (ulong)*stmt->params[column].length; + /* to is after p. The latter hasn't been moved */ + uchar *to = mysql_net_store_length(*p, len); + DBUG_PRINT("info", ("len=x%x", len)); + if (len) + memcpy(to, stmt->params[column].buffer, len); + (*p) = to + len; + break; + } + + default: + /* unsupported parameter type */ + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +/* {{{ mysqlnd_stmt_execute_generate_request */ +unsigned char* mysql_stmt_execute_generate_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 + ------------------------------------------ + n data from bind_buffer + */ + + size_t length= 1024; + size_t free_bytes= 0; + size_t data_size= 0; + uint i; + + uchar *start= NULL, *p; + + DBUG_ENTER("mysql_stmt_execute_generate_request"); + + /* preallocate length bytes */ + if (!(start= p= (uchar *)my_malloc(length, MYF(MY_WME | MY_ZEROFILL)))) + 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++; + + int1store(p, 1); /* and send 1 for iteration count */ + p+= 4; + + if (stmt->param_count) + { + size_t null_byte_offset, + 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 (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + 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 (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + p= start + offset; + } + for (i = 0; i < stmt->param_count; i++) + { + /* this differs from mysqlnd, c api supports unsinged !! */ + 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 (i = 0; i < stmt->param_count; i++) { + if (stmt->params[i].buffer && !stmt->params[i].is_null) + stmt->params[i].is_null = &is_not_null; + if (!stmt->params[i].length) + stmt->params[i].length= &stmt->params[i].length_value; + if (!(stmt->params[i].is_null && *stmt->params[i].is_null) && !stmt->params[i].long_data_used) + { + switch (stmt->params[i].buffer_type) { + case MYSQL_TYPE_NULL: + stmt->params[i].is_null = &is_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_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: + data_size+= 5; /* max 8 bytes for size */ + data_size+= (size_t)*stmt->params[i].length; + break; + default: + data_size+= mysql_ps_fetch_functions[stmt->params[i].buffer_type].pack_len; + break; + } + } + } + + /* store data */ + free_bytes= length - (p - start); + if (free_bytes < data_size + 20) + { + size_t offset= p - start; + length= offset + data_size + 20; + if (!(start= (uchar *)my_realloc((gptr)start, length, MYF(MY_WME | MY_ZEROFILL)))) + goto mem_error; + p= start + offset; + } + for (i = 0; i < stmt->param_count; i++) + { + if (stmt->params[i].long_data_used) { + stmt->params[i].long_data_used= 0; + } + else { + if (!stmt->params[i].buffer || *stmt->params[i].is_null || stmt->params[i].buffer_type == MYSQL_TYPE_NULL) { + (start + null_byte_offset)[i/8] |= (unsigned char) (1 << (i & 7)); + } else { + DBUG_PRINT("info", ("storing parameter %d at offset %d", i, p - start)); + store_param(stmt, i, &p); + } + } + } + } + stmt->send_types_to_server= 0; + *request_len = (size_t)(p - start); + DBUG_RETURN(start); + + +mem_error: + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + my_free(start); + *request_len= 0; + DBUG_RETURN(NULL); +} +/* }}} */ + +/*! + ******************************************************************************* + + \fn my_ulonglong mysql_stmt_affected_rows + \brief returns the number of affected rows from last mysql_stmt_execute + call + + \param[in] stmt The statement handle + ******************************************************************************* + */ +my_ulonglong 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) +{ + DBUG_ENTER("mysql_stmt_attr_get"); + + switch (attr_type) { + 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; + default: + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value) +{ + DBUG_ENTER("mysql_stmt_attr_get"); + + 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) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); + DBUG_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; + default: + SET_CLIENT_STMT_ERROR(stmt, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + DBUG_ENTER("mysql_stmt_bind_param"); + + if (stmt->state < MYSQL_STMT_PREPARED) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_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)) + { + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_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_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: + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + break; + } + } + } + stmt->bind_param_done= stmt->send_types_to_server= 1; + + CLEAR_CLIENT_STMT_ERROR(stmt); + DBUG_RETURN(0); +} + +my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) +{ + uint i; + DBUG_ENTER("mysql_stmt_bind_result"); + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!bind) + DBUG_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) + { + MEM_ROOT *fields_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; +// free_root(fields_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_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)) + { + SET_CLIENT_STMT_ERROR(stmt, CR_UNSUPPORTED_PARAM_TYPE, SQLSTATE_UNKNOWN, 0); + DBUG_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); + + DBUG_RETURN(0); +} + +my_bool net_stmt_close(MYSQL_STMT *stmt, my_bool remove) +{ + char stmt_id[STMT_ID_LENGTH]; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + + /* clear memory */ + free_root(&stmt->result.alloc, MYF(0)); /* allocated in mysql_stmt_store_result */ + free_root(&stmt->mem_root,MYF(0)); + free_root(fields_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) + { + stmt->mysql->methods->db_stmt_flush_unbuffered(stmt); + stmt->mysql->status= MYSQL_STATUS_READY; + } + if (stmt->state > MYSQL_STMT_INITTED) + { + int4store(stmt_id, stmt->stmt_id); + if (simple_command(stmt->mysql,MYSQL_COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + { + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, stmt->mysql->net.last_error); + return 1; + } + } + } + return 0; +} + +my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_close"); + + if (stmt && stmt->mysql && stmt->mysql->net.vio) + mysql_stmt_reset(stmt); + net_stmt_close(stmt, 1); + + my_free(stmt->extension); + my_free(stmt); + + DBUG_RETURN(0); +} + +void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong offset) +{ + my_ulonglong i= offset; + MYSQL_ROWS *ptr= stmt->result.data; + DBUG_ENTER("mysql_stmt_data_seek"); + + DBUG_PRINT("info", ("total rows: %llu offset: %llu", stmt->result.rows, offset)); + + while(i-- && ptr) + ptr= ptr->next; + + stmt->result_cursor= ptr; + stmt->state= MYSQL_STMT_USER_FETCHING; + + DBUG_VOID_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; + + DBUG_ENTER("mysql_stmt_fetch"); + + if (stmt->state <= MYSQL_STMT_EXECUTED) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_WAITING_USE_OR_STORE || !stmt->field_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } else if (stmt->state== MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt->default_rset_handler(stmt); + } + + if (stmt->state == MYSQL_STMT_FETCH_DONE) + DBUG_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 */ + DBUG_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); + DBUG_RETURN(rc); +} + +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset) +{ + DBUG_ENTER("mysql_stmt_fetch"); + + if (stmt->state < MYSQL_STMT_USER_FETCHING || column >= stmt->field_count || + stmt->state == MYSQL_STMT_FETCH_DONE) { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_DATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->bind[column].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].row_ptr; + mysql_ps_fetch_functions[stmt->fields[column].type].func(&bind[0], &stmt->fields[column], &stmt->bind[column].row_ptr); + stmt->bind[column].row_ptr= save_ptr; + } + DBUG_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; + DBUG_ENTER("mysql_stmt_init"); + + if (!(stmt= (MYSQL_STMT *)my_malloc(sizeof(MYSQL_STMT), MYF(MY_WME | MY_ZEROFILL))) || + !(stmt->extension= (MADB_STMT_EXTENSION *)my_malloc(sizeof(MADB_STMT_EXTENSION), + MYF(MY_WME | MY_ZEROFILL)))) + { + my_free(stmt); + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + + /* fill mysql's stmt list */ + stmt->list.data= stmt; + stmt->mysql= mysql; + 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; + + init_alloc_root(&stmt->mem_root, 2048, 0); + init_alloc_root(&stmt->result.alloc, 4096, 0); + init_alloc_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root, 2048, 0); + + DBUG_RETURN(stmt); +} + +my_bool mthd_stmt_read_prepare_response(MYSQL_STMT *stmt) +{ + ulong packet_length; + uchar *p; + + DBUG_ENTER("read_prepare_response"); + + if ((packet_length= net_safe_read(stmt->mysql)) == packet_error) + DBUG_RETURN(1); + + DBUG_PRINT("info",("packet_length= %ld",packet_length)); + + p= (uchar *)stmt->mysql->net.read_pos; + + if (0xFF == p[0]) /* Error occured */ + { + DBUG_RETURN(1); + } + + p++; + stmt->stmt_id= uint4korr(p); + p+= 4; + stmt->field_count= uint2korr(p); + p+= 2; + stmt->param_count= uint2korr(p); + + /* filler */ + p++; + stmt->upsert_status.warning_count= uint2korr(p); + + DBUG_RETURN(0); +} + +my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + + DBUG_ENTER("stmt_get_param_metadata"); + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + DBUG_RETURN(1); + + free_rows(result); + DBUG_RETURN(0); +} + +my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_DATA *result; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + DBUG_ENTER("stmt_read_result_metadata"); + + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + DBUG_RETURN(1); + if (!(stmt->fields= unpack_fields(result,fields_alloc_root, + stmt->field_count, 0, + stmt->mysql->server_capabilities & CLIENT_LONG_FLAG))) + DBUG_RETURN(1); + DBUG_RETURN(0); +} + +int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length) +{ + MYSQL *mysql= stmt->mysql; + int rc= 1; + DBUG_ENTER("mysql_stmt_prepare"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + /* clear flags */ + CLEAR_CLIENT_STMT_ERROR(stmt); + CLEAR_CLIENT_ERROR(stmt->mysql); + stmt->upsert_status.affected_rows= mysql->affected_rows= (my_ulonglong) ~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. Statment handle will get a new stmt_id */ + char stmt_id[STMT_ID_LENGTH]; + + if (mysql_stmt_reset(stmt)) + goto fail; + + free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + free_root(&((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root, MYF(0)); + + stmt->param_count= 0; + stmt->field_count= 0; + + int4store(stmt_id, stmt->stmt_id); + if (simple_command(mysql, MYSQL_COM_STMT_CLOSE, stmt_id, sizeof(stmt_id), 1, stmt)) + goto fail; + } + if (simple_command(mysql, MYSQL_COM_STMT_PREPARE, query, length, 1, stmt)) + goto fail; + + if (stmt->mysql->methods->db_read_prepare_response && + stmt->mysql->methods->db_read_prepare_response(stmt)) + goto fail; + + /* metadata not supported yet */ + + if (stmt->param_count && + stmt->mysql->methods->db_stmt_get_param_metadata(stmt)) + { + goto fail; + } + + /* allocated bind buffer for parameters */ + if (stmt->field_count && + stmt->mysql->methods->db_stmt_get_result_metadata(stmt)) + { + goto fail; + } + if (stmt->param_count) + { + if (!(stmt->params= (MYSQL_BIND *)alloc_root(&stmt->mem_root, stmt->param_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + memset(stmt->params, '\0', stmt->param_count * sizeof(MYSQL_BIND)); + } + /* allocated bind buffer for result */ + if (stmt->field_count) + { + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + goto fail; + } + } + bzero(stmt->bind, sizeof(MYSQL_BIND) * stmt->field_count); + stmt->state = MYSQL_STMT_PREPARED; + DBUG_RETURN(0); + +fail: + stmt->state= MYSQL_STMT_INITTED; + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + DBUG_RETURN(rc); +} + +int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) +{ + unsigned int last_server_status; + DBUG_ENTER("mysql_stmt_store_result"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!stmt->field_count) + DBUG_RETURN(0); + + /* test_pure_coverage requires checking of error_no */ + if (stmt->last_errno) + DBUG_RETURN(1); + + if (stmt->state < MYSQL_STMT_EXECUTED) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_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 (simple_command(stmt->mysql, MYSQL_COM_STMT_FETCH, buff, sizeof(buff), 1, stmt)) + DBUG_RETURN(1); + /* todo: cursor */ + } + else if (stmt->mysql->status != MYSQL_STATUS_GET_RESULT) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->mysql->methods->db_stmt_read_all_rows(stmt)) + { + /* error during read - reset stmt->data */ + free_root(&stmt->result.alloc, 0); + stmt->result.data= NULL; + stmt->result.rows= 0; + stmt->mysql->status= MYSQL_STATUS_READY; + DBUG_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; + + DBUG_RETURN(0); +} + +static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) +{ + uint i; + MEM_ROOT *fields_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + + DBUG_ENTER("madb_alloc_stmt_fields"); + + if (stmt->mysql->field_count) + { + free_root(fields_alloc_root, MYF(0)); + if (!(stmt->fields= (MYSQL_FIELD *)alloc_root(fields_alloc_root, + sizeof(MYSQL_FIELD) * stmt->mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + stmt->field_count= stmt->mysql->field_count; + + for (i=0; i < stmt->field_count; i++) + { + if (stmt->mysql->fields[i].db) + stmt->fields[i].db= strdup_root(fields_alloc_root, stmt->mysql->fields[i].db); + if (stmt->mysql->fields[i].table) + stmt->fields[i].table= strdup_root(fields_alloc_root, stmt->mysql->fields[i].table); + if (stmt->mysql->fields[i].org_table) + stmt->fields[i].org_table= strdup_root(fields_alloc_root, stmt->mysql->fields[i].org_table); + if (stmt->mysql->fields[i].name) + stmt->fields[i].name= strdup_root(fields_alloc_root, stmt->mysql->fields[i].name); + if (stmt->mysql->fields[i].org_name) + stmt->fields[i].org_name= strdup_root(fields_alloc_root, stmt->mysql->fields[i].org_name); + if (stmt->mysql->fields[i].catalog) + stmt->fields[i].catalog= strdup_root(fields_alloc_root, stmt->mysql->fields[i].catalog); + stmt->fields[i].def= stmt->mysql->fields[i].def ? strdup_root(fields_alloc_root, stmt->mysql->fields[i].def) : NULL; + stmt->fields[i].type= stmt->mysql->fields[i].type; + stmt->fields[i].length= stmt->mysql->fields[i].length; + stmt->fields[i].flags= stmt->mysql->fields[i].flags; + stmt->fields[i].decimals= stmt->mysql->fields[i].decimals; + stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr; + stmt->fields[i].max_length= stmt->mysql->fields[i].max_length; + } + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + bzero(stmt->bind, stmt->field_count * sizeof(MYSQL_BIND)); + stmt->bind_result_done= 0; + } + DBUG_RETURN(0); +} + + +int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + char *request; + int ret; + size_t request_len= 0; + + + DBUG_ENTER("mysql_stmt_execute"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_PREPARED) + { + SET_CLIENT_ERROR(mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->param_count && !stmt->bind_param_done) + { + SET_CLIENT_STMT_ERROR(stmt, CR_PARAMS_NOT_BOUND, SQLSTATE_UNKNOWN, 0); + DBUG_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) + { + mysql->methods->db_stmt_flush_unbuffered(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) + { + free_root(&stmt->result.alloc, MYF(MY_KEEP_PREALLOC)); + stmt->result_cursor= stmt->result.data= 0; + stmt->result.rows= 0; + } + request= (char *)mysql_stmt_execute_generate_request(stmt, &request_len); + DBUG_PRINT("info",("request_len=%ld", request_len)); + + ret= test(simple_command(mysql, MYSQL_COM_STMT_EXECUTE, request, request_len, 1, stmt) || + (mysql && mysql->methods->db_read_stmt_result && mysql->methods->db_read_stmt_result(mysql))); + if (request) + my_free(request); + + /* if a reconnect occured, our connection handle is invalid */ + if (!stmt->mysql) + DBUG_RETURN(1); + + /* update affected rows, also if an error occured */ + stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; + + if (ret) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + stmt->state= MYSQL_STMT_PREPARED; + DBUG_RETURN(1); + } + stmt->upsert_status.last_insert_id= mysql->insert_id; + stmt->upsert_status.server_status= mysql->server_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 */ + { + MEM_ROOT *fields_alloc_root= + &((MADB_STMT_EXTENSION *)stmt->extension)->fields_alloc_root; + uint i; + + free_root(fields_alloc_root, MYF(0)); + if (!(stmt->bind= (MYSQL_BIND *)alloc_root(fields_alloc_root, + sizeof(MYSQL_BIND) * mysql->field_count)) || + !(stmt->fields= (MYSQL_FIELD *)alloc_root(fields_alloc_root, + sizeof(MYSQL_FIELD) * mysql->field_count))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + bzero(stmt->bind, 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 */ + stmt->fields[i].extension= 0; /* not in use yet */ + if (mysql->fields[i].db) + stmt->fields[i].db= strdup_root(fields_alloc_root, mysql->fields[i].db); + if (mysql->fields[i].table) + stmt->fields[i].table= strdup_root(fields_alloc_root, mysql->fields[i].table); + if (mysql->fields[i].org_table) + stmt->fields[i].org_table= strdup_root(fields_alloc_root, mysql->fields[i].org_table); + if (mysql->fields[i].name) + stmt->fields[i].name= strdup_root(fields_alloc_root, mysql->fields[i].name); + if (mysql->fields[i].org_name) + stmt->fields[i].org_name= strdup_root(fields_alloc_root, mysql->fields[i].org_name); + if (mysql->fields[i].catalog) + stmt->fields[i].catalog= strdup_root(fields_alloc_root, mysql->fields[i].catalog); + if (mysql->fields[i].def) + stmt->fields[i].def= strdup_root(fields_alloc_root, mysql->fields[i].def); + } + } + + if (stmt->upsert_status.server_status & SERVER_STATUS_CURSOR_EXISTS) + { + 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) + { + /* + 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 */ + mysql_stmt_store_result(stmt); + } else + { + /* preferred is unbuffered read */ + stmt->default_rset_handler = _mysql_stmt_use_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 */ + SET_CLIENT_STMT_ERROR(stmt, CR_NEW_STMT_METADATA, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + +static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 0; + + DBUG_ENTER("madb_stmt_reset"); + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_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) + { + 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) + { + unsigned char cmd_buf[STMT_ID_LENGTH]; + int4store(cmd_buf, stmt->stmt_id); + if ((ret= simple_command(mysql,MYSQL_COM_STMT_RESET, (char *)cmd_buf, sizeof(cmd_buf), 0, stmt))) + { + SET_CLIENT_STMT_ERROR(stmt, mysql->net.last_errno, mysql->net.sqlstate, + mysql->net.last_error); + DBUG_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; + } + } + + } + DBUG_RETURN(ret); +} + +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +{ + MYSQL *mysql= stmt->mysql; + my_bool ret= 1; + unsigned int flags= MADB_RESET_LONGDATA | MADB_RESET_BUFFER | MADB_RESET_ERROR; + + DBUG_ENTER("mysql_stmt_reset"); + + if (!mysql) + { + /* connection could be invalid, e.g. after mysql_stmt_close or failed reconnect + attempt (see bug CONC-97) */ + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + 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; + } + } + ret= madb_reset_stmt(stmt, MADB_RESET_SERVER); + } + stmt->state= MYSQL_STMT_PREPARED; + 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; + stmt->upsert_status.warning_count= mysql->warning_count; + mysql->status= MYSQL_STATUS_READY; + + DBUG_RETURN(ret); +} + +MYSQL_RES * STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt) +{ + MYSQL_RES *res; + + DBUG_ENTER("mysql_stmt_result_metadata"); + + if (!stmt->field_count) + DBUG_RETURN(NULL); + + /* aloocate result set structutr and copy stmt information */ + if (!(res= (MYSQL_RES *)my_malloc(sizeof(MYSQL_RES), MYF(MY_WME | MY_ZEROFILL)))) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + res->eof= 1; + res->fields= stmt->fields; + res->field_count= stmt->field_count; + DBUG_RETURN(res);} + +const char * STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt) +{ + return stmt->sqlstate; +} + +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_row_tell"); + DBUG_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 */ + DBUG_ENTER("mysql_stmt_row_seek"); + + old_row= stmt->result_cursor; + stmt->result_cursor= new_row; + + DBUG_RETURN(old_row); +} + +my_bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, + const char *data, ulong length) +{ + DBUG_ENTER("mysql_stmt_send_long_data"); + + CLEAR_CLIENT_ERROR(stmt->mysql); + CLEAR_CLIENT_STMT_ERROR(stmt); + + if (stmt->state < MYSQL_STMT_PREPARED || !stmt->params) + { + SET_CLIENT_STMT_ERROR(stmt, CR_NO_PREPARE_STMT, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (param_number >= stmt->param_count) + { + SET_CLIENT_STMT_ERROR(stmt, CR_INVALID_PARAMETER_NO, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (length || !stmt->params[param_number].long_data_used) + { + int ret; + size_t packet_len; + uchar *cmd_buff= (uchar *)my_malloc(packet_len= STMT_ID_LENGTH + 2 + length, MYF(MY_WME | MY_ZEROFILL)); + 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= simple_command(stmt->mysql,MYSQL_COM_STMT_SEND_LONG_DATA, (char *)cmd_buff, packet_len, 1, stmt); + my_free(cmd_buff); + DBUG_RETURN(ret); + } + DBUG_RETURN(0); +} + +my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) +{ + return stmt->upsert_status.last_insert_id; +} + +my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) +{ + return stmt->result.rows; +} + +MYSQL_RES* STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_param_metadata"); + /* server doesn't deliver any information yet, + so we just return NULL + */ + DBUG_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; + DBUG_ENTER("mysql_stmt_next_result"); + + if (!stmt->mysql) + { + SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (stmt->state < MYSQL_STMT_EXECUTED) + { + SET_CLIENT_ERROR(stmt->mysql, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + SET_CLIENT_STMT_ERROR(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(1); + } + + if (!mysql_stmt_more_results(stmt)) + DBUG_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; + SET_CLIENT_STMT_ERROR(stmt, stmt->mysql->net.last_errno, stmt->mysql->net.sqlstate, + stmt->mysql->net.last_error); + DBUG_RETURN(1); + } + + if (stmt->mysql->field_count) + rc= madb_alloc_stmt_fields(stmt); + else + { + 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; + stmt->upsert_status.warning_count= stmt->mysql->warning_count; + } + + stmt->field_count= stmt->mysql->field_count; + + DBUG_RETURN(rc); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt_codec.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt_codec.c new file mode 100644 index 0000000..73004a8 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_stmt_codec.c @@ -0,0 +1,1117 @@ +/**************************************************************************** + 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 + 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 | + | Andrey Hristov | + | Ulf Wendel | + +----------------------------------------------------------------------+ +*/ + +#include "my_global.h" +#include +#include +#include +#include +#include "mysql.h" + +#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 + +#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN) +#define LONGLONG_MIN ((long long) 0x8000000000000000LL) +#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL) +#endif + +#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 +#define MAX_DOUBLE_STRING_REP_LENGTH 300 + +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)) + +/* helper function for zero fill */ +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 longlong my_atoll(const char *number, const char *end, int *error) +{ + char buffer[255]; + longlong llval= 0; + size_t i; + /* set error at the following conditions: + - string contains invalid character(s) + - length > 254 + - strtoll returns invalid range + */ + + memcpy(buffer, number, MIN((uint)(end - number), 254)); + buffer[(uint)(end - number)]= 0; + + llval= strtoll(buffer, NULL, 10); + + /* check size */ + if ((uint)(end - number) > 254) + { + *error= 1; + return llval; + } + + /* check characters */ + for (i=0; i < strlen(buffer); i++) + { + if (buffer[i] < '0' || buffer[i] > '9') + { + *error= 1; + return llval; + } + } + + /* check strtoll result */ + if (errno == ERANGE) + *error= errno; + return llval; +} + +double my_atod(const char *number, const char *end, int *error) +{ + double val= 0.0; + char buffer[255]; + int len= (int)(end - number); + + if (len > 254) + *error= 1; + + len= MIN(len, 254); + memcpy(&buffer, number, len); + buffer[len]= '\0'; + + val= strtod(buffer, NULL); +/* if (!*error) + *error= errno; */ + return val; +} + +my_bool str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm) +{ + char *start= alloca(length + 1); + my_bool is_date= 0, is_time= 0; + + memset(tm, 0, sizeof(MYSQL_TIME)); + if (!start) + goto error; + tm->time_type= MYSQL_TIMESTAMP_NONE; + + memcpy(start, str, length); + start[length]= '\0'; + + while (length && isspace(*start)) start++, length--; + + if (!length) + goto error; + + /* negativ value? */ + if (*start == '-') + { + tm->neg= 1; + start++; + length--; + } + + if (!length) + return 1; + + /* Determine time type: + MYSQL_TIMESTAMP_DATE: [-]YY[YY].MM.DD + MYSQL_TIMESTAMP_DATETIME: [-]YY[YY].MM.DD hh:mm:ss.mmmmmm + MYSQL_TIMESTAMP_TIME: [-]hh:mm:ss.mmmmmm + */ + if (strchr(start, '-')) + { + if (tm->neg) + goto error; + tm->time_type= MYSQL_TIMESTAMP_DATE; + if (sscanf(start, "%d-%d-%d", &tm->year, &tm->month, &tm->day) < 3) + goto error; + is_date= 1; + if (!(start= strchr(start, ' '))) + goto check; + } + if (!strchr(start, ':')) + goto check; + + is_time= 1; + if (tm->time_type== MYSQL_TIMESTAMP_DATE) + tm->time_type= MYSQL_TIMESTAMP_DATETIME; + else + tm->time_type= MYSQL_TIMESTAMP_TIME; + + if (strchr(start, '.')) /* fractional seconds */ + { + if (sscanf(start, "%d:%d:%d.%ld", &tm->hour, &tm->minute, + &tm->second,&tm->second_part) < 4) + goto error; + } else { + if (sscanf(start, "%d:%d:%d", &tm->hour, &tm->minute, + &tm->second) < 3) + goto error; + } + +check: + if (tm->time_type == MYSQL_TIMESTAMP_NONE) + goto error; + + if (is_date) + { + if (tm->year < 69) + tm->year+= 2000; + else if (tm->year < 100) + tm->year+= 1900; + if (tm->day > 31 || tm->month > 12) + goto error; + } + if (is_time) + { + if (tm->minute > 59 || tm->second > 59) + goto error; + } + return 0; +error: + tm->time_type= MYSQL_TIMESTAMP_ERROR; + return 1; +} + +static void convert_from_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= 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: + { + 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; + *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); + *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); + *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= is_unsigned ? (ulonglong )dbl != (ulonglong)val : (longlong)dbl != (longlong)val; + r_param->buffer_length= 8; + break; + } + case MYSQL_TYPE_FLOAT: + { + float fval; + fval= is_unsigned ? (float)(ulonglong)(val) : (float)val; + float4store((float *)r_param->buffer, fval); + *r_param->error= is_unsigned ? (ulonglong)fval != (ulonglong)val : (longlong)fval != val; + r_param->buffer_length= 4; + } + break; + default: + { + char buffer[22]; + char *endptr; + uint len; + + endptr= longlong10_to_str(val, buffer, is_unsigned ? 10 : -10); + len= (uint)(endptr - buffer); + + if (field->flags & ZEROFILL_FLAG && + len < field->length && len < r_param->buffer_length) + { + ma_bmove_upp(buffer + field->length, buffer + len, len); + memset((char*) buffer, '0', field->length - len); + len= field->length; + } + convert_from_string(r_param, buffer, len); + } + break; + } +} + + +/* {{{ ps_fetch_null */ +static +void ps_fetch_null(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row) +{ + /* 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 ? (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, double val, int size) +{ + 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; + case MYSQL_TYPE_DOUBLE: + { + memcpy(buf, &val, 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, 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); + memset((char*) buff, '0', field->length - length); + length= field->length; + } + convert_from_string(r_param, buff, length); + } + break; + } +} + +static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size) +{ + 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, 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); + memset((char*) buff, '0', field->length - length); + length= field->length; + } + convert_from_string(r_param, buff, length); + } + break; + } +} + + +/* {{{ 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; +} +/* }}} */ + + +/* {{{ 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; +} +/* }}} */ + + +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]; + 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); + if (t->day) + { + t->hour+= t->day * 24; + t->day= 0; + } + 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; + unsigned int length; + convert_to_datetime(&tm, row, len, field->type); + + if (tm.time_type== MYSQL_TIMESTAMP_TIME && tm.day) + { + tm.hour+= tm.day * 24; + tm.day=0; + } + 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) + { + 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) + { + 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_from_string(r_param, dtbuffer, length); + break; + } + } + (*row) += len; +} +/* }}} */ + +/* {{{ ps_fetch_string */ +static +void ps_fetch_string(MYSQL_BIND *r_param, const MYSQL_FIELD *field, + 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_from_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) + ((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 = 6; + + 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 = 9; + + 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 = 21; + + 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 = 15; + + 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_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/mariadb-connector-c-v_2.3.7/libmariadb/my_symlink.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_symlink.c new file mode 100644 index 0000000..dd23efe --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_symlink.c @@ -0,0 +1,138 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include +#ifdef HAVE_REALPATH +#include +#include +#endif + +/* + Reads the content of a symbolic link + If the file is not a symbolic link, return the original file name in to. + Returns: 0 if table was a symlink, + 1 if table was a normal file + -1 on error. +*/ + +int my_readlink(char *to, const char *filename, myf MyFlags) +{ +#ifndef HAVE_READLINK + strmov(to,filename); + return 1; +#else + int result=0; + int length; + DBUG_ENTER("my_readlink"); + + if ((length=readlink(filename, to, FN_REFLEN-1)) < 0) + { + /* Don't give an error if this wasn't a symlink */ + if ((my_errno=errno) == EINVAL) + { + result= 1; + strmov(to,filename); + } + else + { + if (MyFlags & MY_WME) + my_error(EE_CANT_READLINK, MYF(0), filename, errno); + result= -1; + } + } + else + to[length]=0; + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} + + +/* Create a symbolic link */ + +int my_symlink(const char *content, const char *linkname, myf MyFlags) +{ +#ifndef HAVE_READLINK + return 0; +#else + int result; + DBUG_ENTER("my_symlink"); + + result= 0; + if (symlink(content, linkname)) + { + result= -1; + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno); + } + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} + +/* + Resolve all symbolic links in path + 'to' may be equal to 'filename' + + Because purify gives a lot of UMR errors when using realpath(), + this code is disabled when using purify. + + If MY_RESOLVE_LINK is given, only do realpath if the file is a link. +*/ + +#if defined(SCO) +#define BUFF_LEN 4097 +#elif defined(MAXPATHLEN) +#define BUFF_LEN MAXPATHLEN +#else +#define BUFF_LEN FN_LEN +#endif + +int my_realpath(char *to, const char *filename, myf MyFlags) +{ +#if defined(HAVE_REALPATH) && !defined(HAVE_purify) && !defined(HAVE_BROKEN_REALPATH) + int result=0; + char buff[BUFF_LEN]; + struct stat stat_buff; + DBUG_ENTER("my_realpath"); + + if (!(MyFlags & MY_RESOLVE_LINK) || + (!lstat(filename,&stat_buff) && S_ISLNK(stat_buff.st_mode))) + { + char *ptr; + if ((ptr=realpath(filename,buff))) + strmake(to,ptr,FN_REFLEN-1); + else + { + /* Realpath didn't work; Use original name */ + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_REALPATH, MYF(0), filename, my_errno); + if (to != filename) + strmov(to,filename); + result= -1; + } + } + DBUG_RETURN(result); +#else + if (to != filename) + strmov(to,filename); + return 0; +#endif +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_thr_init.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_thr_init.c new file mode 100644 index 0000000..b082ccc --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_thr_init.c @@ -0,0 +1,246 @@ +/* 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 */ + +/* +** Functions to handle initializating and allocationg of all mysys & debug +** thread variables. +*/ + +#include "mysys_priv.h" +#include +#include + +#ifdef HAVE_OPENSSL +#include +#include +#endif + +#ifdef THREAD +#ifdef USE_TLS +pthread_key(struct st_my_thread_var*, THR_KEY_mysys); +#else +pthread_key(struct st_my_thread_var, THR_KEY_mysys); +#endif /* USE_TLS */ +pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open, + THR_LOCK_lock, THR_LOCK_net, THR_LOCK_mysys; +#ifdef HAVE_OPENSSL +pthread_mutex_t LOCK_ssl_config; +#endif +#ifndef HAVE_LOCALTIME_R +pthread_mutex_t LOCK_localtime_r; +#endif +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP +pthread_mutexattr_t my_fast_mutexattr; +#endif +#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +pthread_mutexattr_t my_errchk_mutexattr; +#endif +my_bool THR_KEY_mysys_initialized= FALSE; + +/* FIXME Note. TlsAlloc does not set an auto destructor, so + the function my_thread_global_free must be called from + somewhere before final exit of the library */ + +my_bool my_thread_global_init(void) +{ + if (pthread_key_create(&THR_KEY_mysys,free)) + { + fprintf(stderr,"Can't initialize threads: error %d\n",errno); + exit(1); + } +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + pthread_mutexattr_init(&my_fast_mutexattr); + pthread_mutexattr_setkind_np(&my_fast_mutexattr,PTHREAD_MUTEX_ADAPTIVE_NP); +#endif +#ifdef PPTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP + pthread_mutexattr_init(&my_errchk_mutexattr); + pthread_mutexattr_setkind_np(&my_errchk_mutexattr, + PTHREAD_MUTEX_ERRORCHECK_NP); +#endif + THR_KEY_mysys_initialized= TRUE; +#ifdef HAVE_OPENSSL + pthread_mutex_init(&LOCK_ssl_config,MY_MUTEX_INIT_FAST); +#endif + pthread_mutex_init(&THR_LOCK_malloc,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_open,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_lock,MY_MUTEX_INIT_FAST); + pthread_mutex_init(&THR_LOCK_net,MY_MUTEX_INIT_FAST); +#ifdef _WIN32 + /* win_pthread_init(); */ +#endif +#ifndef HAVE_LOCALTIME_R + pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW); +#endif + return my_thread_init(); +} + +void my_thread_global_end(void) +{ +#if defined(USE_TLS) + (void) TlsFree(THR_KEY_mysys); +#endif +#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP + pthread_mutexattr_destroy(&my_fast_mutexattr); +#endif +#ifdef PPTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP + pthread_mutexattr_destroy(&my_errchk_mutexattr); +#endif +#ifdef HAVE_OPENSSL + pthread_mutex_destroy(&LOCK_ssl_config); +#endif +} + +static long thread_id=0; + +/* + We can't use mutex_locks here if we are using windows as + we may have compiled the program with SAFE_MUTEX, in which + case the checking of mutex_locks will not work until + the pthread_self thread specific variable is initialized. +*/ + +my_bool my_thread_init(void) +{ + struct st_my_thread_var *tmp; + if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys)) + { + DBUG_PRINT("info", ("my_thread_init was already called. Thread id: %lu", + pthread_self())); + return 0; /* Safequard */ + } + /* We must have many calloc() here because these are freed on + pthread_exit */ + if (!(tmp=(struct st_my_thread_var *) + calloc(1,sizeof(struct st_my_thread_var)))) + { + return 1; + } + pthread_setspecific(THR_KEY_mysys,tmp); + + if (tmp->initialized) /* Already initialized */ + { + return 0; + } + + pthread_mutex_init(&tmp->mutex,MY_MUTEX_INIT_FAST); + pthread_cond_init(&tmp->suspend, NULL); + pthread_mutex_lock(&THR_LOCK_lock); + tmp->id= ++thread_id; + pthread_mutex_unlock(&THR_LOCK_lock); + tmp->initialized= TRUE; + return 0; +} + +void my_thread_end(void) +{ + struct st_my_thread_var *tmp= + my_pthread_getspecific(struct st_my_thread_var *, THR_KEY_mysys); + + if (tmp && tmp->initialized) + { +#if defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(NULL); +#endif +#if !defined(DBUG_OFF) + if (tmp->dbug) + { + DBUG_POP(); + free(tmp->dbug); + tmp->dbug=0; + } +#endif +#if !defined(__bsdi__) || defined(HAVE_mit_thread) /* bsdi dumps core here */ + pthread_cond_destroy(&tmp->suspend); +#endif + pthread_mutex_destroy(&tmp->mutex); + free(tmp); + pthread_setspecific(THR_KEY_mysys,0); + } + else + pthread_setspecific(THR_KEY_mysys,0); +} + +struct st_my_thread_var *_my_thread_var(void) +{ + struct st_my_thread_var *tmp= + my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); +#if defined(USE_TLS) + if (!tmp) + { + my_thread_init(); + tmp=my_pthread_getspecific(struct st_my_thread_var*,THR_KEY_mysys); + } +#endif + return tmp; +} + +/**************************************************************************** +** Get name of current thread. +****************************************************************************/ + +#define UNKNOWN_THREAD -1 + +long my_thread_id() +{ +#if defined(HAVE_PTHREAD_GETSEQUENCE_NP) + return pthread_getsequence_np(pthread_self()); +#elif (defined(__sun) || defined(__sgi) || defined(__linux__)) && !defined(HAVE_mit_thread) + return pthread_self(); +#else + return my_thread_var->id; +#endif +} + +#ifdef DBUG_OFF +const char *my_thread_name(void) +{ + return "no_name"; +} + +#else + +const char *my_thread_name(void) +{ + char name_buff[100]; + struct st_my_thread_var *tmp=my_thread_var; + if (!tmp->name[0]) + { + long id=my_thread_id(); + sprintf(name_buff,"T@%ld", id); + strmake(tmp->name,name_buff,THREAD_NAME_SIZE); + } + return tmp->name; +} + +extern void **my_thread_var_dbug() +{ + struct st_my_thread_var *tmp; + /* + Instead of enforcing DBUG_ASSERT(THR_KEY_mysys_initialized) here, + which causes any DBUG_ENTER and related traces to fail when + used in init / cleanup code, we are more tolerant: + using DBUG_ENTER / DBUG_PRINT / DBUG_RETURN + when the dbug instrumentation is not in place will do nothing. + */ + if (! THR_KEY_mysys_initialized) + return NULL; + tmp= _my_thread_var(); + return tmp && tmp->initialized ? (void **)&tmp->dbug : 0; +} +#endif /* DBUG_OFF */ + +#endif /* THREAD */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_vsnprintf.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_vsnprintf.c new file mode 100644 index 0000000..46cbc9d --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_vsnprintf.c @@ -0,0 +1,119 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include +#include +#include + + +int my_snprintf(char* to, size_t n, const char* fmt, ...) +{ + int result; + va_list args; + va_start(args,fmt); + result= my_vsnprintf(to, n, fmt, args); + va_end(args); + return result; +} + + +int my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap) +{ + char *start=to, *end=to+n-1; + for (; *fmt ; fmt++) + { + if (fmt[0] != '%') + { + if (to == end) /* End of buffer */ + break; + *to++= *fmt; /* Copy ordinary char */ + continue; + } + /* Skipp if max size is used (to be compatible with printf) */ + fmt++; + while (isdigit(*fmt) || *fmt == '.' || *fmt == '-') + fmt++; + if (*fmt == 'l') + fmt++; + if (*fmt == 's') /* String parameter */ + { + reg2 char *par = va_arg(ap, char *); + uint plen; + if (!par) par = (char*)"(null)"; + plen = (uint) strlen(par); + if ((uint) (end-to) > plen) /* Replace if possible */ + { + to=strmov(to,par); + continue; + } + } + else if (*fmt == 'd' || *fmt == 'u') /* Integer parameter */ + { + register int iarg; + if ((uint) (end-to) < 16) + break; + iarg = va_arg(ap, int); + if (*fmt == 'd') + to=int10_to_str((long) iarg,to, -10); + else + to=int10_to_str((long) (uint) iarg,to,10); + continue; + } + /* We come here on '%%', unknown code or too long parameter */ + if (to == end) + break; + *to++='%'; /* % used as % or unknown code */ + } + *to='\0'; /* End of errmessage */ + return (uint) (to - start); +} + + +#ifdef MAIN +static void my_printf(const char * fmt, ...) +{ + char buf[32]; + int n; + va_list ar; + va_start(ar, fmt); + n = my_vsnprintf(buf, sizeof(buf),fmt, ar); + printf(buf); + printf("n=%d, strlen=%d\n", n, strlen(buf)); + va_end(ar); +} + + +int main() +{ + + my_printf("Hello\n"); + my_printf("Hello int, %d\n", 1); + my_printf("Hello string '%s'\n", "I am a string"); + my_printf("Hello hack hack hack hack hack hack hack %d\n", 1); + my_printf("Hello %d hack %d\n", 1, 4); + my_printf("Hello %d hack hack hack hack hack %d\n", 1, 4); + my_printf("Hello '%s' hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n", "hack"); + my_printf("Hello hhhhhhhhhhhhhh %d sssssssssssssss\n", 1); + my_printf("Hello %u\n", 1); + my_printf("conn %ld to: '%-.64s' user: '%-.32s' host:\ + `%-.64s' (%-.64s)", 1, 0,0,0,0); + return 0; +} +#endif + diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/my_write.c b/mariadb-connector-c-v_2.3.7/libmariadb/my_write.c new file mode 100644 index 0000000..1a38737 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/my_write.c @@ -0,0 +1,90 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include + + + /* Write a chunk of bytes to a file */ + +uint my_write(int Filedes, const unsigned char *Buffer, uint Count, myf MyFlags) +{ + uint writenbytes,errors; + ulong written; + DBUG_ENTER("my_write"); + DBUG_PRINT("my",("Fd: %d Buffer: %lx Count: %d MyFlags: %d", + Filedes, Buffer, Count, MyFlags)); + errors=0; written=0L; + + for (;;) + { + if ((writenbytes = (uint) write(Filedes, Buffer, Count)) == Count) + break; + if ((int) writenbytes != -1) + { /* Safeguard */ + written+=writenbytes; + Buffer+=writenbytes; + Count-=writenbytes; + } + my_errno=errno; + DBUG_PRINT("error",("Write only %d bytes, error: %d", + writenbytes,my_errno)); +#ifndef NO_BACKGROUND +#ifdef THREAD + if (my_thread_var->abort) + MyFlags&= ~ MY_WAIT_IF_FULL; /* End if aborted by user */ +#endif + if (my_errno == ENOSPC && (MyFlags & MY_WAIT_IF_FULL) && + (uint) writenbytes != (uint) -1) + { + if (!(errors++ % MY_WAIT_GIVE_USER_A_MESSAGE)) + my_error(EE_DISK_FULL,MYF(ME_BELL | ME_NOREFRESH), + my_filename(Filedes)); + VOID(sleep(MY_WAIT_FOR_USER_TO_FIX_PANIC)); + continue; + } + if (!writenbytes) + { + /* We may come here on an interrupt or if the file quote is exeeded */ + if (my_errno == EINTR) + continue; + if (!errors++) /* Retry once */ + { + errno=EFBIG; /* Assume this is the error */ + continue; + } + } + else if ((uint) writenbytes != (uint) -1) + continue; /* Retry */ +#endif + if (MyFlags & (MY_NABP | MY_FNABP)) + { + if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) + { + my_error(EE_WRITE, MYF(ME_BELL+ME_WAITTANG), + my_filename(Filedes),my_errno); + } + DBUG_RETURN(MY_FILE_ERROR); /* Error on read */ + } + else + break; /* Return bytes written */ + } + if (MyFlags & (MY_NABP | MY_FNABP)) + DBUG_RETURN(0); /* Want only errors */ + DBUG_RETURN(writenbytes+written); +} /* my_write */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/mysql_async.c b/mariadb-connector-c-v_2.3.7/libmariadb/mysql_async.c new file mode 100644 index 0000000..3809786 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mysql_async.c @@ -0,0 +1,2000 @@ +/* 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 +#include "my_global.h" +#include "my_sys.h" +#include "mysql.h" +#include "errmsg.h" +#ifndef LIBMARIADB +#include "sql_common.h" +#else +#include "mysql_com.h" +#include "ma_common.h" +#endif +#include "my_context.h" +#include "violite.h" +#include "mysql_async.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) { \ + my_bool old_mode; \ + if ((mysql)->net.vio) vio_blocking((mysql)->net.vio, FALSE, &old_mode); \ + } +#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(struct mysql_async_context *b, my_socket fd, + const struct sockaddr *name, uint namelen, int vio_timeout) +{ + int res; + size_socket s_err_size; + + /* Make the socket non-blocking. */ +#ifdef _WIN32 + ulong arg= 1; + ioctlsocket(fd, FIONBIO, (void *)&arg); +#else + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + + 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(fd, 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_occured & MYSQL_WAIT_TIMEOUT) + return -1; + + s_err_size= sizeof(res); + if (getsockopt(fd, 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 + +ssize_t +my_recv_async(struct mysql_async_context *b, int fd, + unsigned char *buf, size_t size, int timeout) +{ + ssize_t res; + + for (;;) + { + res= recv(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + 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_occured & MYSQL_WAIT_TIMEOUT) + return -1; + } +} + + +ssize_t +my_send_async(struct mysql_async_context *b, int fd, + const unsigned char *buf, size_t size, int timeout) +{ + ssize_t res; + + for (;;) + { + res= send(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + 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_occured & MYSQL_WAIT_TIMEOUT) + return -1; + } +} + + +my_bool +my_io_wait_async(struct mysql_async_context *b, enum enum_vio_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_occured & MYSQL_WAIT_TIMEOUT) ? 0 : 1; +} + + +#ifdef HAVE_OPENSSL +static my_bool +my_ssl_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; +} + +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 */ + +/* + 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= 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) +{ + return mysql->options.extension->async_context->timeout_value; +} + + +/* + 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_mysql_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_mysql_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \ + *ret= err_val; \ + return 0; \ + } \ + \ + b->active= 1; \ + b->events_occured= 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_mysql_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_mysql_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_mysql_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \ + return 0; \ + } \ + \ + b->active= 1; \ + b->events_occured= 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_mysql_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; + }, + 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) +{ +MK_ASYNC_START_BODY( + mysql_real_query, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.stmt_str= stmt_str; + parms.length= length; + }, + 1, + r_int, + /* Nothing */) +} +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.vio) + { + 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_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/mariadb-connector-c-v_2.3.7/libmariadb/mysys_priv.h b/mariadb-connector-c-v_2.3.7/libmariadb/mysys_priv.h new file mode 100644 index 0000000..d99f7ab --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/mysys_priv.h @@ -0,0 +1,32 @@ +/* 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 */ + +#include +#include + +#ifdef USE_SYSTEM_WRAPPERS +#include "system_wrappers.h" +#endif + +#ifdef THREAD +#include +extern pthread_mutex_t THR_LOCK_malloc,THR_LOCK_open,THR_LOCK_keycache, + THR_LOCK_lock,THR_LOCK_isam,THR_LOCK_net,THR_LOCK_charset; +extern pthread_mutex_t LOCK_bitmap; +#else +#include +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/net.c b/mariadb-connector-c-v_2.3.7/libmariadb/net.c new file mode 100644 index 0000000..410553f --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/net.c @@ -0,0 +1,868 @@ +/* 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 */ + +/* Write and read of logical packets to/from socket +** Writes are cached into net_buffer_length big packets. +** Read packets are reallocated dynamicly when reading big packets. +** Each logical packet has the following pre-info: +** 3 byte length & 1 byte package-number. +*/ + +#include +#include +#include +#include +#include "mysql.h" +#include "mysqld_error.h" +#include +#include +#include +#ifndef _WIN32 +#include +#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) && !defined(MSDOS) +#include +#else +#undef MYSQL_SERVER /* Win32 can't handle interrupts */ +#endif +#if !defined(MSDOS) && !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) +#include +#include +#include +#if !defined(alpha_linux_port) +#include +#endif +#endif +#include "mysqld_error.h" +#ifdef MYSQL_SERVER +#include "my_pthread.h" +#include "thr_alarm.h" +void sql_print_error(const char *format,...); +#define RETRY_COUNT mysqld_net_retry_count +extern ulong mysqld_net_retry_count; +#else + +#ifdef OS2 /* avoid name conflict */ +#define thr_alarm_t thr_alarm_t_net +#define ALARM ALARM_net +#endif + +typedef my_bool thr_alarm_t; +typedef my_bool ALARM; +#define thr_alarm_init(A) (*(A))=0 +#define thr_alarm_in_use(A) (*(A)) +#define thr_end_alarm(A) +#define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) +static inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) +{ + *A=1; + return 0; +} +#define thr_got_alarm(A) 0 +#define RETRY_COUNT 1 +#endif + +#ifdef MYSQL_SERVER +extern ulong bytes_sent, bytes_received; +extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; +#else +#undef statistic_add +#define statistic_add(A,B,C) +#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. +*/ + +#define TEST_BLOCKING 8 +static int net_write_buff(NET *net,const char *packet, size_t len); + + + /* Init with packet info */ + +int my_net_init(NET *net, Vio* vio) +{ + if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME | MY_ZEROFILL)))) + return 1; + max_allowed_packet= net->max_packet_size= MAX(net_buffer_length, max_allowed_packet); + net->buff_end=net->buff+(net->max_packet=net_buffer_length); + net->vio = vio; + 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 (vio != 0) /* If real connection */ + { + net->fd = vio_fd(vio); /* For perl DBI/DBD */ +#if defined(MYSQL_SERVER) && !defined(__WIN32) && !defined(__EMX__) && !defined(OS2) + if (!(test_flags & TEST_BLOCKING)) + vio_blocking(vio, FALSE, 0); +#endif + vio_fastsend(vio); + } + return 0; +} + +void net_end(NET *net) +{ + my_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; + + DBUG_ENTER("net_realloc"); + DBUG_PRINT("info", ("length: %lu max_allowed_packet: %lu", + (ulong)length, max_allowed_packet)); + + if (length >= net->max_packet_size) + { + DBUG_PRINT("error",("Packet too large (%lu)", length)); + net->error=1; + net->last_errno=ER_NET_PACKET_TOO_LARGE; + DBUG_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*) my_realloc((char*) net->buff, + pkt_length + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) + { + DBUG_PRINT("info", ("Out of memory")); + net->error=1; + DBUG_RETURN(1); + } + net->buff=net->write_pos=buff; + net->buff_end=buff+(net->max_packet=pkt_length); + DBUG_RETURN(0); +} + + +/* check if the socket is still alive */ +static my_bool net_check_socket_status(my_socket sock) +{ +#ifndef _WIN32 + struct pollfd poll_fd; +#else + FD_SET sfds; + struct timeval tv= {0,0}; +#endif + int res; +#ifndef _WIN32 + memset(&poll_fd, 0, sizeof(struct pollfd)); + poll_fd.events= POLLPRI | POLLIN; + poll_fd.fd= sock; + + res= poll(&poll_fd, 1, 0); + if (res <= 0) /* timeout or error */ + return FALSE; + if (!(poll_fd.revents & (POLLIN | POLLPRI))) + return FALSE; + return TRUE; +#else + /* We can't use the WSAPoll function, it's broken :-( + (see Windows 8 Bugs 309411 - WSAPoll does not report failed connections) + Instead we need to use select function: + If TIMEVAL is initialized to {0, 0}, select will return immediately; + this is used to poll the state of the selected sockets. + */ + FD_ZERO(&sfds); + FD_SET(sock, &sfds); + + res= select(sock + 1, &sfds, NULL, NULL, &tv); + if (res > 0 && FD_ISSET(sock, &sfds)) + return TRUE; + return FALSE; +#endif + +} + + /* Remove unwanted characters from connection */ + +void net_clear(NET *net) +{ + DBUG_ENTER("net_clear"); + + /* see conc-71: we need to check the socket status first: + if the socket is dead we set net->error, so net_flush + will report an error */ + while (net_check_socket_status(net->vio->sd)) + { + /* vio_read returns size_t. so casting to long is required to check for -1 */ + if ((long)vio_read(net->vio, (gptr)net->buff, (size_t) net->max_packet) <= 0) + { + net->error= 2; + DBUG_PRINT("info", ("socket disconnected")); + DBUG_VOID_RETURN; + } + } + net->compress_pkt_nr= net->pkt_nr=0; /* Ready for new command */ + net->write_pos=net->buff; + DBUG_VOID_RETURN; +} + + + /* Flush write_buffer if not empty. */ + +int net_flush(NET *net) +{ + int error=0; + DBUG_ENTER("net_flush"); + if (net->buff != net->write_pos) + { + error=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; + DBUG_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 +my_net_write(NET *net, const char *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 (net_write_buff(net,(char*) buff,NET_HEADER_SIZE) || + net_write_buff(net, 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 (net_write_buff(net,(char*) buff,NET_HEADER_SIZE) || + net_write_buff(net, packet, len)) + return 1; + return 0; +} + +int +net_write_command(NET *net, uchar command, + const char *packet, size_t len) +{ + 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 (net_write_buff(net, (char *)buff, buff_size) || + 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 (net_write_buff(net,(char *)buff, buff_size) || + net_write_buff(net,packet,len) || + net_flush(net)); + return rc; +} + + +static int +net_write_buff(NET *net,const char *packet, size_t len) +{ + size_t left_length; + + 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 (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 (net_real_write(net, packet, left_length)) + return 1; + packet+= left_length; + len-= left_length; + } + } + if (len > net->max_packet) + return(test(net_real_write(net, packet, len))); + } + memcpy((char*) net->write_pos,packet,len); + net->write_pos+=len; + return 0; +} + +/* Read and write using timeouts */ + +int +net_real_write(NET *net,const char *packet,size_t len) +{ + size_t length; + char *pos,*end; + thr_alarm_t alarmed; +#if !defined(_WIN32) && !defined(__EMX__) && !defined(OS2) + ALARM alarm_buff; +#endif + uint retry_count=0; + my_bool net_blocking = vio_is_blocking(net->vio); + DBUG_ENTER("net_real_write"); + + if (net->error == 2) + DBUG_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*) my_malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE + 1, + MYF(MY_WME)))) + { + net->last_errno=ER_OUT_OF_RESOURCES; + net->error=2; + net->reading_or_writing=0; + DBUG_RETURN(1); + } + memcpy(b+header_length,packet,len); + + if (my_compress((unsigned char*) b+header_length,&len,&complen)) + { + DBUG_PRINT("warning", + ("Compression error; Continuing without compression")); + 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 */ + + alarmed=0; + + pos=(char*) packet; end=pos+len; + while (pos != end) + { + if ((long) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0) + { + my_bool interrupted = vio_should_retry(net->vio); +#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) + if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed)) + { + if (!thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff)) + { /* Always true for client */ + if (!vio_is_blocking(net->vio)) + { + while (vio_blocking(net->vio, TRUE, 0) < 0) + { + if (vio_should_retry(net->vio) && retry_count++ < RETRY_COUNT) + continue; +#ifdef EXTRA_DEBUG + fprintf(stderr, + "%s: my_net_write: fcntl returned error %d, aborting thread\n", + my_progname,vio_errno(net->vio)); +#endif /* EXTRA_DEBUG */ + net->error=2; /* Close socket */ + net->last_errno= (interrupted ? + ER_NET_WRITE_INTERRUPTED : ER_NET_ERROR_ON_WRITE); + goto end; + } + } + retry_count=0; + continue; + } + } + else +#endif /* (!defined(_WIN32) && !defined(__EMX__)) */ + if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && + interrupted) + { + if (retry_count++ < RETRY_COUNT) + continue; +#ifdef EXTRA_DEBUG + fprintf(stderr, "%s: write looped, aborting thread\n", + my_progname); +#endif /* EXTRA_DEBUG */ + } +#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) + if (vio_errno(net->vio) == SOCKET_EINTR) + { + DBUG_PRINT("warning",("Interrupted write. Retrying...")); + continue; + } +#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */ + net->error=2; /* Close socket */ + net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED : + ER_NET_ERROR_ON_WRITE); + break; + } + pos+=length; + statistic_add(bytes_sent,length,&LOCK_bytes_sent); + } +#ifndef _WIN32 + end: +#endif +#ifdef HAVE_COMPRESS + if (net->compress) + my_free((void *)packet); +#endif + if (thr_alarm_in_use(&alarmed)) + { + thr_end_alarm(&alarmed); + vio_blocking(net->vio, net_blocking, 0); + } + net->reading_or_writing=0; + DBUG_RETURN(((int) (pos != end))); +} + + +/***************************************************************************** +** Read something from server/clinet +*****************************************************************************/ + +#ifdef MYSQL_SERVER + +/* + Help function to clear the commuication buffer when we get a too + big packet +*/ + +static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed, + ALARM *alarm_buff) +{ + uint retry_count=0; + if (!thr_alarm_in_use(alarmed)) + { + if (thr_alarm(alarmed,net->timeout,alarm_buff) || + (!vio_is_blocking(net->vio) && vio_blocking(net->vio,TRUE, 0) < 0)) + return; /* Can't setup, abort */ + } + while (remain > 0) + { + ulong length; + if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L) + { + my_bool interrupted = vio_should_retry(net->vio); + if (!thr_got_alarm(alarmed) && interrupted) + { /* Probably in MIT threads */ + if (retry_count++ < RETRY_COUNT) + continue; + } + return; + } + remain -=(ulong) length; + statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + } +} +#endif /* MYSQL_SERVER */ + + +static ulong +my_real_read(NET *net, size_t *complen) +{ + uchar *pos; + size_t length; + uint i,retry_count=0; + ulong len=packet_error; + thr_alarm_t alarmed; +#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) + ALARM alarm_buff; +#endif + my_bool net_blocking=vio_is_blocking(net->vio); + size_t remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : + NET_HEADER_SIZE); + *complen = 0; + + net->reading_or_writing=1; + thr_alarm_init(&alarmed); +#ifdef MYSQL_SERVER + if (net_blocking) + thr_alarm(&alarmed,net->timeout,&alarm_buff); +#endif /* MYSQL_SERVER */ + + 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 ((long) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L) + { + my_bool interrupted = vio_should_retry(net->vio); + + DBUG_PRINT("info",("vio_read returned %d, errno: %d", + length, vio_errno(net->vio))); +#if (!defined(_WIN32) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) + /* + We got an error that there was no data on the socket. We now set up + an alarm to not 'read forever', change the socket to non blocking + mode and try again + */ + if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed)) + { + if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */ + { + if (!vio_is_blocking(net->vio)) + { + while (vio_blocking(net->vio,TRUE, 0) < 0) + { + if (vio_should_retry(net->vio) && + retry_count++ < RETRY_COUNT) + continue; + DBUG_PRINT("error", + ("fcntl returned error %d, aborting thread", + vio_errno(net->vio))); +#ifdef EXTRA_DEBUG + fprintf(stderr, + "%s: read: fcntl returned error %d, aborting thread\n", + my_progname,vio_errno(net->vio)); +#endif /* EXTRA_DEBUG */ + len= packet_error; + net->error=2; /* Close socket */ +#ifdef MYSQL_SERVER + net->last_errno=ER_NET_FCNTL_ERROR; +#endif + goto end; + } + } + retry_count=0; + continue; + } + } +#endif /* (!defined(_WIN32) && !defined(__EMX__)) || defined(MYSQL_SERVER) */ + if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && + interrupted) + { /* Probably in MIT threads */ + if (retry_count++ < RETRY_COUNT) + continue; +#ifdef EXTRA_DEBUG + fprintf(stderr, "%s: read looped with error %d, aborting thread\n", + my_progname,vio_errno(net->vio)); +#endif /* EXTRA_DEBUG */ + } +#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) + if (vio_should_retry(net->vio)) + { + DBUG_PRINT("warning",("Interrupted read. Retrying...")); + continue; + } +#endif + DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); + len= packet_error; + net->error=2; /* Close socket */ + goto end; + } + remain -= (ulong) length; + pos+= (ulong) length; + statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + } + if (i == 0) + { /* First parts is packet length */ + ulong helping; + if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr) + { + if (net->buff[net->where_b] != (uchar) 255) + { + DBUG_PRINT("error", + ("Packets out of order (Found: %d, expected %d)", + (int) net->buff[net->where_b + 3], + (uint) (uchar) net->pkt_nr)); +#ifdef EXTRA_DEBUG + fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", + (int) net->buff[net->where_b + 3], + (uint) (uchar) net->pkt_nr); +#endif + } + len= packet_error; +#ifdef MYSQL_SERVER + net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER; +#endif + goto end; + } + 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)) + { +#ifdef MYSQL_SERVER + if (i == 1) + my_net_skip_rest(net, len, &alarmed, &alarm_buff); +#endif + len= packet_error; /* Return error */ + goto end; + } + } + pos=net->buff + net->where_b; + remain = len; + } + } + +end: + if (thr_alarm_in_use(&alarmed)) + { + thr_end_alarm(&alarmed); + vio_blocking(net->vio, net_blocking, 0); + } + net->reading_or_writing=0; + return(len); +} + +ulong my_net_read(NET *net) +{ + size_t len,complen; + +#ifdef HAVE_COMPRESS + if (!net->compress) + { +#endif + len = my_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= my_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_lengt h 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=buffer_length; + + if ((packet_length = my_real_read(net,(size_t *)&complen)) == packet_error) + return packet_error; + if (my_uncompress((unsigned char*) net->buff + net->where_b, &packet_length, &complen)) + { + len= packet_error; + net->error=2; /* caller will close socket */ + net->last_errno=ER_NET_UNCOMPRESS_ERROR; + break; + return packet_error; + } + buffer_length+= complen; + } + /* set values */ + net->buf_length= buffer_length; + net->remain_in_buf= 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; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/password.c b/mariadb-connector-c-v_2.3.7/libmariadb/password.c new file mode 100644 index 0000000..69eabf8 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/password.c @@ -0,0 +1,234 @@ +/* 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 */ + +/* 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 +#include +#include +#include +#include "mysql.h" + + +void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) +{ /* For mysql 3.21.# */ +#ifdef HAVE_purify + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ +#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; +} + +static void old_randominit(struct rand_struct *rand_st,ulong seed1) +{ /* For mysql 3.20.# */ + rand_st->max_value= 0x01FFFFFFL; + rand_st->max_value_dbl=(double) rand_st->max_value; + seed1%=rand_st->max_value; + rand_st->seed1=seed1 ; rand_st->seed2=seed1/2; +} + +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 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; /* skipp 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; +} + +static inline unsigned int char_val(char X) +{ + return (uint) (X >= '0' && X <= '9' ? X-'0' : + X >= 'A' && X <= 'Z' ? X-'A'+10 : + X-'a'+10); +} + +/* + * Genererate 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 my_scramble_41(const unsigned char *buffer, const char *scramble, const char *password) +{ + MYSQL_SHA1_CTX context; + unsigned char sha1[SHA1_MAX_LENGTH]; + unsigned char sha2[SHA1_MAX_LENGTH]; + + + /* Phase 1: hash password */ + MYSQL_SHA1Init(&context); + MYSQL_SHA1Update(&context, (unsigned char *)password, strlen((char *)password)); + MYSQL_SHA1Final(sha1, &context); + + /* Phase 2: hash sha1 */ + MYSQL_SHA1Init(&context); + MYSQL_SHA1Update(&context, (unsigned char*)sha1, SHA1_MAX_LENGTH); + MYSQL_SHA1Final(sha2, &context); + + /* Phase 3: hash scramble + sha2 */ + MYSQL_SHA1Init(&context); + MYSQL_SHA1Update(&context, (unsigned char *)scramble, SCRAMBLE_LENGTH); + MYSQL_SHA1Update(&context, (unsigned char*)sha2, SHA1_MAX_LENGTH); + MYSQL_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 make_scrambled_password(char *to,const char *password) +{ + ulong hash_res[2]; + hash_password(hash_res,password, strlen(password)); + sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); +} + +/* +** This code assumes that len(password) is divideable with 8 and that +** res is big enough (2 in mysql) +*/ + +void get_salt_from_password(ulong *res,const char *password) +{ + res[0]=res[1]=0; + if (password) + { + while (*password) + { + ulong val=0; + uint i; + for (i=0 ; i < 8 ; i++) + val=(val << 4)+char_val(*password++); + *res++=val; + } + } + return; +} + +void make_password_from_salt(char *to, ulong *hash_res) +{ + sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); +} + + +/* + * Genererate a new message based on message and password + * The same thing is done in client and server and the results are checked. + */ +char *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; + hash_password(hash_pass,password, (uint) strlen(password)); + /* Don't use strlen, could be > SCRAMBLE_LENGTH_323 ! */ + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + 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; +} + +my_bool check_scramble(const char *scrambled, const char *message, + ulong *hash_pass, my_bool old_ver) +{ + struct rand_struct rand_st; + ulong hash_message[2]; + char buff[16],*to,extra; /* Big enough for check */ + const char *pos; + + hash_password(hash_message,message, strlen(message)); + if (old_ver) + old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); + else + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); + to=buff; + for (pos=scrambled ; *pos ; pos++) + *to++=(char) (floor(rnd(&rand_st)*31)+64); + if (old_ver) + extra=0; + else + extra=(char) (floor(rnd(&rand_st)*31)); + to=buff; + while (*scrambled) + { + if (*scrambled++ != (char) (*to++ ^ extra)) + return 1; /* Wrong password */ + } + return 0; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/sha1.c b/mariadb-connector-c-v_2.3.7/libmariadb/sha1.c new file mode 100644 index 0000000..b1c231a --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/sha1.c @@ -0,0 +1,325 @@ +/**************************************************************************** + 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 + 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 "my_global.h" +#include "string.h" + +/* This code is heavily based on the PHP md5 implementation */ + +#include "sha1.h" + + +static void SHA1Transform(uint32[5], const unsigned char[64]); +static void SHA1Encode(unsigned char *, uint32 *, unsigned int); +static void 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); \ + } + + +/* {{{ MYSQL_SHA1Init + * SHA1 initialization. Begins an SHA1 operation, writing a new context. + */ +void MYSQL_SHA1Init(MYSQL_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; +} +/* }}} */ + +/* {{{ MYSQL_SHA1Update + SHA1 block update operation. Continues an SHA1 message-digest + operation, processing another message block, and updating the + context. + */ +void MYSQL_SHA1Update(MYSQL_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); + SHA1Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + 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); +} +/* }}} */ + +/* {{{ MYSQL_SHA1Final + SHA1 finalization. Ends an SHA1 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MYSQL_SHA1Final(unsigned char digest[20], MYSQL_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); + MYSQL_SHA1Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MYSQL_SHA1Update(context, bits, 8); + + /* Store state in digest */ + SHA1Encode(digest, context->state, 20); + + /* Zeroize sensitive information. + */ + memset((unsigned char*) context, 0, sizeof(*context)); +} +/* }}} */ + +/* {{{ SHA1Transform + * SHA1 basic transformation. Transforms state based on block. + */ +static void 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; + + 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)); +} +/* }}} */ + +/* {{{ SHA1Encode + Encodes input (uint32) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void 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); + } +} +/* }}} */ + +/* {{{ SHA1Decode + Decodes input (unsigned char) into output (uint32). Assumes len is + a multiple of 4. + */ +static void 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/mariadb-connector-c-v_2.3.7/libmariadb/str2int.c b/mariadb-connector-c-v_2.3.7/libmariadb/str2int.c new file mode 100644 index 0000000..3905b3e --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/str2int.c @@ -0,0 +1,202 @@ +/* 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 */ + +/* + str2int(src, radix, lower, upper, &val) + converts the string pointed to by src to an integer and stores it in + val. It skips leading spaces and tabs (but not newlines, formfeeds, + backspaces), then it accepts an optional sign and a sequence of digits + in the specified radix. The result should satisfy lower <= *val <= upper. + The result is a pointer to the first character after the number; + trailing spaces will NOT be skipped. + + If an error is detected, the result will be NullS, the value put + in val will be 0, and errno will be set to + EDOM if there are no digits + ERANGE if the result would overflow or otherwise fail to lie + within the specified bounds. + Check that the bounds are right for your machine. + This looks amazingly complicated for what you probably thought was an + easy task. Coping with integer overflow and the asymmetric range of + twos complement machines is anything but easy. + + So that users of atoi and atol can check whether an error occured, + I have taken a wholly unprecedented step: errno is CLEARED if this + call has no problems. +*/ + +#include +#include "m_string.h" +#include "m_ctype.h" +#include "my_sys.h" /* defines errno */ +#include + +#define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\ + X >= 'A' && X <= 'Z' ? X-'A'+10 :\ + X >= 'a' && X <= 'z' ? X-'a'+10 :\ + '\177') + +char *str2int(register const char *src, register int radix, long int lower, long int upper, long int *val) +{ + int sign; /* is number negative (+1) or positive (-1) */ + int n; /* number of digits yet to be converted */ + long limit; /* "largest" possible valid input */ + long scale; /* the amount to multiply next digit by */ + long sofar; /* the running value */ + register int d; /* (negative of) next digit */ + char *start; + int digits[32]; /* Room for numbers */ + + /* Make sure *val is sensible in case of error */ + + *val = 0; + + /* Check that the radix is in the range 2..36 */ + +#ifndef DBUG_OFF + if (radix < 2 || radix > 36) { + errno=EDOM; + return NullS; + } +#endif + + /* The basic problem is: how do we handle the conversion of + a number without resorting to machine-specific code to + check for overflow? Obviously, we have to ensure that + no calculation can overflow. We are guaranteed that the + "lower" and "upper" arguments are valid machine integers. + On sign-and-magnitude, twos-complement, and ones-complement + machines all, if +|n| is representable, so is -|n|, but on + twos complement machines the converse is not true. So the + "maximum" representable number has a negative representative. + Limit is set to min(-|lower|,-|upper|); this is the "largest" + number we are concerned with. */ + + /* Calculate Limit using Scale as a scratch variable */ + + if ((limit = lower) > 0) limit = -limit; + if ((scale = upper) > 0) scale = -scale; + if (scale < limit) limit = scale; + + /* Skip leading spaces and check for a sign. + Note: because on a 2s complement machine MinLong is a valid + integer but |MinLong| is not, we have to keep the current + converted value (and the scale!) as *negative* numbers, + so the sign is the opposite of what you might expect. + */ + while (isspace(*src)) src++; + sign = -1; + if (*src == '+') src++; else + if (*src == '-') src++, sign = 1; + + /* Skip leading zeros so that we never compute a power of radix + in scale that we won't have a need for. Otherwise sticking + enough 0s in front of a number could cause the multiplication + to overflow when it neededn't. + */ + start=(char*) src; + while (*src == '0') src++; + + /* Move over the remaining digits. We have to convert from left + to left in order to avoid overflow. Answer is after last digit. + */ + + for (n = 0; (digits[n]=char_val(*src)) < radix && n < 20; n++,src++) ; + + /* Check that there is at least one digit */ + + if (start == src) { + errno=EDOM; + return NullS; + } + + /* The invariant we want to maintain is that src is just + to the right of n digits, we've converted k digits to + sofar, scale = -radix**k, and scale < sofar < 0. Now + if the final number is to be within the original + Limit, we must have (to the left)*scale+sofar >= Limit, + or (to the left)*scale >= Limit-sofar, i.e. the digits + to the left of src must form an integer <= (Limit-sofar)/(scale). + In particular, this is true of the next digit. In our + incremental calculation of Limit, + + IT IS VITAL that (-|N|)/(-|D|) = |N|/|D| + */ + + for (sofar = 0, scale = -1; --n >= 1;) + { + if ((long) -(d=digits[n]) < limit) { + errno=ERANGE; + return NullS; + } + limit = (limit+d)/radix, sofar += d*scale; scale *= radix; + } + if (n == 0) + { + if ((long) -(d=digits[n]) < limit) /* get last digit */ + { + errno=ERANGE; + return NullS; + } + sofar+=d*scale; + } + + /* Now it might still happen that sofar = -32768 or its equivalent, + so we can't just multiply by the sign and check that the result + is in the range lower..upper. All of this caution is a right + pain in the neck. If only there were a standard routine which + says generate thus and such a signal on integer overflow... + But not enough machines can do it *SIGH*. + */ + if (sign < 0) + { + if (sofar < -LONG_MAX || (sofar= -sofar) > upper) + { + errno=ERANGE; + return NullS; + } + } + else if (sofar < lower) + { + errno=ERANGE; + return NullS; + } + *val = sofar; + errno=0; /* indicate that all went well */ + return (char*) src; +} + + /* Theese are so slow compared with ordinary, optimized atoi */ + +#ifdef WANT_OUR_ATOI + +int atoi(const char *src) +{ + long val; + str2int(src, 10, (long) INT_MIN, (long) INT_MAX, &val); + return (int) val; +} + + +long atol(const char *src) +{ + long val; + str2int(src, 10, LONG_MIN, LONG_MAX, &val); + return val; +} + +#endif /* WANT_OUR_ATOI */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strcend.c b/mariadb-connector-c-v_2.3.7/libmariadb/strcend.c new file mode 100644 index 0000000..b539df9 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strcend.c @@ -0,0 +1,48 @@ +/* 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 : strcend.c + Author : Michael Widenius: ifdef MC68000 + Updated: 20 April 1984 + Defines: strcend() + + strcend(s, c) returns a pointer to the first place in s where c + occurs, or a pointer to the end-null of s if c does not occur in s. +*/ + +#include +#include "m_string.h" + +/** + \fn char *strcend + \brief returns a pointer to the first occurence of specified stopchar + \param str char * + \param stopchar char + + returns a poimter to the first occurence of stopchar or to null char, + if stopchar wasn't found. +*/ +char *strcend(register const char *str, register char stopchar) +{ + for (;;) + { + if (*str == stopchar) + return (char*) str; + if (!*str++) + return (char*) str-1; + } +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strcont.c b/mariadb-connector-c-v_2.3.7/libmariadb/strcont.c new file mode 100644 index 0000000..b27e7ed --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strcont.c @@ -0,0 +1,46 @@ +/* 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 : strcont.c + Author : Monty + Updated: 1988.07.27 + Defines: strcont() + + strcont(str, set) if str contanies any character in the string set. + The result is the position of the first found character in str, or NullS + if there isn't anything found. + +*/ + +#include +#include "m_string.h" + +my_string strcont(reg1 const char *str,reg2 const char *set) +{ + reg3 my_string start = (my_string) set; + + while (*str) + { + while (*set) + { + if (*set++ == *str) + return ((char*) str); + } + set=start; str++; + } + return (NullS); +} /* strcont */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strend.c b/mariadb-connector-c-v_2.3.7/libmariadb/strend.c new file mode 100644 index 0000000..a56caf0 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strend.c @@ -0,0 +1,50 @@ +/* Copyright (C) 2002 MySQL 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 : strend.c + Author : Richard A. O'Keefe. + Updated: 23 April 1984 + Defines: strend() + + strend(s) returns a character pointer to the NUL which ends s. That + is, strend(s)-s == strlen(s). This is useful for adding things at + the end of strings. It is redundant, because strchr(s,'\0') could + be used instead, but this is clearer and faster. + Beware: the asm version works only if strlen(s) < 65535. +*/ + +#include +#include "m_string.h" + +#if VaxAsm + +char *strend(s) +const char *s; +{ + asm("locc $0,$65535,*4(ap)"); + asm("movl r1,r0"); +} + +#else /* ~VaxAsm */ + +char *strend(register const char *s) +{ + while (*s++); + return (char*) (s-1); +} + +#endif /* VaxAsm */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strfill.c b/mariadb-connector-c-v_2.3.7/libmariadb/strfill.c new file mode 100644 index 0000000..dce0186 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strfill.c @@ -0,0 +1,36 @@ +/* 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 : strfill.c + Author : Monty + Updated: 1987.04.16 + Defines: strfill() + + strfill(dest, len, fill) makes a string of fill-characters. The result + string is of length == len. The des+len character is allways set to NULL. + strfill() returns pointer to dest+len; +*/ + +#include +#include "m_string.h" + +my_string strfill(my_string s, size_t len, pchar fill) +{ + while (len--) *s++ = fill; + *(s) = '\0'; + return(s); +} /* strfill */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/string.c b/mariadb-connector-c-v_2.3.7/libmariadb/string.c new file mode 100644 index 0000000..bb3668e --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/string.c @@ -0,0 +1,127 @@ +/* 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 */ + +/* + Code for handling strings with can grow dynamicly. + Copyright Monty Program KB. + By monty. +*/ + +#include "mysys_priv.h" +#include + +my_bool init_dynamic_string(DYNAMIC_STRING *str, const char *init_str, + size_t init_alloc, size_t alloc_increment) +{ + uint length; + DBUG_ENTER("init_dynamic_string"); + + 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*) my_malloc(init_alloc,MYF(MY_WME)))) + DBUG_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; + DBUG_RETURN(FALSE); +} + + +my_bool dynstr_set(DYNAMIC_STRING *str, const char *init_str) +{ + uint length; + DBUG_ENTER("dynstr_set"); + + 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*) my_realloc(str->str,str->max_length,MYF(MY_WME)))) + DBUG_RETURN(TRUE); + } + if (init_str) + { + str->length=length-1; + memcpy(str->str,init_str,length); + } + else + str->length=0; + DBUG_RETURN(FALSE); +} + + +my_bool dynstr_realloc(DYNAMIC_STRING *str, size_t additional_size) +{ + DBUG_ENTER("dynstr_realloc"); + + if (!additional_size) DBUG_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*) my_realloc(str->str,str->max_length,MYF(MY_WME)))) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +my_bool dynstr_append(DYNAMIC_STRING *str, const char *append) +{ + return dynstr_append_mem(str,append,strlen(append)); +} + + +my_bool 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*) my_realloc(str->str,new_length,MYF(MY_WME)))) + 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 dynstr_free(DYNAMIC_STRING *str) +{ + if (str->str) + { + my_free(str->str); + str->str=0; + } +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strinstr.c b/mariadb-connector-c-v_2.3.7/libmariadb/strinstr.c new file mode 100644 index 0000000..6779930 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strinstr.c @@ -0,0 +1,50 @@ +/* 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 : strinstr.c + Author : Monty & David + Updated: 1986.12.08 + Defines: strinstr() + + strinstr(src, pat) looks for an instance of pat in src. pat is not a + regex(3) pattern, it is a literal string which must be matched exactly. + The result 0 if the pattern was not found else it is the start char of + the pattern counted from the beginning of the string, where the first + char is 1. +*/ + +#include +#include "m_string.h" + +uint strinstr(reg1 const char *str,reg4 const char *search) +{ + reg2 my_string i,j; + my_string start = (my_string) str; + + skipp: + while (*str != '\0') + { + if (*str++ == *search) + { + i=(my_string) str; j= (my_string) search+1; + while (*j) + if (*i++ != *j++) goto skipp; + return ((uint) (str - start)); + } + } + return (0); +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strmake.c b/mariadb-connector-c-v_2.3.7/libmariadb/strmake.c new file mode 100644 index 0000000..1638fe6 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strmake.c @@ -0,0 +1,54 @@ +/* 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 : strmake.c + Author : Michael Widenius + Updated: 20 Jul 1984 + Defines: strmake() + + strmake(dst,src,length) moves length characters, or until end, of src to + dst and appends a closing NUL to dst. + Note that is strlen(src) >= length then dst[length] will be set to \0 + strmake() returns pointer to closing null +*/ + +#include +#include "m_string.h" + +#ifdef BAD_STRING_COMPILER + +char *strmake(char *dst,const char *src, size_t length) +{ + reg1 char *res; + + if ((res=memccpy(dst,src,0,length))) + return res-1; + dst[length]=0; + return dst+length; +} + +#define strmake strmake_overlapp /* Use orginal for overlapping str */ +#endif + +char *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/mariadb-connector-c-v_2.3.7/libmariadb/strmov.c b/mariadb-connector-c-v_2.3.7/libmariadb/strmov.c new file mode 100644 index 0000000..3178aaf --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strmov.c @@ -0,0 +1,59 @@ +/* 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 */ + +/* + strmov(dst, src) moves all the characters of src (including the + closing NUL) to dst, and returns a pointer to the new closing NUL in + dst. The similar UNIX routine strcpy returns the old value of dst, + which I have never found useful. strmov(strmov(dst,a),b) moves a//b + into dst, which seems useful. +*/ + +#include +#include "m_string.h" + +#ifdef BAD_STRING_COMPILER +#undef strmov +#define strmov strmov_overlapp +#endif + +#ifndef strmov + +#if !defined(MC68000) && !defined(DS90) + +char *strmov(register char *dst, register const char *src) +{ + while ((*dst++ = *src++)) ; + return dst-1; +} + +#else + +char *strmov(dst, src) + char *dst, *src; +{ + asm(" movl 4(a7),a1 "); + asm(" movl 8(a7),a0 "); + asm(".L4: movb (a0)+,(a1)+ "); + asm(" jne .L4 "); + asm(" movl a1,d0 "); + asm(" subql #1,d0 "); +} + +#endif + +#endif /* strmov */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strnlen.c b/mariadb-connector-c-v_2.3.7/libmariadb/strnlen.c new file mode 100644 index 0000000..1e6693e --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strnlen.c @@ -0,0 +1,36 @@ +/* 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 : strnlen.c + Author : Michael Widenius + Updated: 20 April 1984 + Defines: strnlen. + strnlen(s, len) returns the length of s or len if s is longer than len. +*/ + +#include +#include "m_string.h" + +#ifndef HAVE_STRNLEN + +uint strnlen(register const char *s, register uint maxlen) +{ + const char *end= (const char *)memchr(s, '\0', maxlen); + return end ? (uint) (end - s) : maxlen; +} + +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strnmov.c b/mariadb-connector-c-v_2.3.7/libmariadb/strnmov.c new file mode 100644 index 0000000..30a8be8 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strnmov.c @@ -0,0 +1,36 @@ +/* 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 */ + +/* + strnmov(dst,src,length) moves length characters, or until end, of src to + dst and appends a closing NUL to dst if src is shorter than length. + The result is a pointer to the first NUL in dst, or is dst+n if dst was + truncated. +*/ + +#include +#include "m_string.h" + +char *strnmov(register char *dst, register const char *src, uint n) +{ + while (n-- != 0) { + if (!(*dst++ = *src++)) { + return (char*) dst-1; + } + } + return dst; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strto.c b/mariadb-connector-c-v_2.3.7/libmariadb/strto.c new file mode 100644 index 0000000..17e6dbe --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strto.c @@ -0,0 +1,209 @@ +/* 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 */ + +/* + strtol,strtoul,strtoll,strtoull + convert string to long, unsigned long, long long or unsigned long long. + strtoxx(char *src,char **ptr,int base) + converts the string pointed to by src to an long of appropriate long and + returnes it. It skips leading spaces and tabs (but not newlines, formfeeds, + backspaces), then it accepts an optional sign and a sequence of digits + in the specified radix. + If the value of ptr is not (char **)NULL, a pointer to the character + terminating the scan is returned in the location pointed to by ptr. + Trailing spaces will NOT be skipped. + + If an error is detected, the result will be LONG_MIN, 0 or LONG_MAX, + (or LONGLONG..) and errno will be set to + EDOM if there are no digits + ERANGE if the result would overflow. + the ptr will be set to src. + This file is based on the strtol from the the GNU C Library. + it can be compiled with the UNSIGNED and/or LONGLONG flag set +*/ + +#define strtoll glob_strtoll /* Fix for True64 */ + +#include "m_string.h" +#include "m_ctype.h" +#include "my_sys.h" /* defines errno */ +#include + +#undef strtoull +#undef strtoll +#undef strtoul +#undef strtol +#ifdef USE_LONGLONG +#define UTYPE_MAX (~(ulonglong) 0) +#define TYPE_MIN LONGLONG_MIN +#define TYPE_MAX LONGLONG_MAX +#define longtype longlong +#define ulongtype ulonglong +#ifdef USE_UNSIGNED +#define function ulongtype strtoull +#else +#define function longtype strtoll +#endif +#else +#define UTYPE_MAX (ulong) ~0L +#define TYPE_MIN LONG_MIN +#define TYPE_MAX LONG_MAX +#define longtype long +#define ulongtype unsigned long +#ifdef USE_UNSIGNED +#define function ulongtype strtoul +#else +#define function longtype strtol +#endif +#endif + + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ + + +function (const char *nptr,char **endptr,int base) +{ + int negative; + register ulongtype cutoff; + register unsigned int cutlim; + register ulongtype i; + register const char *s; + register unsigned char c; + const char *save; + int overflow; + + if (base < 0 || base == 1 || base > 36) + base = 10; + + s = nptr; + + /* Skip white space. */ + while (isspace (*s)) + ++s; + if (*s == '\0') + { + goto noconv; + } + + /* Check for a sign. */ + if (*s == '-') + { + negative = 1; + ++s; + } + else if (*s == '+') + { + negative = 0; + ++s; + } + else + negative = 0; + + if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X') + s += 2; + + /* If BASE is zero, figure it out ourselves. */ + if (base == 0) + { + if (*s == '0') + { + if (toupper (s[1]) == 'X') + { + s += 2; + base = 16; + } + else + base = 8; + } + else + base = 10; + } + + /* Save the pointer so we can check later if anything happened. */ + save = s; + + cutoff = UTYPE_MAX / (unsigned long int) base; + cutlim = (uint) (UTYPE_MAX % (unsigned long int) base); + + overflow = 0; + i = 0; + for (c = *s; c != '\0'; c = *++s) + { + if (isdigit (c)) + c -= '0'; + else if (isalpha (c)) + c = toupper (c) - 'A' + 10; + else + break; + if (c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + i *= (ulongtype) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (char *) s; + +#ifndef USE_UNSIGNED + /* Check for a value that is within the range of + `unsigned long int', but outside the range of `long int'. */ + if (negative) + { + if (i > (ulongtype) TYPE_MIN) + overflow = 1; + } + else if (i > (ulongtype) TYPE_MAX) + overflow = 1; +#endif + + if (overflow) + { + my_errno=ERANGE; +#ifdef USE_UNSIGNED + return UTYPE_MAX; +#else + return negative ? TYPE_MIN : TYPE_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return (negative ? -((longtype) i) : (longtype) i); + +noconv: + /* There was no number to convert. */ + my_errno=EDOM; + if (endptr != NULL) + *endptr = (char *) nptr; + return 0L; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strtoll.c b/mariadb-connector-c-v_2.3.7/libmariadb/strtoll.c new file mode 100644 index 0000000..34d1d7d --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strtoll.c @@ -0,0 +1,198 @@ +/* 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 */ + +/* This is defines strtoll() if neaded */ + +#include +#include +#if !defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG) +#define USE_LONGLONG + +#define strtoll glob_strtoll /* Fix for True64 */ + +#include "m_string.h" +#include "m_ctype.h" +#include "my_sys.h" /* defines errno */ +#include + +#undef strtoull +#undef strtoll +#undef strtoul +#undef strtol +#ifdef USE_LONGLONG +#define UTYPE_MAX (~(ulonglong) 0) +#define TYPE_MIN LONGLONG_MIN +#define TYPE_MAX LONGLONG_MAX +#define longtype longlong +#define ulongtype ulonglong +#ifdef USE_UNSIGNED +#define function ulongtype strtoull +#else +#define function longtype strtoll +#endif +#else +#define UTYPE_MAX (ulong) ~0L +#define TYPE_MIN LONG_MIN +#define TYPE_MAX LONG_MAX +#define longtype long +#define ulongtype unsigned long +#ifdef USE_UNSIGNED +#define function ulongtype strtoul +#else +#define function longtype strtol +#endif +#endif + + +/* Convert NPTR to an `unsigned long int' or `long int' in base BASE. + If BASE is 0 the base is determined by the presence of a leading + zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal. + If BASE is < 2 or > 36, it is reset to 10. + If ENDPTR is not NULL, a pointer to the character after the last + one converted is stored in *ENDPTR. */ + + +function (const char *nptr,char **endptr,int base) +{ + int negative; + register ulongtype cutoff; + register unsigned int cutlim; + register ulongtype i; + register const char *s; + register unsigned char c; + const char *save; + int overflow; + + if (base < 0 || base == 1 || base > 36) + base = 10; + + s = nptr; + + /* Skip white space. */ + while (isspace (*s)) + ++s; + if (*s == '\0') + { + goto noconv; + } + + /* Check for a sign. */ + if (*s == '-') + { + negative = 1; + ++s; + } + else if (*s == '+') + { + negative = 0; + ++s; + } + else + negative = 0; + + if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X') + s += 2; + + /* If BASE is zero, figure it out ourselves. */ + if (base == 0) + { + if (*s == '0') + { + if (toupper (s[1]) == 'X') + { + s += 2; + base = 16; + } + else + base = 8; + } + else + base = 10; + } + + /* Save the pointer so we can check later if anything happened. */ + save = s; + + cutoff = UTYPE_MAX / (unsigned long int) base; + cutlim = (uint) (UTYPE_MAX % (unsigned long int) base); + + overflow = 0; + i = 0; + for (c = *s; c != '\0'; c = *++s) + { + if (isdigit (c)) + c -= '0'; + else if (isalpha (c)) + c = toupper (c) - 'A' + 10; + else + break; + if (c >= base) + break; + /* Check for overflow. */ + if (i > cutoff || (i == cutoff && c > cutlim)) + overflow = 1; + else + { + i *= (ulongtype) base; + i += c; + } + } + + /* Check if anything actually happened. */ + if (s == save) + goto noconv; + + /* Store in ENDPTR the address of one character + past the last character we converted. */ + if (endptr != NULL) + *endptr = (char *) s; + +#ifndef USE_UNSIGNED + /* Check for a value that is within the range of + `unsigned long int', but outside the range of `long int'. */ + if (negative) + { + if (i > (ulongtype) TYPE_MIN) + overflow = 1; + } + else if (i > (ulongtype) TYPE_MAX) + overflow = 1; +#endif + + if (overflow) + { + my_errno=ERANGE; +#ifdef USE_UNSIGNED + return UTYPE_MAX; +#else + return negative ? TYPE_MIN : TYPE_MAX; +#endif + } + + /* Return the result of the appropriate sign. */ + return (negative ? -((longtype) i) : (longtype) i); + +noconv: + /* There was no number to convert. */ + my_errno=EDOM; + if (endptr != NULL) + *endptr = (char *) nptr; + return 0L; +} + + +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strtoull.c b/mariadb-connector-c-v_2.3.7/libmariadb/strtoull.c new file mode 100644 index 0000000..417c1af --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strtoull.c @@ -0,0 +1,26 @@ +/* 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 */ + +/* This is defines strtoull() */ + +#include +#include +#if !defined(HAVE_STRTOULL) && defined(HAVE_LONG_LONG) +#define USE_UNSIGNED +#define USE_LONGLONG +#include "strto.c" +#endif diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strxmov.c b/mariadb-connector-c-v_2.3.7/libmariadb/strxmov.c new file mode 100644 index 0000000..f3125aa --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strxmov.c @@ -0,0 +1,50 @@ +/* Copyright (C) 2002 MySQL 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 : strxmov.c + Author : Richard A. O'Keefe. + Updated: 25 may 1984 + Defines: strxmov() + + strxmov(dst, src1, ..., srcn, NullS) + moves the concatenation of src1,...,srcn to dst, terminates it + with a NUL character, and returns a pointer to the terminating NUL. + It is just like strmov except that it concatenates multiple sources. + Beware: the last argument should be the null character pointer. + Take VERY great care not to omit it! Also be careful to use NullS + and NOT to use 0, as on some machines 0 is not the same size as a + character pointer, or not the same bit pattern as NullS. +*/ + +#include +#include "m_string.h" +#include + +char *strxmov(char *dst,const char *src, ...) +{ + va_list pvar; + + va_start(pvar,src); + while (src != NullS) { + while ((*dst++ = *src++)) ; + dst--; + src = va_arg(pvar, char *); + } + va_end(pvar); + *dst = 0; /* there might have been no sources! */ + return dst; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/strxnmov.c b/mariadb-connector-c-v_2.3.7/libmariadb/strxnmov.c new file mode 100644 index 0000000..d5167f6 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/strxnmov.c @@ -0,0 +1,48 @@ +/* File : strxnmov.c + Author : Richard A. O'Keefe. + Updated: 2 June 1984 + Defines: strxnmov() + + strxnmov(dst, len, src1, ..., srcn, NullS) + moves the first len characters of the concatenation of src1,...,srcn + to dst. If there aren't that many characters, a NUL character will + be added to the end of dst to terminate it properly. This gives the + same effect as calling strxcpy(buff, src1, ..., srcn, NullS) with a + large enough buffer, and then calling strnmov(dst, buff, len). + It is just like strnmov except that it concatenates multiple sources. + Beware: the last argument should be the null character pointer. + Take VERY great care not to omit it! Also be careful to use NullS + and NOT to use 0, as on some machines 0 is not the same size as a + character pointer, or not the same bit pattern as NullS. + + Note: strxnmov is like strnmov in that it moves up to len + characters; dst will be padded on the right with one NUL characters if + needed. +*/ + +#include +#include "m_string.h" +#include + +char *strxnmov(char *dst, size_t len, const char *src, ...) +{ + va_list pvar; + char *end_of_dst=dst+len; + + va_start(pvar,src); + while (src != NullS) + { + do + { + if (dst == end_of_dst) + goto end; + } + while ((*dst++ = *src++)); + dst--; + src = va_arg(pvar, char *); + } + *dst=0; +end: + va_end(pvar); + return dst; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/thr_mutex.c b/mariadb-connector-c-v_2.3.7/libmariadb/thr_mutex.c new file mode 100644 index 0000000..102bb33 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/thr_mutex.c @@ -0,0 +1,231 @@ +/* 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 */ + +/* This makes a wrapper for mutex handling to make it easier to debug mutex */ + +#include +#if defined(HAVE_LINUXTHREADS) && !defined (__USE_UNIX98) +#define __USE_UNIX98 /* To get rw locks under Linux */ +#endif +#include +#if defined(THREAD) && defined(SAFE_MUTEX) +#undef SAFE_MUTEX /* Avoid safe_mutex redefinitions */ +#include + +#ifndef DO_NOT_REMOVE_THREAD_WRAPPERS +/* Remove wrappers */ +#undef pthread_mutex_init +#undef pthread_mutex_lock +#undef pthread_mutex_unlock +#undef pthread_mutex_destroy +#undef pthread_cond_wait +#undef pthread_cond_timedwait +#ifdef HAVE_NONPOSIX_PTHREAD_MUTEX_INIT +#define pthread_mutex_init(a,b) my_pthread_mutex_init((a),(b)) +#endif +#endif /* DO_NOT_REMOVE_THREAD_WRAPPERS */ + +int safe_mutex_init(safe_mutex_t *mp, + const pthread_mutexattr_t *attr __attribute__((unused))) +{ + bzero((char*) mp,sizeof(*mp)); + pthread_mutex_init(&mp->global,MY_MUTEX_INIT_ERRCHK); + pthread_mutex_init(&mp->mutex,attr); + return 0; +} + +int safe_mutex_lock(safe_mutex_t *mp,const char *file, uint line) +{ + int error; + pthread_mutex_lock(&mp->global); + if (mp->count > 0 && pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr,"safe_mutex: Trying to lock mutex at %s, line %d, when the mutex was already locked at %s, line %d\n", + file,line,mp->file,mp->line); + fflush(stderr); + abort(); + } + pthread_mutex_unlock(&mp->global); + error=pthread_mutex_lock(&mp->mutex); + if (error || (error=pthread_mutex_lock(&mp->global))) + { + fprintf(stderr,"Got error %d when trying to lock mutex at %s, line %d\n", + error, file, line); + fflush(stderr); + abort(); + } + if (mp->count++) + { + fprintf(stderr,"safe_mutex: Error in thread libray: Got mutex at %s, line %d more than 1 time\n", file,line); + fflush(stderr); + abort(); + } + mp->thread=pthread_self(); + mp->file= (char*) file; + mp->line=line; + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_mutex_unlock(safe_mutex_t *mp,const char *file, uint line) +{ + int error; + pthread_mutex_lock(&mp->global); + if (mp->count == 0) + { + fprintf(stderr,"safe_mutex: Trying to unlock mutex that wasn't locked at %s, line %d\n Last used at %s, line: %d\n", + file,line,mp->file ? mp->file : "",mp->line); + fflush(stderr); + abort(); + } + if (!pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr,"safe_mutex: Trying to unlock mutex at %s, line %d that was locked by another thread at: %s, line: %d\n", + file,line,mp->file,mp->line); + fflush(stderr); + abort(); + } + mp->count--; +#ifdef _WIN32 + pthread_mutex_unlock(&mp->mutex); + error=0; +#else + error=pthread_mutex_unlock(&mp->mutex); + if (error) + { + fprintf(stderr,"safe_mutex: Got error: %d when trying to unlock mutex at %s, line %d\n", error, file, line); + fflush(stderr); + abort(); + } +#endif /* _WIN32 */ + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_cond_wait(pthread_cond_t *cond, safe_mutex_t *mp, const char *file, + uint line) +{ + int error; + pthread_mutex_lock(&mp->global); + if (mp->count == 0) + { + fprintf(stderr,"safe_mutex: Trying to cond_wait on a unlocked mutex at %s, line %d\n",file,line); + fflush(stderr); + abort(); + } + if (!pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr,"safe_mutex: Trying to cond_wait on a mutex at %s, line %d that was locked by another thread at: %s, line: %d\n", + file,line,mp->file,mp->line); + fflush(stderr); + abort(); + } + + if (mp->count-- != 1) + { + fprintf(stderr,"safe_mutex: Count was %d on locked mutex at %s, line %d\n", + mp->count+1, file, line); + fflush(stderr); + abort(); + } + pthread_mutex_unlock(&mp->global); + error=pthread_cond_wait(cond,&mp->mutex); + pthread_mutex_lock(&mp->global); + if (error) + { + fprintf(stderr,"safe_mutex: Got error: %d when doing a safe_mutex_wait at %s, line %d\n", error, file, line); + fflush(stderr); + abort(); + } + if (mp->count++) + { + fprintf(stderr, + "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d\n", + mp->count-1, my_thread_id(), file, line); + fflush(stderr); + abort(); + } + mp->thread=pthread_self(); + mp->file= (char*) file; + mp->line=line; + pthread_mutex_unlock(&mp->global); + return error; +} + + +int safe_cond_timedwait(pthread_cond_t *cond, safe_mutex_t *mp, + struct timespec *abstime, + const char *file, uint line) +{ + int error; + pthread_mutex_lock(&mp->global); + if (mp->count != 1 || !pthread_equal(pthread_self(),mp->thread)) + { + fprintf(stderr,"safe_mutex: Trying to cond_wait at %s, line %d on a not hold mutex\n",file,line); + fflush(stderr); + abort(); + } + mp->count--; /* Mutex will be released */ + pthread_mutex_unlock(&mp->global); + error=pthread_cond_timedwait(cond,&mp->mutex,abstime); +#ifdef EXTRA_DEBUG + if (error && (error != EINTR && error != ETIMEDOUT)) + { + fprintf(stderr,"safe_mutex: Got error: %d when doing a safe_mutex_timedwait at %s, line %d\n", error, file, line); + } +#endif + pthread_mutex_lock(&mp->global); + if (mp->count++) + { + fprintf(stderr, + "safe_mutex: Count was %d in thread %lx when locking mutex at %s, line %d (error: %d)\n", + mp->count-1, my_thread_id(), file, line, error); + fflush(stderr); + abort(); + } + mp->thread=pthread_self(); + mp->file= (char*) file; + mp->line=line; + pthread_mutex_unlock(&mp->global); + return error; +} + +int safe_mutex_destroy(safe_mutex_t *mp, const char *file, uint line) +{ + int error=0; + if (mp->count != 0) + { + fprintf(stderr,"safe_mutex: Trying to destroy a mutex that was locked at %s, line %d at %s, line %d\n", + mp->file,mp->line, file, line); + fflush(stderr); + abort(); + } +#ifdef _WIN32 + pthread_mutex_destroy(&mp->global); + pthread_mutex_destroy(&mp->mutex); +#else + if (pthread_mutex_destroy(&mp->global)) + error=1; + if (pthread_mutex_destroy(&mp->mutex)) + error=1; +#endif + return error; +} + +#endif /* THREAD && SAFE_MUTEX */ diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/typelib.c b/mariadb-connector-c-v_2.3.7/libmariadb/typelib.c new file mode 100644 index 0000000..58f93e3 --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/typelib.c @@ -0,0 +1,106 @@ +/* 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 */ + +/* Functions to handle typelib */ + +#include "mysys_priv.h" +#include +#include + +/*************************************************************************** +** Search after a fieldtype. Endspace in x is not compared. +** If part, uniq field is found and full_name == 0 then x is expanded +** to full field. +** full_name has the following bit values: +** If & 1 accept only whole names +** If & 2 don't expand if half field +** If & 4 allow #number# as type +****************************************************************************/ + +int find_type(my_string x, TYPELIB *typelib, uint full_name) +{ + int find,pos,findpos= 0; + reg1 my_string i; + reg2 const char *j; + DBUG_ENTER("find_type"); + DBUG_PRINT("enter",("x: '%s' lib: %lx",x,typelib)); + + if (!typelib->count) + { + DBUG_PRINT("exit",("no count")); + DBUG_RETURN(0); + } + LINT_INIT(findpos); + find=0; + for (pos=0 ; (j=typelib->type_names[pos]) ; pos++) + { + for (i=x ; *i && toupper(*i) == toupper(*j) ; i++, j++) ; + if (! *j) + { + while (*i == ' ') + i++; /* skipp_end_space */ + if (! *i) + DBUG_RETURN(pos+1); + } + if (! *i && (!*j || !(full_name & 1))) + { + find++; + findpos=pos; + } + } + if (find == 0 && (full_name & 4) && x[0] == '#' && strend(x)[-1] == '#' && + (findpos=atoi(x+1)-1) >= 0 && (uint) findpos < typelib->count) + find=1; + else if (find == 0 || ! x[0]) + { + DBUG_PRINT("exit",("Couldn't find type")); + DBUG_RETURN(0); + } + else if (find != 1 || (full_name & 1)) + { + DBUG_PRINT("exit",("Too many possybilities")); + DBUG_RETURN(-1); + } + if (!(full_name & 2)) + (void) strmov(x,typelib->type_names[findpos]); + DBUG_RETURN(findpos+1); +} /* find_type */ + + + /* Get name of type nr 'nr' */ + /* Warning first type is 1, 0 = empty field */ + +void make_type(register my_string to, register uint nr, register TYPELIB *typelib) +{ + DBUG_ENTER("make_type"); + if (!nr) + to[0]=0; + else + (void) strmov(to,get_type(typelib,nr-1)); + DBUG_VOID_RETURN; +} /* make_type */ + + + /* Get type */ + /* Warning first type is 0 */ + +const char *get_type(TYPELIB *typelib, uint nr) +{ + if (nr < (uint) typelib->count && typelib->type_names) + return(typelib->type_names[nr]); + return "?"; +} diff --git a/mariadb-connector-c-v_2.3.7/libmariadb/violite.c b/mariadb-connector-c-v_2.3.7/libmariadb/violite.c new file mode 100644 index 0000000..995d25a --- /dev/null +++ b/mariadb-connector-c-v_2.3.7/libmariadb/violite.c @@ -0,0 +1,709 @@ +/* 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 */ + +/* + Note that we can't have assertion on file descriptors; The reason for + this is that during mysql shutdown, another thread can close a file + we are working on. In this case we should just return read errors from + the file descriptior. +*/ + +#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_POLL +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_OPENSSL +#include +#endif + +#ifdef _WIN32 +#define socklen_t int +#pragma comment (lib, "ws2_32") +#endif + +#if !defined(_WIN32) && !defined(HAVE_BROKEN_NETINET_INCLUDES) +#include +#include +#if !defined(alpha_linux_port) +#include +#endif +#endif + +#if defined(__EMX__) || defined(OS2) +#define ioctlsocket ioctl +#endif /* defined(__EMX__) */ + +#if defined(MSDOS) || defined(_WIN32) +#define O_NONBLOCK 1 /* For emulation of fcntl() */ +#endif +#ifndef EWOULDBLOCK +#define SOCKET_EWOULDBLOCK SOCKET_EAGAIN +#endif + +#include +#include + +#ifdef _WIN32 +#define ma_get_error() WSAGetLastError() +#else +#define ma_get_error() errno +#endif + +typedef void *vio_ptr; +typedef char *vio_cstring; + +/* + * Helper to fill most of the Vio* with defaults. + */ + +void vio_reset(Vio* vio, enum enum_vio_type type, + my_socket sd, HANDLE hPipe, + my_bool localhost) +{ + uchar *save_cache= vio->cache; + int save_timeouts[2]= {vio->read_timeout, vio->write_timeout}; + bzero((char*) vio, sizeof(*vio)); + vio->type= type; + vio->sd= sd; + vio->hPipe= hPipe; + vio->localhost= localhost; + /* do not clear cache */ + vio->cache= vio->cache_pos= save_cache; + vio->cache_size= 0; + vio->read_timeout= save_timeouts[0]; + vio->write_timeout= save_timeouts[1]; +} + +void vio_timeout(Vio *vio, int type, uint timeval) +{ +#ifdef _WIN32 + uint timeout= timeval; /* milli secs */ +#else + struct timeval timeout; + timeout.tv_sec= timeval / 1000; + timeout.tv_usec= (timeval % 1000) * 1000; +#endif + + if (setsockopt(vio->sd, SOL_SOCKET, type, +#ifdef _WIN32 + (const char *)&timeout, +#else + (const void *)&timeout, +#endif + sizeof(timeout))) + { + DBUG_PRINT("error", ("setsockopt failed. Errno: %d", errno)); + } +} + +void vio_read_timeout(Vio *vio, uint timeout) +{ + vio->read_timeout= timeout * 1000; + vio_timeout(vio, SO_RCVTIMEO, vio->read_timeout); +} + +void vio_write_timeout(Vio *vio, uint timeout) +{ + vio->write_timeout= timeout * 1000; + vio_timeout(vio, SO_SNDTIMEO, vio->write_timeout); +} + +/* Open the socket or TCP/IP connection and read the fnctl() status */ + +Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost) +{ + Vio *vio; + DBUG_ENTER("vio_new"); + DBUG_PRINT("enter", ("sd=%d", sd)); + if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME)))) + { + vio_reset(vio, type, sd, 0, localhost); + sprintf(vio->desc, + (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"), + vio->sd); +#if !defined(__WIN32) && !defined(__EMX__) && !defined(OS2) +#if !defined(NO_FCNTL_NONBLOCK) + vio->fcntl_mode = fcntl(sd, F_GETFL); +#elif defined(HAVE_SYS_IOCTL_H) /* hpux */ + /* Non blocking sockets doesn't work good on HPUX 11.0 */ + (void) ioctl(sd,FIOSNBIO,0); +#endif +#else /* !defined(_WIN32) && !defined(__EMX__) */ + { + /* set to blocking mode by default */ + ulong arg=0, r; + r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg/*, sizeof(arg)*/); + } +#endif + } + if (!(vio->cache= my_malloc(VIO_CACHE_SIZE, MYF(MY_ZEROFILL)))) + { + my_free(vio); + vio= NULL; + } + vio->cache_size= 0; + vio->cache_pos= vio->cache; + DBUG_RETURN(vio); +} + + +#ifdef _WIN32 + +Vio *vio_new_win32pipe(HANDLE hPipe) +{ + Vio *vio; + DBUG_ENTER("vio_new_handle"); + if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_ZEROFILL)))) + { + vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE); + strmov(vio->desc, "named pipe"); + } + DBUG_RETURN(vio); +} + +#endif + +void vio_delete(Vio * vio) +{ + /* It must be safe to delete null pointers. */ + /* This matches the semantics of C++'s delete operator. */ + if (vio) + { + if (vio->type != VIO_CLOSED) + vio_close(vio); + my_free(vio->cache); + my_free(vio); + } +} + +int vio_errno(Vio *vio __attribute__((unused))) +{ + return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ +} + +int vio_wait_or_timeout(Vio *vio, my_bool is_read, int timeout) +{ + int rc; +#ifndef _WIN32 + struct pollfd p_fd; +#else + struct timeval tv= {0,0}; + fd_set fds, exc_fds; +#endif + + if (!timeout) + timeout= -1; + + /* we don't support it via named pipes yet. + * maybe this could be handled via PeekNamedPipe somehow !? */ + if (vio->type == VIO_TYPE_NAMEDPIPE) + return 1; + + /* + Note that if zero timeout, then we will not block, so we do not need to + yield to calling application in the async case. + */ + if (timeout != 0 && vio->async_context && vio->async_context->active) + { + rc= my_io_wait_async(vio->async_context, + (is_read) ? VIO_IO_EVENT_READ : VIO_IO_EVENT_WRITE, + timeout); + return(rc); + } + else + { +#ifndef _WIN32 + memset(&p_fd, 0, sizeof(p_fd)); + p_fd.fd= vio->sd; + p_fd.events= (is_read) ? POLLIN : POLLOUT; + + do { + rc= poll(&p_fd, 1, timeout); + } while (rc == -1 && errno == EINTR); + + if (rc == 0) + errno= ETIMEDOUT; +#else + FD_ZERO(&fds); + FD_ZERO(&exc_fds); + + FD_SET(vio->sd, &fds); + FD_SET(vio->sd, &exc_fds); + + if (timeout >= 0) + { + tv.tv_sec= timeout / 1000; + tv.tv_usec= (timeout % 1000) * 1000; + } + + rc= select(0, (is_read) ? &fds : NULL, + (is_read) ? NULL : &fds, + &exc_fds, + (timeout >= 0) ? &tv : NULL); + if (rc == SOCKET_ERROR) + errno= WSAGetLastError(); + if (rc == 0) + errno= ETIMEDOUT; +#endif + } + return rc; +} + + +size_t vio_real_read(Vio *vio, gptr buf, size_t size) +{ + size_t r; + + switch(vio->type) { +#ifdef HAVE_OPENSSL + case VIO_TYPE_SSL: + return my_ssl_read(vio, (char *)buf, size); + break; +#endif +#ifdef _WIN32 + case VIO_TYPE_NAMEDPIPE: + { + DWORD length= 0; + if (!ReadFile(vio->hPipe, buf, (DWORD)size, &length, NULL)) + return -1; + return length; + } + break; +#endif + default: + if (vio->async_context && vio->async_context->active) + r= my_recv_async(vio->async_context, + vio->sd, + buf, size, vio->read_timeout); + else + { + if (vio->async_context) + { + /* + If switching from non-blocking to blocking API usage, set the socket + back to blocking mode. + */ + my_bool old_mode; + vio_blocking(vio, TRUE, &old_mode); + } +#ifndef _WIN32 + if (vio_wait_or_timeout(vio, TRUE, vio->read_timeout) < 1) + { + return -1; + } + do { + r= recv(vio->sd, buf, size, 0); + } while (r == -1 && errno == EINTR); +#else + { + WSABUF wsaData; + DWORD dwBytes = 0; + DWORD flags = 0; + + wsaData.len= size; + wsaData.buf= buf; + + if (WSARecv(vio->sd, &wsaData, 1, &dwBytes, &flags, NULL, NULL) == SOCKET_ERROR) + { + errno= WSAGetLastError(); + return 0; + } + r= (size_t)dwBytes; + } +#endif + } + break; + } + return r; +} + + +size_t vio_read(Vio * vio, gptr buf, size_t size) +{ + size_t r; + DBUG_ENTER("vio_read"); + DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); + + if (!vio->cache) + DBUG_RETURN(vio_real_read(vio, buf, size)); + + if (vio->cache + vio->cache_size > vio->cache_pos) + { + r= MIN(size, (size_t)(vio->cache + vio->cache_size - vio->cache_pos)); + memcpy(buf, vio->cache_pos, r); + vio->cache_pos+= r; + } + else if (size >= VIO_CACHE_MIN_SIZE) + { + r= vio_real_read(vio, buf, size); + } + else + { + r= vio_real_read(vio, vio->cache, VIO_CACHE_SIZE); + if ((ssize_t)r > 0) + { + if (size < r) + { + vio->cache_size= r; /* might be < VIO_CACHE_SIZE */ + vio->cache_pos= vio->cache + size; + r= size; + } + memcpy(buf, vio->cache, r); + } + } + +#ifndef DBUG_OFF + if ((ssize_t)r == -1) + { + DBUG_PRINT("vio_error", ("Got error %d during read",socket_errno)); + } +#endif /* DBUG_OFF */ + DBUG_PRINT("exit", ("%u", (uint)r)); + DBUG_RETURN(r); +} + +/* + Return data from the beginning of the receive queue without removing + that data from the queue. A subsequent receive call will return the same data. +*/ +my_bool vio_read_peek(Vio *vio, size_t *bytes) +{ +#ifdef _WIN32 + if (ioctlsocket(vio->sd, FIONREAD, (unsigned long*)bytes)) + return TRUE; +#else + char buffer[1024]; + ssize_t length; + + vio_blocking(vio, 0, 0); + length= recv(vio->sd, &buffer, sizeof(buffer), MSG_PEEK); + if (length < 0) + return TRUE; + *bytes= length; +#endif + return FALSE; +} + + +size_t vio_write(Vio * vio, const gptr buf, size_t size) +{ + size_t r; + DBUG_ENTER("vio_write"); + DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); +#ifdef HAVE_OPENSSL + if (vio->type == VIO_TYPE_SSL) + { + r= my_ssl_write(vio, (uchar *)buf, size); + DBUG_RETURN(r); + } +#endif +#ifdef _WIN32 + if ( vio->type == VIO_TYPE_NAMEDPIPE) + { + DWORD length; + if (!WriteFile(vio->hPipe, (char*) buf, (DWORD)size, &length, NULL)) + DBUG_RETURN(-1); + DBUG_RETURN(length); + } +#endif + if (vio->async_context && vio->async_context->active) + r= my_send_async(vio->async_context, vio->sd, buf, size, + vio->write_timeout); + else + { + if (vio->async_context) + { + /* + If switching from non-blocking to blocking API usage, set the socket + back to blocking mode. + */ + my_bool old_mode; + vio_blocking(vio, TRUE, &old_mode); + } +#ifndef _WIN32 + do { + r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL); + } while (r == -1 && errno == EINTR); + + while (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) && + vio->write_timeout > 0) + { + if (vio_wait_or_timeout(vio, FALSE, vio->write_timeout) < 1) + return 0; + do { + r= send(vio->sd, buf, size, vio->write_timeout ? MSG_DONTWAIT : MSG_WAITALL); + } while (r == -1 && errno == EINTR); + } +#else + { + WSABUF wsaData; + DWORD dwBytes = 0; + + wsaData.len= size; + wsaData.buf= (char *)buf; + + if (WSASend(vio->sd, &wsaData, 1, &dwBytes, 0, NULL, NULL) == SOCKET_ERROR) + { + errno= WSAGetLastError(); + DBUG_RETURN(0); + } + r= (size_t)dwBytes; + } +#endif + } +#ifndef DBUG_OFF + if ((size_t)r == -1) + { + DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno)); + } +#endif /* DBUG_OFF */ + DBUG_PRINT("exit", ("%u", (uint)r)); + DBUG_RETURN(r); +} + + +int vio_blocking(Vio *vio, my_bool block, my_bool *previous_mode) +{ + int *sd_flags= &vio->fcntl_mode; + int save_flags= vio->fcntl_mode; + my_bool tmp; + my_socket sock= vio->sd; + + if (vio->type == VIO_TYPE_NAMEDPIPE) + return 0; + + if (!previous_mode) + previous_mode= &tmp; + +#ifdef _WIN32 + *previous_mode= (*sd_flags & O_NONBLOCK) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; + { + ulong arg= 1 - block; + if (ioctlsocket(sock, FIONBIO, (void *)&arg)) + { + vio->fcntl_mode= save_flags; + return(WSAGetLastError()); + } + } +#else +#if defined(O_NONBLOCK) + *previous_mode= (*sd_flags & O_NONBLOCK) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NONBLOCK : *sd_flags | O_NONBLOCK; +#elif defined(O_NDELAY) + *previous_mode= (*sd_flags & O_NODELAY) != 0; + *sd_flags = (block) ? *sd_flags & ~O_NODELAY : *sd_flags | O_NODELAY; +#elif defined(FNDELAY) + *previous_mode= (*sd_flags & O_FNDELAY) != 0; + *sd_flags = (block) ? *sd_flags & ~O_FNDELAY : *sd_flags | O_FNDELAY; +#else +#error socket blocking is not supported on this platform +#endif + if (fcntl(sock, F_SETFL, *sd_flags) == -1) + { + vio->fcntl_mode= save_flags; + return errno; + } +#endif + return 0; +} + +my_bool +vio_is_blocking(Vio * vio) +{ + my_bool r; + DBUG_ENTER("vio_is_blocking"); + r = !(vio->fcntl_mode & O_NONBLOCK); + DBUG_PRINT("exit", ("%d", (int) r)); + DBUG_RETURN(r); +} + + +int vio_fastsend(Vio * vio __attribute__((unused))) +{ + int r=0; + DBUG_ENTER("vio_fastsend"); + + { +#ifdef IPTOS_THROUGHPUT + int tos = IPTOS_THROUGHPUT; + if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos))) +#endif /* IPTOS_THROUGHPUT */ + { + int nodelay = 1; + if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay, + sizeof(nodelay))) { + DBUG_PRINT("warning", + ("Couldn't set socket option for fast send")); + r= -1; + } + } + } + DBUG_PRINT("exit", ("%d", r)); + DBUG_RETURN(r); +} + +int vio_keepalive(Vio* vio, my_bool set_keep_alive) +{ + int r=0; + uint opt = 0; + DBUG_ENTER("vio_keepalive"); + DBUG_PRINT("enter", ("sd=%d set_keep_alive=%d", vio->sd, (int) + set_keep_alive)); + if (vio->type != VIO_TYPE_NAMEDPIPE) + { + if (set_keep_alive) + opt = 1; + r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, + sizeof(opt)); + } + DBUG_RETURN(r); +} + + +my_bool +vio_should_retry(Vio * vio __attribute__((unused))) +{ + int en = socket_errno; + return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_EWOULDBLOCK; +} + + +int vio_close(Vio * vio) +{ + int r; + DBUG_ENTER("vio_close"); +#ifdef HAVE_OPENSSL + if (vio->type == VIO_TYPE_SSL) + { + r = my_ssl_close(vio); + } +#endif +#ifdef _WIN32 + if (vio->type == VIO_TYPE_NAMEDPIPE) + { + r=CloseHandle(vio->hPipe); + } + else if (vio->type != VIO_CLOSED) +#endif /* _WIN32 */ + { + r=0; + if (shutdown(vio->sd,2)) + r= -1; + if (closesocket(vio->sd)) + r= -1; + } + if (r) + { + DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno)); + /* FIXME: error handling (not critical for MySQL) */ + } + vio->type= VIO_CLOSED; + vio->sd= -1; + DBUG_RETURN(r); +} + + +const char *vio_description(Vio * vio) +{ + return vio->desc; +} + +enum enum_vio_type vio_type(Vio* vio) +{ + return vio->type; +} + +my_socket vio_fd(Vio* vio) +{ + return vio->sd; +} + + +my_bool vio_peer_addr(Vio * vio, char *buf) +{ + DBUG_ENTER("vio_peer_addr"); + DBUG_PRINT("enter", ("sd=%d", vio->sd)); + if (vio->localhost) + { + strmov(buf,"127.0.0.1"); + } + else + { + socklen_t addrLen = sizeof(struct sockaddr); + if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)), + &addrLen) != 0) + { + DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno)); + DBUG_RETURN(1); + } + my_inet_ntoa(vio->remote.sin_addr,buf); + } + DBUG_PRINT("exit", ("addr=%s", buf)); + DBUG_RETURN(0); +} + + +void vio_in_addr(Vio *vio, struct in_addr *in) +{ + DBUG_ENTER("vio_in_addr"); + if (vio->localhost) + bzero((char*) in, sizeof(*in)); /* This should never be executed */ + else + *in=vio->remote.sin_addr; + DBUG_VOID_RETURN; +} + + +/* Return 0 if there is data to be read */ +/* +my_bool vio_poll_read(Vio *vio,uint timeout) +{ +#ifndef HAVE_POLL + return 0; +#else + struct pollfd fds; + int res; + DBUG_ENTER("vio_poll"); + fds.fd=vio->sd; + fds.events=POLLIN; + fds.revents=0; + if ((res=poll(&fds,1,(int) timeout*1000)) <= 0) + { + DBUG_RETURN(res < 0 ? 0 : 1); + } + DBUG_RETURN(fds.revents & POLLIN ? 0 : 1); +#endif +} +*/ + +#endif /* HAVE_VIO */ -- cgit v1.2.3