From 3e76d54b98e328768b1999344c0affc7a8f04b69 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 27 Dec 2015 19:37:28 +0100 Subject: [PATCH 01/83] - Fix MDEV-9322. modified: storage/connect/json.cpp --- storage/connect/json.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index fc7edb84c0f..d99a021cdc9 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -594,7 +594,7 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty) if (fs) { fputs(EL, fs); fclose(fs); - str = (err) ? NULL : "Ok"; + str = (err) ? NULL : strcpy(g->Message, "Ok"); } else if (!err) { str = ((JOUTSTR*)jp)->Strp; jp->WriteChr('\0'); From ff2482026996d007689f41d522fb4d8289143a6b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 30 Dec 2015 19:39:31 +0100 Subject: [PATCH 02/83] Fix process handle leak in buildbot. GenerateConsoleCtrlEvent sent to non-existing process will add a process handle to this non-existing process to console host process conhost.exe --- mysql-test/lib/My/SafeProcess/safe_process_win.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess/safe_process_win.cc b/mysql-test/lib/My/SafeProcess/safe_process_win.cc index 87a14481e25..dca2faded71 100644 --- a/mysql-test/lib/My/SafeProcess/safe_process_win.cc +++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc @@ -357,14 +357,14 @@ int main(int argc, const char** argv ) CloseHandle(job_handle); message("Job terminated and closed"); - if (!jobobject_assigned) - { - GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, process_info.dwProcessId); - TerminateProcess(process_info.hProcess, 202); - } + if (wait_res != WAIT_OBJECT_0 + CHILD) { + if (!jobobject_assigned) + { + TerminateProcess(process_info.hProcess, 202); + } /* The child has not yet returned, wait for it */ message("waiting for child to exit"); if ((wait_res= WaitForSingleObject(wait_handles[CHILD], INFINITE)) From 4b4777ab63522b2bdd1f98ad03558a5b1da53dc0 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 12:48:55 +0200 Subject: [PATCH 03/83] Backported fix for ccache Fixed compiler warnings Added --big-test to tokudb change_column_char & change_column_bin --- BUILD/SETUP.sh | 15 +++++++++++++-- storage/connect/json.cpp | 2 +- storage/connect/jsonudf.cpp | 2 +- .../mysql-test/tokudb/t/change_column_bin.py | 1 + .../mysql-test/tokudb/t/change_column_bin.test | 1 + .../mysql-test/tokudb/t/change_column_char.py | 1 + .../mysql-test/tokudb/t/change_column_char.test | 1 + 7 files changed, 19 insertions(+), 4 deletions(-) diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index bde095b0aa8..858f42320b8 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -31,6 +31,7 @@ Usage: $0 [-h|-n] [configure-options] -h, --help Show this help message. -n, --just-print Don't actually run any commands; just print them. -c, --just-configure Stop after running configure. + Combined with --just-print shows configure options. --extra-configs=xxx Add this to configure options --extra-flags=xxx Add this C and CXX flags --extra-cflags=xxx Add this to C flags @@ -257,6 +258,10 @@ fi # (http://samba.org/ccache) is installed, use it. # We use 'grep' and hope 'grep' will work as expected # (returns 0 if finds lines) + +# As cmake doesn't like CC and CXX with a space, use symlinks from +# /usr/lib64/ccache if they exits. + if test "$USING_GCOV" != "1" then # Not using gcov; Safe to use ccache @@ -265,8 +270,14 @@ fi if ccache -V > /dev/null 2>&1 && test "$CCACHE_GCOV_VERSION_ENABLED" = "1" then - echo "$CC" | grep "ccache" > /dev/null || CC="ccache $CC" - echo "$CXX" | grep "ccache" > /dev/null || CXX="ccache $CXX" + if test -x /usr/lib64/ccache/gcc + then + CC=/usr/lib64/ccache/gcc + fi + if test -x /usr/lib64/ccache/g++ + then + CXX=/usr/lib64/ccache/g++ + fi fi # gcov diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index fc7edb84c0f..aa677342fac 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -594,7 +594,7 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty) if (fs) { fputs(EL, fs); fclose(fs); - str = (err) ? NULL : "Ok"; + str = (err) ? NULL : (char*) "Ok"; } else if (!err) { str = ((JOUTSTR*)jp)->Strp; jp->WriteChr('\0'); diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 40685ae0f0e..215562af46e 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -3365,7 +3365,7 @@ my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message) long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *res_length, char *is_null, char *error) { - char *p, res[256]; + char *p __attribute__((unused)), res[256]; long long n; unsigned long reslen; diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py index 0ddee301d1b..b94a80ec7be 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.py @@ -24,6 +24,7 @@ def gen_test(n): def main(): print "# this test is generated by change_bin.py" print "# test binary expansion is hot" + print "--source include/big_test.inc" print "--disable_warnings" print "DROP TABLE IF EXISTS t,ti;" print "--enable_warnings" diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test index 0365c155302..6053a263cf0 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_bin.test @@ -1,6 +1,7 @@ source include/have_tokudb.inc; # this test is generated by change_bin.py # test binary expansion is hot +--source include/big_test.inc --disable_warnings DROP TABLE IF EXISTS t,ti; --enable_warnings diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_char.py b/storage/tokudb/mysql-test/tokudb/t/change_column_char.py index 99d99d1c017..7481d9092f2 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_char.py +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_char.py @@ -24,6 +24,7 @@ def gen_test(n): def main(): print "# this test is generated by change_char.py" print "# test char expansion" + print "--source include/big_test.inc" print "--disable_warnings" print "DROP TABLE IF EXISTS t,ti;" print "--enable_warnings" diff --git a/storage/tokudb/mysql-test/tokudb/t/change_column_char.test b/storage/tokudb/mysql-test/tokudb/t/change_column_char.test index 8a0b0a9abd8..977420ad07e 100644 --- a/storage/tokudb/mysql-test/tokudb/t/change_column_char.test +++ b/storage/tokudb/mysql-test/tokudb/t/change_column_char.test @@ -1,6 +1,7 @@ source include/have_tokudb.inc; # this test is generated by change_char.py # test char expansion +--source include/big_test.inc --disable_warnings DROP TABLE IF EXISTS t,ti; --enable_warnings From 661a6d89065390ca1e9b4be05219b75f850ed290 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 13:20:07 +0200 Subject: [PATCH 04/83] Cleanup of slave code: - Added testing if connection is killed to shortcut reading of connection data This will allow us later in 10.2 to do a cleaner shutdown of slaves (less errors in the log) - Add new status variables: Slaves_connected, Slaves_running and Slave_connections. - Use MYSQL_SLAVE_NOT_RUN instead of 0 with slave_running. - Don't print obvious extra warnings to the error log when slave is shut down normally. --- sql/mysqld.cc | 35 +++++++++++++++++++++++++++++++++++ sql/net_serv.cc | 20 +++++++++++++++----- sql/rpl_mi.cc | 16 +++++++++------- sql/rpl_mi.h | 2 +- sql/rpl_rli.cc | 2 +- sql/slave.cc | 13 ++++++------- sql/sql_class.cc | 9 ++++++++- sql/sql_class.h | 1 + sql/sql_parse.cc | 1 + 9 files changed, 77 insertions(+), 22 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e3e331932d0..c6ac3f7a4a2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7299,6 +7299,38 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) } +/* How many slaves are connected to this master */ + +static int show_slaves_connected(THD *thd, SHOW_VAR *var, char *buff) +{ + + var->type= SHOW_LONGLONG; + var->value= buff; + mysql_mutex_lock(&LOCK_slave_list); + + *((longlong *)buff)= slave_list.records; + + mysql_mutex_unlock(&LOCK_slave_list); + return 0; +} + + +/* How many masters this slave is connected to */ + + +static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) +{ + var->type= SHOW_LONGLONG; + var->value= buff; + mysql_mutex_lock(&LOCK_active_mi); + + *((longlong *)buff)= master_info_index->any_slave_sql_running(); + + mysql_mutex_unlock(&LOCK_active_mi); + return 0; +} + + static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) { Master_info *mi= NULL; @@ -7950,6 +7982,9 @@ SHOW_VAR status_vars[]= { {"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS}, {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_INT}, #ifdef HAVE_REPLICATION + {"Slaves_connected", (char*) &show_slaves_connected, SHOW_SIMPLE_FUNC }, + {"Slaves_running", (char*) &show_slaves_running, SHOW_SIMPLE_FUNC }, + {"Slave_connections", (char*) offsetof(STATUS_VAR, com_register_slave), SHOW_LONG_STATUS}, {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC}, {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_SIMPLE_FUNC}, {"Slave_retried_transactions",(char*)&slave_retried_transactions, SHOW_LONG}, diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 0ce0fa93f99..91a17606d68 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -34,6 +34,7 @@ HFTODO this must be hidden if we don't want client capabilities in embedded library */ + #include #include #include @@ -107,13 +108,12 @@ extern void query_cache_insert(const char *packet, ulong length, unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A -#else -#define update_statistics(A) -#endif - -#ifdef MYSQL_SERVER +extern my_bool thd_net_is_killed(); /* Additional instrumentation hooks for the server */ #include "mysql_com_server.h" +#else +#define update_statistics(A) +#define thd_net_is_killed() 0 #endif #define TEST_BLOCKING 8 @@ -875,6 +875,16 @@ my_real_read(NET *net, size_t *complen, DBUG_PRINT("info",("vio_read returned %ld errno: %d", (long) length, vio_errno(net->vio))); + + if (i== 0 && thd_net_is_killed()) + { + len= packet_error; + net->error= 0; + net->last_errno= ER_CONNECTION_KILLED; + MYSQL_SERVER_my_error(net->last_errno, MYF(0)); + goto end; + } + #if !defined(__WIN__) && defined(MYSQL_SERVER) /* We got an error that there was no data on the socket. We now set up diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index ae6cc1ac0a2..a7ee3f3f45e 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -35,7 +35,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, rli(is_slave_recovery), port(MYSQL_PORT), checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF), connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0), - slave_running(0), slave_run_id(0), clock_diff_with_master(0), + slave_running(MYSQL_SLAVE_NOT_RUN), slave_run_id(0), + clock_diff_with_master(0), sync_counter(0), heartbeat_period(0), received_heartbeats(0), master_id(0), prev_master_id(0), using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0), @@ -1273,23 +1274,24 @@ bool Master_info_index::give_error_if_slave_running() The LOCK_active_mi must be held while calling this function. @return - TRUE If some slave SQL thread is running. - FALSE No slave SQL thread is running + 0 No Slave SQL thread is running + # Number of slave SQL thread running */ -bool Master_info_index::any_slave_sql_running() +uint Master_info_index::any_slave_sql_running() { + uint count= 0; DBUG_ENTER("any_slave_sql_running"); if (!this) // master_info_index is set to NULL on server shutdown - DBUG_RETURN(TRUE); + DBUG_RETURN(count); for (uint i= 0; i< master_info_hash.records; ++i) { Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i); if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) - DBUG_RETURN(TRUE); + count++; } - DBUG_RETURN(FALSE); + DBUG_RETURN(count); } diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 2b0b40feb3d..a27672e4c90 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -218,7 +218,7 @@ public: Master_info *get_master_info(LEX_STRING *connection_name, Sql_condition::enum_warning_level warning); bool give_error_if_slave_running(); - bool any_slave_sql_running(); + uint any_slave_sql_running(); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); }; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 8c7724d88a3..422c38209b5 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -63,7 +63,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), sql_driver_thd(), gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0), - slave_running(0), until_condition(UNTIL_NONE), + slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), executed_entries(0), m_flags(0) { diff --git a/sql/slave.cc b/sql/slave.cc index 3f8bc066378..6b2a88a2953 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -112,7 +112,7 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]= { { "Waiting to reconnect after a failed registration on master", - "Slave I/O thread killed while waitnig to reconnect after a failed \ + "Slave I/O thread killed while waiting to reconnect after a failed \ registration on master", "Reconnecting after a failed registration on master", "failed registering on master, reconnecting to try again, \ @@ -4040,10 +4040,9 @@ connected: if (request_dump(thd, mysql, mi, &suppress_warnings)) { sql_print_error("Failed on request_dump()"); - if (check_io_slave_killed(mi, "Slave I/O thread killed while \ -requesting master dump") || - try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, - reconnect_messages[SLAVE_RECON_ACT_DUMP])) + if (check_io_slave_killed(mi, NullS) || + try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings, + reconnect_messages[SLAVE_RECON_ACT_DUMP])) goto err; goto connected; } @@ -4059,6 +4058,7 @@ requesting master dump") || }); const char *event_buf; + mi->slave_running= MYSQL_SLAVE_RUN_READING; DBUG_ASSERT(mi->last_error().number == 0); while (!io_slave_killed(mi)) { @@ -4071,8 +4071,7 @@ requesting master dump") || */ THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event); event_len= read_event(mysql, mi, &suppress_warnings); - if (check_io_slave_killed(mi, "Slave I/O thread killed while \ -reading event")) + if (check_io_slave_killed(mi, NullS)) goto err; DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT", if (!retry_count_event) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 7a9cdad84f6..3d0d99bcfa2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1743,7 +1743,8 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, void THD::awake(killed_state state_to_set) { DBUG_ENTER("THD::awake"); - DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd)); + DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d", + this, current_thd, (int) state_to_set)); THD_CHECK_SENTRY(this); mysql_mutex_assert_owner(&LOCK_thd_data); @@ -3799,6 +3800,12 @@ void thd_increment_bytes_sent(ulong length) } } +my_bool thd_net_is_killed() +{ + THD *thd= current_thd; + return thd && thd->killed ? 1 : 0; +} + void thd_increment_bytes_received(ulong length) { diff --git a/sql/sql_class.h b/sql/sql_class.h index a94236aec0b..8264921cabd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -656,6 +656,7 @@ typedef struct system_status_var { ulong com_other; ulong com_stat[(uint) SQLCOM_END]; + ulong com_register_slave; ulong created_tmp_disk_tables_; ulong created_tmp_tables_; ulong ha_commit_count; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 23629ff3ec4..8373ddabe3c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1177,6 +1177,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifdef HAVE_REPLICATION case COM_REGISTER_SLAVE: { + status_var_increment(thd->status_var.com_register_slave); if (!register_slave(thd, (uchar*)packet, packet_length)) my_ok(thd); break; From 8fcc0bfefadcb4e9f7acb13d11661daeea5097f9 Mon Sep 17 00:00:00 2001 From: Monty Date: Sun, 3 Jan 2016 13:27:59 +0200 Subject: [PATCH 05/83] Fixed bug in semi_sync replication tests. The problem was that wait_for_slave_io_to_start reported that the io thread was ready, when it was still initializing. This caused test suite to continue too early, for example before the semi sync plugin was properly enabled. Fixed by introducing a new internal stage: "Preparing". Slave_IO_Running is now set to "Yes" only when all initializing is done and the IO thread is ready to read things from the master. The only test affected by this change is rpl_flsh_tbls, which got stuck in the preparing phase while trying to read the GTID position from a table. Fixed by having this test waiting for Preparing instead of Yes. --- mysql-test/extra/rpl_tests/rpl_flsh_tbls.test | 8 +++++++- mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result | 3 ++- .../suite/rpl/r/rpl_semi_sync_uninstall_plugin.result | 8 ++++++++ mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result | 3 ++- .../suite/rpl/t/rpl_semi_sync_uninstall_plugin.test | 6 ++++++ sql/mysqld.cc | 4 ++-- sql/rpl_mi.cc | 2 +- sql/slave.cc | 8 ++++---- sql/slave.h | 9 +++++---- 9 files changed, 37 insertions(+), 14 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test index 5cbda2d591f..a8cec3d2a1a 100644 --- a/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test +++ b/mysql-test/extra/rpl_tests/rpl_flsh_tbls.test @@ -45,7 +45,13 @@ drop table t1; connection slave; flush tables with read lock; start slave; -source include/wait_for_slave_to_start.inc; + +# The IO thread will not be able to read the GTID because of flush tables +let $slave_param= Slave_IO_Running; +let $slave_param_value= Preparing; +source include/wait_for_slave_param.inc; + +--source include/wait_for_slave_sql_to_start.inc --error 1192 stop slave; diff --git a/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result b/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result index 6aca4282ccd..27b23739831 100644 --- a/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result +++ b/mysql-test/suite/rpl/r/rpl_row_flsh_tbls.result @@ -22,7 +22,8 @@ include/wait_for_slave_to_stop.inc drop table t1; flush tables with read lock; start slave; -include/wait_for_slave_to_start.inc +include/wait_for_slave_param.inc [Slave_IO_Running] +include/wait_for_slave_sql_to_start.inc stop slave; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result b/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result index 0809af5f943..36d862dedce 100644 --- a/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result +++ b/mysql-test/suite/rpl/r/rpl_semi_sync_uninstall_plugin.result @@ -14,6 +14,10 @@ DROP TABLE t1; [connection slave] include/install_semisync.inc [connection slave] +show global status like "Slave%_running"; +Variable_name Value +Slave_running ON +Slaves_running 1 UNINSTALL PLUGIN rpl_semi_sync_slave; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown @@ -21,6 +25,10 @@ select plugin_name,plugin_status from information_schema.plugins where plugin_na plugin_name plugin_status rpl_semi_sync_slave DELETED [connection master] +show global status like "Slave%_connect%"; +Variable_name Value +Slave_connections 2 +Slaves_connected 1 UNINSTALL PLUGIN rpl_semi_sync_master; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown diff --git a/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result b/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result index 6aca4282ccd..27b23739831 100644 --- a/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result +++ b/mysql-test/suite/rpl/r/rpl_stm_flsh_tbls.result @@ -22,7 +22,8 @@ include/wait_for_slave_to_stop.inc drop table t1; flush tables with read lock; start slave; -include/wait_for_slave_to_start.inc +include/wait_for_slave_param.inc [Slave_IO_Running] +include/wait_for_slave_sql_to_start.inc stop slave; ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction unlock tables; diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test b/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test index 4ee345e54ba..0f86789cced 100644 --- a/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test +++ b/mysql-test/suite/rpl/t/rpl_semi_sync_uninstall_plugin.test @@ -57,6 +57,8 @@ DROP TABLE t1; # possible at this state --connection slave --echo [connection slave] +show global status like "Slave%_running"; + UNINSTALL PLUGIN rpl_semi_sync_slave; select plugin_name,plugin_status from information_schema.plugins where plugin_name like 'rpl_%'; @@ -64,6 +66,10 @@ select plugin_name,plugin_status from information_schema.plugins where plugin_na # possible at this state --connection master --echo [connection master] + +# The following is to catch errors if the next uninstall plugin would succeed +show global status like "Slave%_connect%"; + UNINSTALL PLUGIN rpl_semi_sync_master; select plugin_name,plugin_status from information_schema.plugins where plugin_name like 'rpl_%'; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c6ac3f7a4a2..416193ff1e0 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7287,8 +7287,8 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) get_master_info(&thd->variables.default_master_connection, Sql_condition::WARN_LEVEL_NOTE); if (mi) - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT && - mi->rli.slave_running); + tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); } mysql_mutex_unlock(&LOCK_active_mi); if (mi) diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index a7ee3f3f45e..9c6f4639717 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -1321,7 +1321,7 @@ bool Master_info_index::start_all_slaves(THD *thd) Try to start all slaves that are configured (host is defined) and are not already running */ - if ((mi->slave_running != MYSQL_SLAVE_RUN_CONNECT || + if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN || !mi->rli.slave_running) && *mi->host) { if ((error= start_slave(thd, mi, 1))) diff --git a/sql/slave.cc b/sql/slave.cc index 6b2a88a2953..7474aec38ca 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2615,6 +2615,8 @@ static bool send_show_master_info_header(THD *thd, bool full, DBUG_RETURN(FALSE); } +/* Text for Slave_IO_Running */ +static const char *slave_running[]= { "No", "Connecting", "Preparing", "Yes" }; static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, String *gtid_pos) @@ -2668,9 +2670,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, &my_charset_bin); protocol->store((ulonglong) mi->rli.group_relay_log_pos); protocol->store(mi->rli.group_master_log_name, &my_charset_bin); - protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ? - "Yes" : (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT ? - "Connecting" : "No"), &my_charset_bin); + protocol->store(slave_running[mi->slave_running], &my_charset_bin); protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin); protocol->store(rpl_filter->get_do_db()); protocol->store(rpl_filter->get_ignore_db()); @@ -2713,7 +2713,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, Seconds_Behind_Master: if SQL thread is running and I/O thread is connected, we can compute it otherwise show NULL (i.e. unknown). */ - if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) && + if ((mi->slave_running == MYSQL_SLAVE_RUN_READING) && mi->rli.slave_running) { long time_diff; diff --git a/sql/slave.h b/sql/slave.h index e16f801b577..5cc02c8a10b 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -132,11 +132,11 @@ extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; /* - 3 possible values for Master_info::slave_running and + 4 possible values for Master_info::slave_running and Relay_log_info::slave_running. - The values 0,1,2 are very important: to keep the diff small, I didn't - substitute places where we use 0/1 with the newly defined symbols. So don't change - these values. + The values 0,1,2,3 are very important: to keep the diff small, I didn't + substitute places where we use 0/1 with the newly defined symbols. + So don't change these values. The same way, code is assuming that in Relay_log_info we use only values 0/1. I started with using an enum, but @@ -145,6 +145,7 @@ extern ulonglong relay_log_space_limit; #define MYSQL_SLAVE_NOT_RUN 0 #define MYSQL_SLAVE_RUN_NOT_CONNECT 1 #define MYSQL_SLAVE_RUN_CONNECT 2 +#define MYSQL_SLAVE_RUN_READING 3 #define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\ "FIRST") From 111acb721ff7237ceb5f5c8fae8c10857ed7c36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Tue, 5 Jan 2016 18:50:54 +0200 Subject: [PATCH 06/83] MDEV-9359: encryption.create_or_replace fails sporadically in buildbot: failing assertion: mutex->magic_n == MUTEX_MAGIC_N Make sure that encryption threads mutex is initialized before starting encryption threads. --- storage/innobase/fil/fil0crypt.cc | 24 ++++++++++++++++-------- storage/innobase/srv/srv0start.cc | 2 ++ storage/innobase/sync/sync0sync.cc | 8 +++++++- storage/xtradb/fil/fil0crypt.cc | 24 ++++++++++++++++-------- storage/xtradb/srv/srv0start.cc | 2 ++ storage/xtradb/sync/sync0sync.cc | 8 +++++++- 6 files changed, 50 insertions(+), 18 deletions(-) diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 100f304bf6f..3d9d6a18122 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -2294,6 +2294,10 @@ fil_crypt_set_thread_cnt( /*=====================*/ uint new_cnt) /*!< in: New key rotation thread count */ { + if (!fil_crypt_threads_inited) { + fil_crypt_threads_init(); + } + if (new_cnt > srv_n_fil_crypt_threads) { uint add = new_cnt - srv_n_fil_crypt_threads; srv_n_fil_crypt_threads = new_cnt; @@ -2358,15 +2362,18 @@ void fil_crypt_threads_init() /*====================*/ { - fil_crypt_event = os_event_create(); - fil_crypt_threads_event = os_event_create(); - mutex_create(fil_crypt_threads_mutex_key, - &fil_crypt_threads_mutex, SYNC_NO_ORDER_CHECK); + ut_ad(mutex_own(&fil_system->mutex)); + if (!fil_crypt_threads_inited) { + fil_crypt_event = os_event_create(); + fil_crypt_threads_event = os_event_create(); + mutex_create(fil_crypt_threads_mutex_key, + &fil_crypt_threads_mutex, SYNC_NO_ORDER_CHECK); - uint cnt = srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = 0; - fil_crypt_set_thread_cnt(cnt); - fil_crypt_threads_inited = true; + uint cnt = srv_n_fil_crypt_threads; + srv_n_fil_crypt_threads = 0; + fil_crypt_threads_inited = true; + fil_crypt_set_thread_cnt(cnt); + } } /********************************************************************* @@ -2389,6 +2396,7 @@ fil_crypt_threads_cleanup() { os_event_free(fil_crypt_event); os_event_free(fil_crypt_threads_event); + fil_crypt_threads_inited = false; } /********************************************************************* diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 8c54d6eb083..d7b37b5bc1e 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -2984,7 +2984,9 @@ files_checked: fts_optimize_init(); /* Create thread(s) that handles key rotation */ + fil_system_enter(); fil_crypt_threads_init(); + fil_system_exit(); /* Create the log scrub thread */ if (srv_scrub_log) diff --git a/storage/innobase/sync/sync0sync.cc b/storage/innobase/sync/sync0sync.cc index 2e1737da3ec..7eb3d0bd6f2 100644 --- a/storage/innobase/sync/sync0sync.cc +++ b/storage/innobase/sync/sync0sync.cc @@ -422,7 +422,13 @@ mutex_validate( const ib_mutex_t* mutex) /*!< in: mutex */ { ut_a(mutex); - ut_a(mutex->magic_n == MUTEX_MAGIC_N); + + if (mutex->magic_n != MUTEX_MAGIC_N) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Mutex %p not initialized file %s line %lu.", + mutex, mutex->cfile_name, mutex->cline); + } + ut_ad(mutex->magic_n == MUTEX_MAGIC_N); return(TRUE); } diff --git a/storage/xtradb/fil/fil0crypt.cc b/storage/xtradb/fil/fil0crypt.cc index 100f304bf6f..3d9d6a18122 100644 --- a/storage/xtradb/fil/fil0crypt.cc +++ b/storage/xtradb/fil/fil0crypt.cc @@ -2294,6 +2294,10 @@ fil_crypt_set_thread_cnt( /*=====================*/ uint new_cnt) /*!< in: New key rotation thread count */ { + if (!fil_crypt_threads_inited) { + fil_crypt_threads_init(); + } + if (new_cnt > srv_n_fil_crypt_threads) { uint add = new_cnt - srv_n_fil_crypt_threads; srv_n_fil_crypt_threads = new_cnt; @@ -2358,15 +2362,18 @@ void fil_crypt_threads_init() /*====================*/ { - fil_crypt_event = os_event_create(); - fil_crypt_threads_event = os_event_create(); - mutex_create(fil_crypt_threads_mutex_key, - &fil_crypt_threads_mutex, SYNC_NO_ORDER_CHECK); + ut_ad(mutex_own(&fil_system->mutex)); + if (!fil_crypt_threads_inited) { + fil_crypt_event = os_event_create(); + fil_crypt_threads_event = os_event_create(); + mutex_create(fil_crypt_threads_mutex_key, + &fil_crypt_threads_mutex, SYNC_NO_ORDER_CHECK); - uint cnt = srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = 0; - fil_crypt_set_thread_cnt(cnt); - fil_crypt_threads_inited = true; + uint cnt = srv_n_fil_crypt_threads; + srv_n_fil_crypt_threads = 0; + fil_crypt_threads_inited = true; + fil_crypt_set_thread_cnt(cnt); + } } /********************************************************************* @@ -2389,6 +2396,7 @@ fil_crypt_threads_cleanup() { os_event_free(fil_crypt_event); os_event_free(fil_crypt_threads_event); + fil_crypt_threads_inited = false; } /********************************************************************* diff --git a/storage/xtradb/srv/srv0start.cc b/storage/xtradb/srv/srv0start.cc index 6e45725098c..e0f9bde63fa 100644 --- a/storage/xtradb/srv/srv0start.cc +++ b/storage/xtradb/srv/srv0start.cc @@ -3076,7 +3076,9 @@ files_checked: fts_optimize_init(); /* Create thread(s) that handles key rotation */ + fil_system_enter(); fil_crypt_threads_init(); + fil_system_exit(); /* Create the log scrub thread */ if (srv_scrub_log) diff --git a/storage/xtradb/sync/sync0sync.cc b/storage/xtradb/sync/sync0sync.cc index 14b332a2185..3facb99ac0a 100644 --- a/storage/xtradb/sync/sync0sync.cc +++ b/storage/xtradb/sync/sync0sync.cc @@ -482,7 +482,13 @@ mutex_validate( const ib_mutex_t* mutex) /*!< in: mutex */ { ut_a(mutex); - ut_a(mutex->magic_n == MUTEX_MAGIC_N); + + if (mutex->magic_n != MUTEX_MAGIC_N) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Mutex %p not initialized file %s line %lu.", + mutex, mutex->cfile_name, mutex->cline); + } + ut_ad(mutex->magic_n == MUTEX_MAGIC_N); return(TRUE); } From 5f48b61509e4bc0fcb4e679abe51a6fc2dbb6359 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 7 Jan 2016 14:45:40 +0100 Subject: [PATCH 07/83] MDEV-9298 : Build failure when linking libmysql. If GCC or CLang compile with link time optimization (-flto), they throw an error during link , when lto sees a function (e.g mysql_real_connect) is redeclared as "external void *" in libmysql_exports.cc The fix disables -flto for generated libmysql_exports.cc --- cmake/libutils.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index cca0032962d..41b7d9d1d86 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -87,6 +87,11 @@ MACRO(CREATE_EXPORT_FILE VAR TARGET API_FUNCTIONS) ENDFOREACH() SET(CONTENT "${CONTENT} (void *)0\n}\;") CONFIGURE_FILE_CONTENT(${CONTENT} ${EXPORTS}) + # Avoid "function redeclared as variable" error + # when using gcc/clang option -flto(link time optimization) + IF(" ${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} " MATCHES " -flto") + SET_SOURCE_FILES_PROPERTIES(${EXPORTS} PROPERTIES COMPILE_FLAGS "-fno-lto") + ENDIF() SET(${VAR} ${EXPORTS}) ENDIF() ENDMACRO() From 1236333333c351b93593b12c0e20b06313e4ac29 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 24 Dec 2015 21:46:38 +0100 Subject: [PATCH 08/83] Fix annoying repetitive tokudb build warning, if MariaDB is build on non-Linux x64 system --- storage/tokudb/CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/storage/tokudb/CMakeLists.txt b/storage/tokudb/CMakeLists.txt index d1f43d71f57..1b256ff100b 100644 --- a/storage/tokudb/CMakeLists.txt +++ b/storage/tokudb/CMakeLists.txt @@ -9,10 +9,9 @@ struct a d = { .b=1, .c=2 }; int main() { return 0; } " TOKUDB_OK) ELSE() - IF(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - MESSAGE(WARNING "Not Intel 64 bit CPU") - ELSE() - MESSAGE(WARNING "Too old CMAKE VERSION. 2.8.9 or higher is required by TokuDB") + IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND + CMAKE_VERSION VERSION_LESS "2.8.9") + MESSAGE(STATUS "CMake 2.8.9 or higher is required by TokuDB") ENDIF() ENDIF() From 3730d8a225fe33a0157e0aaa85121ef7cecf288d Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 5 Jan 2016 22:48:50 +0100 Subject: [PATCH 09/83] MDEV-9366 : do_shutdown_server fails to detect server shutdown on Windows. Fix test whether process is alive in mysqltest. Also fix SHUT_RD definition on Windows to be SD_RECEIVE. SD_BOTH was used instead prior to this patch, and this would occasionally make mysql_shutdown() fail - when the socket for the current connection is not able send the COM_SHUTDOWN response anymore. --- client/mysqltest.cc | 5 +++-- include/violite.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index c601fb57f2f..026934a2feb 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5121,12 +5121,13 @@ static int my_kill(int pid, int sig) { #ifdef __WIN__ HANDLE proc; - if ((proc= OpenProcess(PROCESS_TERMINATE, FALSE, pid)) == NULL) + if ((proc= OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid)) == NULL) return -1; if (sig == 0) { + DWORD wait_result= WaitForSingleObject(proc, 0); CloseHandle(proc); - return 0; + return wait_result == WAIT_OBJECT_0?-1:0; } (void)TerminateProcess(proc, 201); CloseHandle(proc); diff --git a/include/violite.h b/include/violite.h index d5ce66c3061..a7165ca91a9 100644 --- a/include/violite.h +++ b/include/violite.h @@ -208,7 +208,7 @@ void vio_end(void); /* shutdown(2) flags */ #ifndef SHUT_RD -#define SHUT_RD SD_BOTH +#define SHUT_RD SD_RECEIVE #endif /* From 3e20a0d8f414124d040fda5a4981cecf533f62a2 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sat, 9 Jan 2016 19:51:51 +0100 Subject: [PATCH 10/83] - Fix MDEV-9239. Meanwhile, make all references to the database in XTAB Schema (was sometimes in XTAB Catalog) modified: storage/connect/mycat.cc modified: storage/connect/mycat.h modified: storage/connect/reldef.cpp modified: storage/connect/reldef.h modified: storage/connect/tabmysql.cpp modified: storage/connect/tabpivot.cpp modified: storage/connect/tabtbl.cpp modified: storage/connect/tabutil.cpp --- storage/connect/mycat.cc | 31 +++++++++++++++++-------------- storage/connect/mycat.h | 10 ++++++---- storage/connect/reldef.cpp | 13 ++++++++----- storage/connect/reldef.h | 12 +++++++----- storage/connect/tabmysql.cpp | 2 +- storage/connect/tabpivot.cpp | 4 ++-- storage/connect/tabtbl.cpp | 6 +++--- storage/connect/tabutil.cpp | 25 +++++++++++++++++-------- 8 files changed, 61 insertions(+), 42 deletions(-) diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index 2e9085b4c87..97ad980dd6a 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -1,4 +1,4 @@ -/* Copyright (C) Olivier Bertrand 2004 - 2015 +/* Copyright (C) Olivier Bertrand 2004 - 2016 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ /* ------------- */ /* Version 1.4 */ /* */ -/* Author: Olivier Bertrand 2012 - 2015 */ +/* Author: Olivier Bertrand 2012 - 2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -509,30 +509,33 @@ void MYCAT::SetPath(PGLOBAL g, LPCSTR *datapath, const char *path) /* GetTableDesc: retrieve a table descriptor. */ /* Look for a table descriptor matching the name and type. */ /***********************************************************************/ -PRELDEF MYCAT::GetTableDesc(PGLOBAL g, LPCSTR name, +PRELDEF MYCAT::GetTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR type, PRELDEF *) { if (trace) - printf("GetTableDesc: name=%s am=%s\n", name, SVP(type)); + printf("GetTableDesc: name=%s am=%s\n", tablep->GetName(), SVP(type)); // If not specified get the type of this table if (!type) type= Hc->GetStringOption("Type","*"); - return MakeTableDesc(g, name, type); + return MakeTableDesc(g, tablep, type); } // end of GetTableDesc /***********************************************************************/ /* MakeTableDesc: make a table/view description. */ /* Note: caller must check if name already exists before calling it. */ /***********************************************************************/ -PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) +PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am) { TABTYPE tc; + LPCSTR name = (PSZ)PlugDup(g, tablep->GetName()); + LPCSTR schema = (PSZ)PlugDup(g, tablep->GetSchema()); PRELDEF tdp= NULL; if (trace) - printf("MakeTableDesc: name=%s am=%s\n", name, SVP(am)); + printf("MakeTableDesc: name=%s schema=%s am=%s\n", + name, SVP(schema), SVP(am)); /*********************************************************************/ /* Get a unique enum identifier for types. */ @@ -571,11 +574,11 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am) case TAB_VIR: tdp= new(g) VIRDEF; break; case TAB_JSON: tdp= new(g) JSONDEF; break; default: - sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name); + sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name); } // endswitch // Do make the table/view definition - if (tdp && tdp->Define(g, this, name, am)) + if (tdp && tdp->Define(g, this, name, schema, am)) tdp= NULL; return tdp; @@ -588,20 +591,20 @@ PTDB MYCAT::GetTable(PGLOBAL g, PTABLE tablep, MODE mode, LPCSTR type) { PRELDEF tdp; PTDB tdbp= NULL; - LPCSTR name= tablep->GetName(); +// LPCSTR name= tablep->GetName(); if (trace) - printf("GetTableDB: name=%s\n", name); + printf("GetTableDB: name=%s\n", tablep->GetName()); // Look for the description of the requested table - tdp= GetTableDesc(g, name, type); + tdp= GetTableDesc(g, tablep, type); if (tdp) { if (trace) printf("tdb=%p type=%s\n", tdp, tdp->GetType()); - if (tablep->GetQualifier()) - tdp->Database = SetPath(g, tablep->GetQualifier()); + if (tablep->GetSchema()) + tdp->Database = SetPath(g, tablep->GetSchema()); tdbp= tdp->GetTable(g, mode); } // endif tdp diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h index dfcbb2f6766..05163f08f1b 100644 --- a/storage/connect/mycat.h +++ b/storage/connect/mycat.h @@ -100,15 +100,17 @@ class MYCAT : public CATALOG { //void SetDataPath(PGLOBAL g, const char *path) // {SetPath(g, &DataPath, path);} bool StoreIndex(PGLOBAL, PTABDEF) {return false;} // Temporary - PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, - LPCSTR type, PRELDEF *prp = NULL); +// PRELDEF GetTableDesc(PGLOBAL g, LPCSTR name, + PRELDEF GetTableDesc(PGLOBAL g, PTABLE tablep, + LPCSTR type, PRELDEF *prp = NULL); PTDB GetTable(PGLOBAL g, PTABLE tablep, MODE mode = MODE_READ, LPCSTR type = NULL); void ClearDB(PGLOBAL g); protected: - PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am); -//void SetPath(PGLOBAL g, LPCSTR *datapath, const char *path); +// PRELDEF MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am); + PRELDEF MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am); + //void SetPath(PGLOBAL g, LPCSTR *datapath, const char *path); // Members ha_connect *Hc; // The Connect handler diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index 8f9328c0b2f..e455bc8f1a5 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -1,11 +1,11 @@ /************* RelDef CPP Program Source Code File (.CPP) **************/ /* PROGRAM NAME: RELDEF */ /* ------------- */ -/* Version 1.4 */ +/* Version 1.5 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2004-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -37,6 +37,7 @@ #include "plgdbsem.h" #include "reldef.h" #include "colblk.h" +#include "tabcol.h" #include "filamap.h" #include "filamfix.h" #include "filamvct.h" @@ -217,11 +218,13 @@ TABDEF::TABDEF(void) /***********************************************************************/ /* Define: initialize the table definition block from XDB file. */ /***********************************************************************/ -bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) +bool TABDEF::Define(PGLOBAL g, PCATLG cat, + LPCSTR name, LPCSTR schema, LPCSTR am) { int poff = 0; - Name = (PSZ)PlugDup(g, name); + Name = (PSZ)name; + Schema = (PSZ)schema; Cat = cat; Hc = ((MYCAT*)cat)->GetHandler(); Catfunc = GetFuncID(GetStringCatInfo(g, "Catfunc", NULL)); @@ -569,7 +572,7 @@ PTABDEF OEMDEF::GetXdef(PGLOBAL g) } // endif Cbuf // Here "OEM" should be replace by a more useful value - if (xdefp->Define(g, cat, Name, "OEM")) + if (xdefp->Define(g, cat, Name, Schema, "OEM")) return NULL; // Ok, return external block diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h index dada5716dbe..bc1bd2ddd74 100644 --- a/storage/connect/reldef.h +++ b/storage/connect/reldef.h @@ -1,7 +1,7 @@ /*************** RelDef H Declares Source Code File (.H) ***************/ -/* Name: RELDEF.H Version 1.5 */ +/* Name: RELDEF.H Version 1.6 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2004-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2004-2016 */ /* */ /* This file contains the DEF classes definitions. */ /***********************************************************************/ @@ -50,7 +50,8 @@ class DllExport RELDEF : public BLOCK { // Relation definition block int GetCharCatInfo(PSZ what, PSZ sdef, char *buf, int size); char *GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef); virtual int Indexable(void) {return 0;} - virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am) = 0; + virtual bool Define(PGLOBAL g, PCATLG cat, + LPCSTR name, LPCSTR schema, LPCSTR am) = 0; virtual PTDB GetTable(PGLOBAL g, MODE mode) = 0; protected: @@ -97,8 +98,9 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ int GetColCatInfo(PGLOBAL g); void SetIndexInfo(void); bool DropTable(PGLOBAL g, PSZ name); - virtual bool Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am); - virtual bool DefineAM(PGLOBAL, LPCSTR, int) = 0; + virtual bool Define(PGLOBAL g, PCATLG cat, + LPCSTR name, LPCSTR schema, LPCSTR am); + virtual bool DefineAM(PGLOBAL, LPCSTR, int) = 0; protected: // Members diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index 658f3513b07..b9cede52a21 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -334,7 +334,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) Delayed = !!GetIntCatInfo("Delayed", 0); } else { // MYSQL access from a PROXY table - Database = GetStringCatInfo(g, "Database", "*"); + Database = GetStringCatInfo(g, "Database", Schema ? Schema : "*"); Isview = GetBoolCatInfo("View", false); // We must get other connection parms from the calling table diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp index b628e26d3c7..256b454741c 100644 --- a/storage/connect/tabpivot.cpp +++ b/storage/connect/tabpivot.cpp @@ -348,7 +348,7 @@ bool PIVOTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) return TRUE; Tabname = (char*)Tablep->GetName(); - DB = (char*)Tablep->GetQualifier(); + DB = (char*)Tablep->GetSchema(); Tabsrc = (char*)Tablep->GetSrc(); Host = GetStringCatInfo(g, "Host", "localhost"); @@ -529,7 +529,7 @@ bool TDBPIVOT::GetSourceTable(PGLOBAL g) // Get the new table description block of this source table PTABLE tablep = new(g) XTAB("whatever", Tabsrc); - tablep->SetQualifier(Database); + tablep->SetSchema(Database); if (!(Tdbp = GetSubTable(g, tablep, true))) return true; diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp index 7f979eaf4be..6b72c715517 100644 --- a/storage/connect/tabtbl.cpp +++ b/storage/connect/tabtbl.cpp @@ -5,7 +5,7 @@ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to PlugDB Software Development 2008-2015 */ +/* (C) Copyright to PlugDB Software Development 2008-2016 */ /* Author: Olivier BERTRAND */ /* */ /* WHAT THIS PROGRAM DOES: */ @@ -130,10 +130,10 @@ bool TBLDEF::DefineAM(PGLOBAL g, LPCSTR, int) // Allocate the TBLIST block for that table tbl = new(g) XTAB(pn, def); - tbl->SetQualifier(pdb); + tbl->SetSchema(pdb); if (trace) - htrc("TBL: Name=%s db=%s\n", tbl->GetName(), tbl->GetQualifier()); + htrc("TBL: Name=%s db=%s\n", tbl->GetName(), tbl->GetSchema()); // Link the blocks if (Tablep) diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp index 331a7f45d4d..4069cdbed2a 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -1,7 +1,7 @@ /************* Tabutil cpp Declares Source Code File (.CPP) ************/ /* Name: TABUTIL.CPP Version 1.1 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2013 - 2016 */ /* */ /* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */ /***********************************************************************/ @@ -118,7 +118,7 @@ PQRYRES TabColumns(PGLOBAL g, THD *thd, const char *db, FLD_LENGTH, FLD_SCALE, FLD_RADIX, FLD_NULL, FLD_REM, FLD_NO, FLD_CHARSET}; unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 0, 32, 32}; - char *fld, *colname, *chset, *fmt, v; + char *pn, *tn, *fld, *colname, *chset, *fmt, v; int i, n, ncol = sizeof(buftyp) / sizeof(int); int prec, len, type, scale; int zconv = GetConvSize(); @@ -130,7 +130,16 @@ PQRYRES TabColumns(PGLOBAL g, THD *thd, const char *db, PCOLRES crp; if (!info) { - if (!(s = GetTableShare(g, thd, db, name, mysql))) { + // Analyze the table name, it may have the format: [dbname.]tabname + if (strchr((char*)name, '.')) { + tn = (char*)PlugDup(g, name); + pn = strchr(tn, '.'); + *pn++ = 0; + db = tn; + name = pn; + } // endif pn + + if (!(s = GetTableShare(g, thd, db, name, mysql))) { return NULL; } else if (s->is_view) { strcpy(g->Message, "Use MYSQL type to see columns from a view"); @@ -315,7 +324,7 @@ bool PRXDEF::DefineAM(PGLOBAL g, LPCSTR, int) } // endif pn Tablep = new(g) XTAB(tab, def); - Tablep->SetQualifier(db); + Tablep->SetSchema(db); return false; } // end of DefineAM @@ -379,12 +388,12 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) LPCSTR cdb, curdb = hc->GetDBName(NULL); THD *thd = (hc->GetTable())->in_use; - db = (char*)tabp->GetQualifier(); + db = (char*)(tabp->GetSchema() ? tabp->GetSchema() : curdb); name = (char*)tabp->GetName(); // Check for eventual loop for (PTABLE tp = To_Table; tp; tp = tp->Next) { - cdb = (tp->Qualifier) ? tp->Qualifier : curdb; + cdb = (tp->Schema) ? tp->Schema : curdb; if (!stricmp(name, tp->Name) && !stricmp(db, cdb)) { sprintf(g->Message, "Table %s.%s pointing on itself", db, name); @@ -423,7 +432,7 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) } // endif Define if (db) - ((PTDBMY)tdbp)->SetDatabase(tabp->GetQualifier()); + ((PTDBMY)tdbp)->SetDatabase(tabp->GetSchema()); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) tdbp->SetName(Name); // For Make_Command @@ -757,7 +766,7 @@ void PRXCOL::WriteColumn(PGLOBAL g) /***********************************************************************/ TDBTBC::TDBTBC(PPRXDEF tdp) : TDBCAT(tdp) { - Db = (PSZ)tdp->Tablep->GetQualifier(); + Db = (PSZ)tdp->Tablep->GetSchema(); Tab = (PSZ)tdp->Tablep->GetName(); } // end of TDBTBC constructor From c9552533c962af885796c3189d714305d8fbf7cb Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 12 Jan 2016 16:29:02 +0400 Subject: [PATCH 11/83] MDEV-9106 Audit plugin compiled with MariaDB can't install on MySQL 5.7. The audit API was seriously changed in MySQL 5.7. so we had to adapt the plugin's code to that. --- .../suite/plugins/r/server_audit.result | 9 +- mysys/file_logger.c | 2 + plugin/server_audit/CMakeLists.txt | 5 +- plugin/server_audit/plugin_audit_v4.h | 561 +++++++++++++ plugin/server_audit/server_audit.c | 789 ++++++++++++------ plugin/server_audit/test_audit_v4.c | 162 ++++ 6 files changed, 1263 insertions(+), 265 deletions(-) create mode 100644 plugin/server_audit/plugin_audit_v4.h create mode 100644 plugin/server_audit/test_audit_v4.c diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 69c9bc3a4be..67338506001 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -8,6 +8,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users +server_audit_loc_info server_audit_logging OFF server_audit_mode 0 server_audit_output_type file @@ -71,6 +72,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 0 server_audit_output_type file @@ -216,6 +218,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 1 server_audit_output_type file @@ -229,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -281,7 +284,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,sa_db,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, diff --git a/mysys/file_logger.c b/mysys/file_logger.c index 394b59f6378..b94cb705d3f 100644 --- a/mysys/file_logger.c +++ b/mysys/file_logger.c @@ -14,11 +14,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef FLOGGER_SKIP_INCLUDES #include "my_global.h" #include #include #include #include +#endif /*FLOGGER_SKIP_INCLUDES*/ #ifndef flogger_mutex_init #define flogger_mutex_init(A,B,C) mysql_mutex_init(A,B,C) diff --git a/plugin/server_audit/CMakeLists.txt b/plugin/server_audit/CMakeLists.txt index 527d0d702da..3de8c43c871 100644 --- a/plugin/server_audit/CMakeLists.txt +++ b/plugin/server_audit/CMakeLists.txt @@ -13,4 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -MYSQL_ADD_PLUGIN(server_audit server_audit.c MODULE_ONLY) +SET(SERVER_AUDIT_SOURCES + server_audit.c test_audit_v4.c plugin_audit_v4.h) + + MYSQL_ADD_PLUGIN(server_audit ${SERVER_AUDIT_SOURCES} MODULE_ONLY) diff --git a/plugin/server_audit/plugin_audit_v4.h b/plugin/server_audit/plugin_audit_v4.h new file mode 100644 index 00000000000..5f8e43b3811 --- /dev/null +++ b/plugin/server_audit/plugin_audit_v4.h @@ -0,0 +1,561 @@ +/* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 */ + +#ifndef _my_audit_h +#define _my_audit_h + +#ifndef PLUGIN_CONTEXT +#include "plugin.h" +#include "mysql/mysql_lex_string.h" +#ifndef MYSQL_ABI_CHECK +#include "m_string.h" +#endif +#include "my_command.h" +#include "my_sqlcommand.h" +#endif /*PLUGIN_CONTEXT*/ + +#define MYSQL_AUDIT_INTERFACE_VERSION 0x0401 + +/** + @enum mysql_event_class_t + + Audit event classes. +*/ +typedef enum +{ + MYSQL_AUDIT_GENERAL_CLASS = 0, + MYSQL_AUDIT_CONNECTION_CLASS = 1, + MYSQL_AUDIT_PARSE_CLASS = 2, + MYSQL_AUDIT_AUTHORIZATION_CLASS = 3, + MYSQL_AUDIT_TABLE_ACCESS_CLASS = 4, + MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS = 5, + MYSQL_AUDIT_SERVER_STARTUP_CLASS = 6, + MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS = 7, + MYSQL_AUDIT_COMMAND_CLASS = 8, + MYSQL_AUDIT_QUERY_CLASS = 9, + MYSQL_AUDIT_STORED_PROGRAM_CLASS = 10, + /* This item must be last in the list. */ + MYSQL_AUDIT_CLASS_MASK_SIZE +} mysql_event_class_t; + +/** + @struct st_mysql_audit + + The descriptor structure that is referred from st_mysql_plugin. +*/ +struct st_mysql_audit +{ + /** + Interface version. + */ + int interface_version; + + /** + Event occurs when the event class consumer is to be + disassociated from the specified THD.This would typically occur + before some operation which may require sleeping - such as when + waiting for the next query from the client. + */ + void (*release_thd)(MYSQL_THD); + + /** + Invoked whenever an event occurs which is of any + class for which the plugin has interest.The second argument + indicates the specific event class and the third argument is data + as required for that class. + */ + int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *); + + /** + An array of bits used to indicate what event classes + that this plugin wants to receive. + */ + unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; +}; + +/** + @typedef enum_sql_command_t + + SQL command type definition. +*/ +typedef enum enum_sql_command enum_sql_command_t; + +/** + @enum mysql_event_general_subclass_t + + Events for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +typedef enum +{ + /** occurs before emitting to the general query log. */ + MYSQL_AUDIT_GENERAL_LOG = 1 << 0, + /** occurs before transmitting errors to the user. */ + MYSQL_AUDIT_GENERAL_ERROR = 1 << 1, + /** occurs after transmitting a resultset to the user. */ + MYSQL_AUDIT_GENERAL_RESULT = 1 << 2, + /** occurs after transmitting a resultset or errors */ + MYSQL_AUDIT_GENERAL_STATUS = 1 << 3 +} mysql_event_general_subclass_t; + +#define MYSQL_AUDIT_GENERAL_ALL (MYSQL_AUDIT_GENERAL_LOG | \ + MYSQL_AUDIT_GENERAL_ERROR | \ + MYSQL_AUDIT_GENERAL_RESULT | \ + MYSQL_AUDIT_GENERAL_STATUS) +/** + @struct mysql_event_general + + Structure for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +struct mysql_event_general +{ + mysql_event_general_subclass_t event_subclass; + int general_error_code; + unsigned long general_thread_id; + MYSQL_LEX_CSTRING general_user; + MYSQL_LEX_CSTRING general_command; + MYSQL_LEX_CSTRING general_query; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; + MYSQL_LEX_CSTRING general_host; + MYSQL_LEX_CSTRING general_sql_command; + MYSQL_LEX_CSTRING general_external_user; + MYSQL_LEX_CSTRING general_ip; +}; + +/** + @enum mysql_event_connection_subclass_t + + Events for MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +typedef enum +{ + /** occurs after authentication phase is completed. */ + MYSQL_AUDIT_CONNECTION_CONNECT = 1 << 0, + /** occurs after connection is terminated. */ + MYSQL_AUDIT_CONNECTION_DISCONNECT = 1 << 1, + /** occurs after COM_CHANGE_USER RPC is completed. */ + MYSQL_AUDIT_CONNECTION_CHANGE_USER = 1 << 2, + /** occurs before authentication. */ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3 +} mysql_event_connection_subclass_t; + +#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \ + MYSQL_AUDIT_CONNECTION_DISCONNECT | \ + MYSQL_AUDIT_CONNECTION_CHANGE_USER | \ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE) +/** + @struct mysql_event_connection + + Structure for the MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +struct mysql_event_connection +{ + /** Event subclass. */ + mysql_event_connection_subclass_t event_subclass; + /** Current status of the connection. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** User name of this connection. */ + MYSQL_LEX_CSTRING user; + /** Priv user name. */ + MYSQL_LEX_CSTRING priv_user; + /** External user name. */ + MYSQL_LEX_CSTRING external_user; + /** Proxy user used for this connection. */ + MYSQL_LEX_CSTRING proxy_user; + /** Connection host. */ + MYSQL_LEX_CSTRING host; + /** IP of the connection. */ + MYSQL_LEX_CSTRING ip; + /** Database name specified at connection time. */ + MYSQL_LEX_CSTRING database; + /** Connection type: + - 0 Undefined + - 1 TCP/IP + - 2 Socket + - 3 Named pipe + - 4 SSL + - 5 Shared memory + */ + int connection_type; +}; + +/** +@enum mysql_event_parse_subclass_t + +Events for MYSQL_AUDIT_PARSE_CLASS event class. +*/ +typedef enum +{ + /** occurs before the query parsing. */ + MYSQL_AUDIT_PARSE_PREPARSE = 1 << 0, + /** occurs after the query parsing. */ + MYSQL_AUDIT_PARSE_POSTPARSE = 1 << 1 +} mysql_event_parse_subclass_t; + +#define MYSQL_AUDIT_PARSE_ALL (MYSQL_AUDIT_PARSE_PREPARSE | \ + MYSQL_AUDIT_PARSE_POSTPARSE) + +typedef enum +{ + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_NONE = 0, + /// mysql_event_parse::flags Must be set by a plugin if the query is rewritten. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN = 1 << 0, + /// mysql_event_parse::flags Is set by the server if the query is prepared statement. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_IS_PREPARED_STATEMENT = 1 << 1 +} mysql_event_parse_rewrite_plugin_flag; + +/** Data for the MYSQL_AUDIT_PARSE events */ +struct mysql_event_parse +{ + /** MYSQL_AUDIT_[PRE|POST]_PARSE event id */ + mysql_event_parse_subclass_t event_subclass; + + /** one of FLAG_REWRITE_PLUGIN_* */ + mysql_event_parse_rewrite_plugin_flag *flags; + + /** input: the original query text */ + MYSQL_LEX_CSTRING query; + + /** output: returns the null-terminated rewriten query allocated by my_malloc() */ + MYSQL_LEX_CSTRING *rewritten_query; +}; + +/** + @enum mysql_event_authorization_subclass_t + + Events for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +typedef enum +{ + MYSQL_AUDIT_AUTHORIZATION_USER = 1 << 0, + /** Occurs when database privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_DB = 1 << 1, + /** Occurs when table privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_TABLE = 1 << 2, + /** Occurs when column privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_COLUMN = 1 << 3, + /** Occurs when procedure privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE = 1 << 4, + /** Occurs when proxy privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROXY = 1 << 5 +} mysql_event_authorization_subclass_t; + +#define MYSQL_AUDIT_AUTHORIZATION_ALL (MYSQL_AUDIT_AUTHORIZATION_USER | \ + MYSQL_AUDIT_AUTHORIZATION_DB | \ + MYSQL_AUDIT_AUTHORIZATION_TABLE | \ + MYSQL_AUDIT_AUTHORIZATION_COLUMN | \ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE | \ + MYSQL_AUDIT_AUTHORIZATION_PROXY) +/** + @struct mysql_event_authorization + + Structure for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +struct mysql_event_authorization +{ + /** Event subclass. */ + mysql_event_authorization_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned int connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING database; + /** Table name. */ + MYSQL_LEX_CSTRING table; + /** Other name associated with the event. */ + MYSQL_LEX_CSTRING object; + /** Requested authorization privileges. */ + unsigned long requested_privilege; + /** Currently granted authorization privileges. */ + unsigned long granted_privilege; +}; + +/** + @enum mysql_event_table_row_access_subclass_t + + Events for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +typedef enum +{ + /** Occurs when table data are read. */ + MYSQL_AUDIT_TABLE_ACCESS_READ = 1 << 0, + /** Occurs when table data are inserted. */ + MYSQL_AUDIT_TABLE_ACCESS_INSERT = 1 << 1, + /** Occurs when table data are updated. */ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE = 1 << 2, + /** Occurs when table data are deleted. */ + MYSQL_AUDIT_TABLE_ACCESS_DELETE = 1 << 3 +} mysql_event_table_access_subclass_t; + +#define MYSQL_AUDIT_TABLE_ACCESS_ALL (MYSQL_AUDIT_TABLE_ACCESS_READ | \ + MYSQL_AUDIT_TABLE_ACCESS_INSERT | \ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE | \ + MYSQL_AUDIT_TABLE_ACCESS_DELETE) + +/** + @struct mysql_event_table_row_access + + Structure for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +struct mysql_event_table_access +{ + /** Event subclass. */ + mysql_event_table_access_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING table_database; + /** Table name. */ + MYSQL_LEX_CSTRING table_name; +}; + +/** + @enum mysql_event_global_variable_subclass_t + + Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is retrieved. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_GET = 1 << 0, + /** Occurs when global variable is set. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET = 1 << 1 +} mysql_event_global_variable_subclass_t; + +#define MYSQL_AUDIT_GLOBAL_VARIABLE_ALL (MYSQL_AUDIT_GLOBAL_VARIABLE_GET | \ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET) + +/** Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. */ +struct mysql_event_global_variable +{ + /** Event subclass. */ + mysql_event_global_variable_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** Variable name. */ + MYSQL_LEX_CSTRING variable_name; + /** Variable value. */ + MYSQL_LEX_CSTRING variable_value; +}; + +/** + @enum mysql_event_server_startup_subclass_t + + Events for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +typedef enum +{ + /** Occurs after all subsystem are initialized during system start. */ + MYSQL_AUDIT_SERVER_STARTUP_STARTUP = 1 << 0 +} mysql_event_server_startup_subclass_t; + +#define MYSQL_AUDIT_SERVER_STARTUP_ALL (MYSQL_AUDIT_SERVER_STARTUP_STARTUP) + +/** + @struct mysql_event_server_startup + + Structure for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +struct mysql_event_server_startup +{ + /** Event subclass. */ + mysql_event_server_startup_subclass_t event_subclass; + /** Command line arguments. */ + const char **argv; + /** Command line arguments count. */ + unsigned int argc; +}; + +/** + @enum mysql_event_server_shutdown_subclass_t + + Events for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is set. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN = 1 << 0 +} mysql_event_server_shutdown_subclass_t; + +#define MYSQL_AUDIT_SERVER_SHUTDOWN_ALL (MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN) + +/** + @enum mysql_server_shutdown_reason_t + + Server shutdown reason. +*/ +typedef enum +{ + /** User requested shut down. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN, + /** The server aborts. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_ABORT +} mysql_server_shutdown_reason_t; + +/** + @struct mysql_event_server_shutdown + + Structure for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +struct mysql_event_server_shutdown +{ + /** Shutdown event. */ + mysql_event_server_shutdown_subclass_t event_subclass; + /** Exit code associated with the shutdown event. */ + int exit_code; + /** Shutdown reason. */ + mysql_server_shutdown_reason_t reason; +}; + +/** + @enum mysql_event_command_subclass_t + + Events for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +typedef enum +{ + /** Command start event. */ + MYSQL_AUDIT_COMMAND_START = 1 << 0, + /** Command end event. */ + MYSQL_AUDIT_COMMAND_END = 1 << 1 +} mysql_event_command_subclass_t; + +#define MYSQL_AUDIT_COMMAND_ALL (MYSQL_AUDIT_COMMAND_START | \ + MYSQL_AUDIT_COMMAND_END) +/** + @typedef enum_server_command_t + + Server command type definition. +*/ +typedef enum enum_server_command enum_server_command_t; + +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. + Events generated as a result of RPC command requests. +*/ +struct mysql_event_command +{ + /** Command event subclass. */ + mysql_event_command_subclass_t event_subclass; + /** Command event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** Command id. */ + enum_server_command_t command_id; +}; + +/** + @enum mysql_event_query_subclass_t + + Events for MYSQL_AUDIT_QUERY_CLASS event class. +*/ +typedef enum +{ + /** Query start event. */ + MYSQL_AUDIT_QUERY_START = 1 << 0, + /** Nested query start event. */ + MYSQL_AUDIT_QUERY_NESTED_START = 1 << 1, + /** Query post parse event. */ + MYSQL_AUDIT_QUERY_STATUS_END = 1 << 2, + /** Nested query status end event. */ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END = 1 << 3 +} mysql_event_query_subclass_t; + +#define MYSQL_AUDIT_QUERY_ALL (MYSQL_AUDIT_QUERY_START | \ + MYSQL_AUDIT_QUERY_NESTED_START | \ + MYSQL_AUDIT_QUERY_STATUS_END | \ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END) +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_query +{ + /** Event subclass. */ + mysql_event_query_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; +}; + +/** + @enum mysql_event_stored_program_subclass_t + + Events for MYSQL_AUDIT_STORED_PROGRAM_CLASS event class. +*/ +typedef enum +{ + /** Stored program execution event. */ + MYSQL_AUDIT_STORED_PROGRAM_EXECUTE = 1 << 0 +} mysql_event_stored_program_subclass_t; + +#define MYSQL_AUDIT_STORED_PROGRAM_ALL (MYSQL_AUDIT_STORED_PROGRAM_EXECUTE) + +/** + @struct mysql_event_command + +Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_stored_program +{ + /** Event subclass. */ + mysql_event_stored_program_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** The Database the procedure is defined in. */ + MYSQL_LEX_CSTRING database; + /** Name of the stored program. */ + MYSQL_LEX_CSTRING name; + /** Stored program parameters. */ + void *parameters; +}; + +#endif diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index bede4c9545d..fc224db72ee 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Alexey Botchkov and SkySQL Ab +/* Copyright (C) 2013, 2015, Alexey Botchkov and SkySQL Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,13 +14,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define PLUGIN_VERSION 0x103 -#define PLUGIN_STR_VERSION "1.3.0" +#define PLUGIN_VERSION 0x104 +#define PLUGIN_STR_VERSION "1.4.0" + +#define _my_thread_var loc_thread_var #include #include #include #include +#include #ifndef _WIN32 #include @@ -80,8 +83,7 @@ static void closelog() {} #endif /*MARIADB_ONLY*/ #include -//#include -#include +//#include #include #include #include @@ -89,65 +91,135 @@ static void closelog() {} #define RTLD_DEFAULT NULL #endif -#undef my_init_dynamic_array_ci -#define init_dynamic_array2 loc_init_dynamic_array2 -#define my_init_dynamic_array_ci(A,B,C,D) loc_init_dynamic_array2(A,B,NULL,C,D) -#define _my_hash_init loc_my_hash_init -#define my_hash_search loc_my_hash_search -#define my_hash_insert loc_my_hash_insert -#define my_hash_delete loc_my_hash_delete -#define my_hash_update loc_my_hash_update -#define my_hash_free loc_my_hash_free -#define my_hash_first loc_my_hash_first -#define my_hash_reset loc_my_hash_reset -#define my_hash_search_using_hash_value loc_my_hash_search_using_hash_value -#define my_hash_first_from_hash_value loc_my_hash_first_from_hash_value -#define my_calc_hash loc_my_calc_hash -#undef my_hash_first_from_hash_value -#define my_hash_first_from_hash_value loc_my_my_hash_first_from_hash_value -#define my_hash_next loc_my_hash_next -#define my_hash_element loc_my_hash_element -#define my_hash_replace loc_my_hash_replace -#define my_hash_iterate loc_my_hash_iterate - -#define alloc_dynamic loc_alloc_dynamic -#define pop_dynamic loc_pop_dynamic -#define delete_dynamic loc_delete_dynamic -uchar *loc_alloc_dynamic(DYNAMIC_ARRAY *array); -#ifdef my_strnncoll -#undef my_strnncoll -#define my_strnncoll(s, a, b, c, d) (my_strnncoll_binary((s), (a), (b), (c), (d), 0)) -#endif - -static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)), - const uchar *s, size_t slen, - const uchar *t, size_t tlen, - my_bool t_is_prefix) -{ - size_t len=min(slen,tlen); - int cmp= memcmp(s,t,len); - return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen); -} - -#include "../../mysys/array.c" -#include "../../mysys/hash.c" - #ifndef MARIADB_ONLY #undef MYSQL_SERVICE_LOGGER_INCLUDED #undef MYSQL_DYNAMIC_PLUGIN #define FLOGGER_NO_PSI -#define flogger_mutex_init(A,B,C) pthread_mutex_init(&(B)->m_mutex, C) -#define flogger_mutex_destroy(A) pthread_mutex_destroy(&(A)->m_mutex) -#define flogger_mutex_lock(A) pthread_mutex_lock(&(A)->m_mutex) -#define flogger_mutex_unlock(A) pthread_mutex_unlock(&(A)->m_mutex) + +/* How to access the pthread_mutex in mysql_mutex_t */ +//#ifdef SAFE_MUTEX +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#elif defined(MY_PTHREAD_FASTMUTEX) +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#else +#define mysql_mutex_real_mutex(A) &(A)->m_mutex +//#endif + +#define flogger_mutex_init(A,B,C) do{}while(0) +#define flogger_mutex_destroy(A) do{}while(0) +#define flogger_mutex_lock(A) do{}while(0) +#define flogger_mutex_unlock(A) do{}while(0) static char **int_mysql_data_home; static char *default_home= (char *)"."; #define mysql_data_home (*int_mysql_data_home) +#define FLOGGER_SKIP_INCLUDES +#define my_open(A, B, C) loc_open(A, B) +#define my_close(A, B) close(A) +#define my_rename(A, B, C) loc_rename(A, B) +#define my_tell(A, B) loc_tell(A) +#define my_write(A, B, C, D) write(A, B, C) +#define my_malloc(A, B) malloc(A) +#define my_free(A) free(A) +#ifdef my_vsnprintf + #undef my_vsnprintf +#endif +#define my_vsnprintf vsnprintf + +File loc_open(const char *FileName, int Flags) + /* Path-name of file */ + /* Read | write .. */ + /* Special flags */ +{ + File fd; +#if defined(_WIN32) + fd= my_win_open(FileName, Flags); +#elif !defined(NO_OPEN_3) + fd = open(FileName, Flags, my_umask); /* Normal unix */ +#else + fd = open((char *) FileName, Flags); +#endif + return fd; +} + +static int loc_rename(const char *from, const char *to) +{ + int error = 0; + +#if defined(__WIN__) + if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | + MOVEFILE_REPLACE_EXISTING)) + { + my_osmaperr(GetLastError()); +#elif defined(HAVE_RENAME) + if (rename(from,to)) + { +#else + if (link(from, to) || unlink(from)) + { +#endif + my_errno=errno; + error = -1; + } + return error; +} + + +static my_off_t loc_seek(File fd, my_off_t pos, int whence) +{ + os_off_t newpos= -1; +#ifdef _WIN32 + newpos= my_win_lseek(fd, pos, whence); +#else + newpos= lseek(fd, pos, whence); +#endif + if (newpos == (os_off_t) -1) + { + my_errno= errno; + return MY_FILEPOS_ERROR; + } + + return (my_off_t) newpos; +} + + +static my_off_t loc_tell(File fd) +{ + os_off_t pos; +#if defined (HAVE_TELL) && !defined (_WIN32) + pos= tell(fd); +#else + pos= loc_seek(fd, 0L, MY_SEEK_CUR); +#endif + if (pos == (os_off_t) -1) + { + my_errno= errno; + } + return (my_off_t) pos; +} + +#ifdef HAVE_PSI_INTERFACE +#undef HAVE_PSI_INTERFACE +#include #include "../../mysys/file_logger.c" +#define HAVE_PSI_INTERFACE +#else +#include +#include "../../mysys/file_logger.c" +#endif #endif /*!MARIADB_ONLY*/ +#undef flogger_mutex_init +#undef flogger_mutex_destroy +#undef flogger_mutex_lock +#undef flogger_mutex_unlock + +#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C) +#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A)) +#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A)) +#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A)) + #ifndef DBUG_OFF #define PLUGIN_DEBUG_VERSION "-debug" #else @@ -168,7 +240,11 @@ static char *default_home= (char *)"."; extern char server_version[]; static const char *serv_ver= NULL; static int started_mysql= 0; +static int mysql_57_started= 0; +static int debug_server_started= 0; +static int use_event_data_for_disconnect= 0; static int started_mariadb= 0; +static int maria_55_started= 0; static int maria_above_5= 0; static char *incl_users, *excl_users, *file_path, *syslog_info; @@ -193,6 +269,27 @@ static char servhost[256]; static size_t servhost_len; static char *syslog_ident; static char syslog_ident_buffer[128]= "mysql-server_auditing"; + +struct connection_info +{ + int header; + unsigned long thread_id; + unsigned long long query_id; + char db[256]; + int db_length; + char user[64]; + int user_length; + char host[64]; + int host_length; + char ip[64]; + int ip_length; + const char *query; + int query_length; + char query_buffer[1024]; + time_t query_time; + int log_always; +}; + #define DEFAULT_FILENAME_LEN 16 static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log"; @@ -246,7 +343,8 @@ static TYPELIB events_typelib= array_elements(event_names) - 1, "", event_names, NULL }; static MYSQL_SYSVAR_SET(events, events, PLUGIN_VAR_RQCMDARG, - "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE, QUERY_DDL, QUERY_DML.", + "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE," + " QUERY_DDL, QUERY_DML, QUERY_DCL.", NULL, NULL, 0, &events_typelib); #define OUTPUT_SYSLOG 0 #define OUTPUT_FILE 1 @@ -287,6 +385,13 @@ static MYSQL_SYSVAR_UINT(query_log_limit, query_log_limit, PLUGIN_VAR_OPCMDARG, "Limit on the length of the query string in a record.", NULL, NULL, 1024, 0, 0x7FFFFFFF, 1); +char locinfo_ini_value[sizeof(struct connection_info)+4]; + +static MYSQL_THDVAR_STR(loc_info, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, + "Auxiliary info.", NULL, NULL, + locinfo_ini_value); + static const char *syslog_facility_names[]= { "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH", @@ -366,6 +471,7 @@ static struct st_mysql_sys_var* vars[] = { MYSQL_SYSVAR(syslog_facility), MYSQL_SYSVAR(syslog_priority), MYSQL_SYSVAR(query_log_limit), + MYSQL_SYSVAR(loc_info), NULL }; @@ -376,6 +482,8 @@ static long log_write_failures= 0; static char current_log_buf[FN_REFLEN]= ""; static char last_error_buf[512]= ""; +extern void *mysql_v4_descriptor; + static struct st_mysql_show_var audit_status[]= { {"server_audit_active", (char *)&is_active, SHOW_BOOL}, @@ -415,7 +523,7 @@ static uchar *getkey_user(const char *entry, size_t *length, } -static void blank_user(uchar *user) +static void blank_user(char *user) { for (; *user && *user != ','; user++) *user= ' '; @@ -475,19 +583,98 @@ static void remove_blanks(char *user) } -static int user_hash_fill(HASH *h, char *users, - HASH *cmp_hash, int take_over_cmp) +struct user_name +{ + int name_len; + char *name; +}; + + +struct user_coll +{ + int n_users; + struct user_name *users; + int n_alloced; +}; + + +static void coll_init(struct user_coll *c) +{ + c->n_users= 0; + c->users= 0; + c->n_alloced= 0; +} + + +static void coll_free(struct user_coll *c) +{ + if (c->users) + { + free(c->users); + coll_init(c); + } +} + + +static int cmp_users(const void *ia, const void *ib) +{ + const struct user_name *a= (const struct user_name *) ia; + const struct user_name *b= (const struct user_name *) ib; + int dl= a->name_len - b->name_len; + if (dl != 0) + return dl; + + return strncmp(a->name, b->name, a->name_len); +} + + +static char *coll_search(struct user_coll *c, const char *n, int len) +{ + struct user_name un; + struct user_name *found; + un.name_len= len; + un.name= (char *) n; + found= (struct user_name*) bsearch(&un, c->users, c->n_users, + sizeof(c->users[0]), cmp_users); + return found ? found->name : 0; +} + + +static int coll_insert(struct user_coll *c, char *n, int len) +{ + if (c->n_users >= c->n_alloced) + { + c->n_alloced+= 128; + if (c->users == NULL) + c->users= malloc(c->n_alloced * sizeof(c->users[0])); + else + c->users= realloc(c->users, c->n_alloced * sizeof(c->users[0])); + + if (c->users == NULL) + return 1; + } + c->users[c->n_users].name= n; + c->users[c->n_users].name_len= len; + c->n_users++; + return 0; +} + + +static void coll_sort(struct user_coll *c) +{ + qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users); +} + + +static int user_coll_fill(struct user_coll *c, char *users, + struct user_coll *cmp_c, int take_over_cmp) { char *orig_users= users; - uchar *cmp_user= 0; + char *cmp_user= 0; size_t cmp_length; - int refill_cmp_hash= 0; + int refill_cmp_coll= 0; - if (my_hash_inited(h)) - my_hash_reset(h); - else - loc_my_hash_init(h, 0, &my_charset_bin, 0x100, 0, 0, - (my_hash_get_key) getkey_user, 0, 0); + c->n_users= 0; while (*users) { @@ -496,11 +683,10 @@ static int user_hash_fill(HASH *h, char *users, if (!*users) return 0; - - if (cmp_hash) + (void) getkey_user(users, &cmp_length, FALSE); + if (cmp_c) { - (void) getkey_user(users, &cmp_length, FALSE); - cmp_user= my_hash_search(cmp_hash, (const uchar *) users, cmp_length); + cmp_user= coll_search(cmp_c, users, cmp_length); if (cmp_user && take_over_cmp) { @@ -510,7 +696,7 @@ static int user_hash_fill(HASH *h, char *users, MYF(ME_JUST_WARNING), (int) cmp_length, users); internal_stop_logging= 0; blank_user(cmp_user); - refill_cmp_hash= 1; + refill_cmp_coll= 1; } else if (cmp_user) { @@ -522,7 +708,7 @@ static int user_hash_fill(HASH *h, char *users, continue; } } - if (my_hash_insert(h, (const uchar *) users)) + if (coll_insert(c, users, cmp_length)) return 1; while (*users && *users != ',') users++; @@ -531,15 +717,17 @@ static int user_hash_fill(HASH *h, char *users, users++; } - if (refill_cmp_hash) + if (refill_cmp_coll) { remove_blanks(excl_users); - return user_hash_fill(cmp_hash, excl_users, 0, 0); + return user_coll_fill(cmp_c, excl_users, 0, 0); } if (users > orig_users && users[-1] == ',') users[-1]= 0; + coll_sort(c); + return 0; } @@ -668,48 +856,19 @@ static void error_header() static LOGGER_HANDLE *logfile; -static HASH incl_user_hash, excl_user_hash; +static struct user_coll incl_user_coll, excl_user_coll; static unsigned long long query_counter= 1; -struct connection_info + +static struct connection_info *get_loc_info(MYSQL_THD thd) { - unsigned long thread_id; - unsigned long long query_id; - char db[256]; - int db_length; - char user[64]; - int user_length; - char host[64]; - int host_length; - char ip[64]; - int ip_length; - const char *query; - int query_length; - char query_buffer[1024]; - time_t query_time; - int log_always; -}; - - -static HASH connection_hash; - - -struct connection_info *alloc_connection() -{ - return malloc(ALIGN_SIZE(sizeof(struct connection_info))); + return (struct connection_info *) THDVAR(thd, loc_info); } -void free_connection(void* pconn) +static int ci_needs_setup(const struct connection_info *ci) { - (void) free(pconn); -} - - -static struct connection_info *find_connection(unsigned long id) -{ - return (struct connection_info *) - my_hash_search(&connection_hash, (const uchar *) &id, sizeof(id)); + return ci->header != 0; } @@ -873,15 +1032,24 @@ static int stop_logging() return 0; } -static struct connection_info * - add_connection(const struct mysql_event_connection *event) + +static void setup_connection_simple(struct connection_info *ci) +{ + ci->db_length= 0; + ci->user_length= 0; + ci->host_length= 0; + ci->ip_length= 0; + ci->query_length= 0; + ci->header= 0; +} + + +static void setup_connection_connect(struct connection_info *cn, + const struct mysql_event_connection *event) { - struct connection_info *cn= alloc_connection(); - if (!cn) - return 0; - cn->thread_id= event->thread_id; cn->query_id= 0; cn->log_always= 0; + cn->thread_id= event->thread_id; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->database, event->database_length); get_str_n(cn->user, &cn->user_length, sizeof(cn->db), @@ -890,11 +1058,7 @@ static struct connection_info * event->host, event->host_length); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, event->ip_length); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } @@ -917,46 +1081,43 @@ do { \ -static struct connection_info * - add_connection_initdb(const struct mysql_event_general *event) +static void setup_connection_initdb(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= 0; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->general_query, event->general_query_length); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } -static struct connection_info * - add_connection_table(const struct mysql_event_table *event) +static void setup_connection_table(struct connection_info *cn, + const struct mysql_event_table *event) { - struct connection_info *cn; - - if ((cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->thread_id; cn->query_id= query_counter++; cn->log_always= 0; @@ -968,42 +1129,40 @@ static struct connection_info * event->host, SAFE_STRLEN(event->host)); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, SAFE_STRLEN(event->ip)); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } -static struct connection_info * - add_connection_query(const struct mysql_event_general *event) +static void setup_connection_query(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= query_counter++; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), "", 0); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } @@ -1095,6 +1254,27 @@ static int log_connection(const struct connection_info *cn, } +static int log_connection_event(const struct mysql_event_connection *event, + const char *type) +{ + time_t ctime; + size_t csize; + char message[1024]; + + (void) time(&ctime); + csize= log_header(message, sizeof(message)-1, &ctime, + servhost, servhost_len, + event->user, event->user_length, + event->host, event->host_length, + event->ip, event->ip_length, + event->thread_id, 0, type); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,,%d", event->database_length, event->database, event->status); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + static size_t escape_string(const char *str, unsigned int len, char *result, size_t result_len) { @@ -1240,11 +1420,11 @@ static int do_log_user(const char *name) return 0; len= strlen(name); - if (incl_user_hash.records) - return my_hash_search(&incl_user_hash, (const uchar *) name, len) != 0; + if (incl_user_coll.n_users) + return coll_search(&incl_user_coll, name, len) != 0; - if (excl_user_hash.records) - return my_hash_search(&excl_user_hash, (const uchar *) name, len) == 0; + if (excl_user_coll.n_users) + return coll_search(&excl_user_coll, name, len) == 0; return 1; } @@ -1582,14 +1762,14 @@ static void update_general_user(struct connection_info *cn, } +static struct connection_info ci_disconnect_buffer; + #define AA_FREE_CONNECTION 1 #define AA_CHANGE_USER 2 -static struct connection_info *update_connection_hash(unsigned int event_class, - const void *ev, - int *after_action) +static void update_connection_info(struct connection_info *cn, + unsigned int event_class, const void *ev, int *after_action) { - struct connection_info *cn= NULL; *after_action= 0; switch (event_class) { @@ -1602,13 +1782,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { int init_db_command= event->general_command_length == 7 && strncmp(event->general_command, "Init DB", 7) == 0; - if ((cn= find_connection(event->general_thread_id))) + if (!ci_needs_setup(cn)) { if (init_db_command) { /* Change DB */ - get_str_n(cn->db, &cn->db_length, sizeof(cn->db), - event->general_query, event->general_query_length); + if (mysql_57_started) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + else + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->general_query, event->general_query_length); } cn->query_id= mode ? query_counter++ : event->query_id; cn->query= event->general_query; @@ -1617,18 +1801,19 @@ static struct connection_info *update_connection_hash(unsigned int event_class, update_general_user(cn, event); } else if (init_db_command) - cn= add_connection_initdb(event); + setup_connection_initdb(cn, event); else if (event_query_command(event)) - cn= add_connection_query(event); + setup_connection_query(cn, event); + else + setup_connection_simple(cn); break; } case MYSQL_AUDIT_GENERAL_STATUS: if (event_query_command(event)) { - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); if (mode == 0 && cn->db_length == 0 && event->database_length > 0) get_str_n(cn->db, &cn->db_length, sizeof(cn->db), @@ -1654,13 +1839,13 @@ static struct connection_info *update_connection_hash(unsigned int event_class, } break; case MYSQL_AUDIT_GENERAL_ERROR: - /* We need this because of a bug in the MariaDB */ - /* that it returns NULL query field for the */ - /* MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. */ - /* As a result we get empty QUERY field for errors. */ - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + /* + We need this because the MariaDB returns NULL query field for the + MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. + As a result we get empty QUERY field for errors. + */ + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); cn->query_id= mode ? query_counter++ : event->query_id; get_str_n(cn->query_buffer, &cn->query_length, sizeof(cn->query_buffer), event->general_query, event->general_query_length); @@ -1675,9 +1860,9 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { const struct mysql_event_table *event = (const struct mysql_event_table *) ev; - if (!(cn= find_connection(event->thread_id)) && - !(cn= add_connection_table(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_table(cn, event); + if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0) { get_str_n(cn->user, &cn->user_length, sizeof(cn->user), @@ -1703,17 +1888,10 @@ static struct connection_info *update_connection_hash(unsigned int event_class, switch (event->event_subclass) { case MYSQL_AUDIT_CONNECTION_CONNECT: - cn= add_connection(ev); - break; - case MYSQL_AUDIT_CONNECTION_DISCONNECT: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_FREE_CONNECTION; + setup_connection_connect(cn, event); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_CHANGE_USER; + *after_action= AA_CHANGE_USER; break; default:; } @@ -1722,17 +1900,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, default: break; } - return cn; } +struct connection_info cn_error_buffer; + + #define FILTER(MASK) (events == 0 || (events & MASK)) -static void auditing(MYSQL_THD thd __attribute__((unused)), - unsigned int event_class, - const void *ev) +void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) { - struct connection_info *cn; - int after_action; + struct connection_info *cn= 0; + int after_action= 0; /* That one is important as this function can be called with */ /* &lock_operations locked when the server logs an error reported */ @@ -1742,8 +1920,23 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), flogger_mutex_lock(&lock_operations); - if (!(cn= update_connection_hash(event_class, ev, &after_action))) - goto exit_func; + cn= get_loc_info(thd); + + if (ci_needs_setup(cn) && maria_55_started && + (event_class == MYSQL_AUDIT_GENERAL_CLASS && + *((int*)ev) == MYSQL_AUDIT_GENERAL_ERROR)) + { + /* + There's a bug in MariaDB 5.5 that prevents using thread local + variables in some cases. + The 'select * from notexisting_table;' query produces such case. + So just use the static buffer in this case. + */ + cn= &cn_error_buffer; + cn->header= 1; + } + + update_connection_info(cn, event_class, ev, &after_action); if (!logging) goto exit_func; @@ -1757,8 +1950,11 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), /* Only one subclass is logged. */ - if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) + if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event_query_command(event)) + { log_statement(cn, event, "QUERY"); + } } else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn) { @@ -1799,7 +1995,10 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT"); break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: - log_connection(cn, event, "DISCONNECT"); + if (use_event_data_for_disconnect) + log_connection_event(event, "DISCONNECT"); + else + log_connection(&ci_disconnect_buffer, event, "DISCONNECT"); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: log_connection(cn, event, "CHANGEUSER"); @@ -1814,10 +2013,6 @@ exit_func: if (after_action) { switch (after_action) { - case AA_FREE_CONNECTION: - my_hash_delete(&connection_hash, (uchar *) cn); - cn= 0; - break; case AA_CHANGE_USER: { const struct mysql_event_connection *event = @@ -1835,28 +2030,6 @@ exit_func: } -#ifdef DBUG_OFF - #ifdef __x86_64__ -static const int cmd_off= 4200; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2668; -static const int db_off= 60; -static const int db_len_off= 64; - #endif /*x86_64*/ -#else - #ifdef __x86_64__ -static const int cmd_off= 4432; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2808; -static const int db_off= 64; -static const int db_len_off= 68; - #endif /*x86_64*/ -#endif /*DBUG_OFF*/ - struct mysql_event_general_v8 { unsigned int event_class; @@ -1874,8 +2047,31 @@ struct mysql_event_general_v8 unsigned long long general_rows; }; + static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8) { +#ifdef DBUG_OFF + #ifdef __x86_64__ + static const int cmd_off= 4200; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2668; + static const int db_off= 60; + static const int db_len_off= 64; + #endif /*x86_64*/ +#else + #ifdef __x86_64__ + static const int cmd_off= 4432; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2808; + static const int db_off= 64; + static const int db_len_off= 68; + #endif /*x86_64*/ +#endif /*DBUG_OFF*/ + struct mysql_event_general event; if (ev_v8->event_class != MYSQL_AUDIT_GENERAL_CLASS) @@ -1934,6 +2130,41 @@ static void auditing_v13(MYSQL_THD thd, unsigned int *ev_v0) } +int get_db_mysql57(MYSQL_THD thd, char **name, int *len) +{ + int db_off; + int db_len_off; + if (debug_server_started) + { +#ifdef __x86_64__ + db_off= 608; + db_len_off= 616; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + else + { +#ifdef __x86_64__ + db_off= 536; + db_len_off= 544; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + +#ifdef __linux__ + *name= *(char **) (((char *) thd) + db_off); + *len= *((int *) (((char*) thd) + db_len_off)); + if (*name && (*name)[*len] != 0) + return 1; + return 0; +#else + return 1; +#endif +} /* As it's just too difficult to #include "sql_class.h", let's just copy the necessary part of the system_variables @@ -2060,8 +2291,8 @@ static int server_audit_init(void *p __attribute__((unused))) flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST); flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST); - my_hash_clear(&incl_user_hash); - my_hash_clear(&excl_user_hash); + coll_init(&incl_user_coll); + coll_init(&excl_user_coll); if (incl_users) { @@ -2079,9 +2310,6 @@ static int server_audit_init(void *p __attribute__((unused))) update_excl_users(NULL, NULL, NULL, &excl_users); } - loc_my_hash_init(&connection_hash, 0, &my_charset_bin, 0x100, 0, - sizeof(unsigned long), 0, free_connection, 0); - error_header(); fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n", PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION); @@ -2105,6 +2333,16 @@ static int server_audit_init(void *p __attribute__((unused))) } } + ci_disconnect_buffer.header= 10; + ci_disconnect_buffer.thread_id= 0; + ci_disconnect_buffer.query_id= 0; + ci_disconnect_buffer.db_length= 0; + ci_disconnect_buffer.user_length= 0; + ci_disconnect_buffer.host_length= 0; + ci_disconnect_buffer.ip_length= 0; + ci_disconnect_buffer.query= empty_str; + ci_disconnect_buffer.query_length= 0; + if (logging) start_logging(); @@ -2123,13 +2361,8 @@ static int server_audit_init_mysql(void *p) static int server_audit_deinit(void *p __attribute__((unused))) { - if (my_hash_inited(&incl_user_hash)) - my_hash_free(&incl_user_hash); - - if (my_hash_inited(&excl_user_hash)) - my_hash_free(&excl_user_hash); - - my_hash_free(&connection_hash); + coll_free(&incl_user_coll); + coll_free(&excl_user_coll); if (output_type == OUTPUT_FILE && logfile) logger_close(logfile); @@ -2164,6 +2397,7 @@ static struct st_mysql_audit mysql_descriptor = { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } }; + mysql_declare_plugin(server_audit) { MYSQL_AUDIT_PLUGIN, @@ -2206,7 +2440,7 @@ maria_declare_plugin(server_audit) audit_status, vars, PLUGIN_STR_VERSION, - MariaDB_PLUGIN_MATURITY_GAMMA + MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end; @@ -2214,22 +2448,21 @@ maria_declare_plugin_end; static void mark_always_logged(MYSQL_THD thd) { struct connection_info *cn; - if (thd && (cn= find_connection(thd_get_thread_id(thd)))) + if (thd && (cn= get_loc_info(thd))) cn->log_always= 1; } static void log_current_query(MYSQL_THD thd) { - unsigned long thd_id; struct connection_info *cn; - if (!thd || - !(cn= find_connection((thd_id= thd_get_thread_id(thd))))) + if (!thd) return; - if (FILTER(EVENT_QUERY) && do_log_user(cn->user)) + cn= get_loc_info(thd); + if (!ci_needs_setup(cn) && FILTER(EVENT_QUERY) && do_log_user(cn->user)) { - log_statement_ex(cn, cn->query_time, thd_id, cn->query, cn->query_length, - 0, "QUERY"); + log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd), + cn->query, cn->query_length, 0, "QUERY"); cn->log_always= 1; } } @@ -2325,7 +2558,7 @@ static void update_incl_users(MYSQL_THD thd, mark_always_logged(thd); strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)); incl_users= incl_user_buffer; - user_hash_fill(&incl_user_hash, incl_users, &excl_user_hash, 1); + user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); flogger_mutex_unlock(&lock_operations); @@ -2341,7 +2574,7 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), mark_always_logged(thd); strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)); excl_users= excl_user_buffer; - user_hash_fill(&excl_user_hash, excl_users, &incl_user_hash, 0); + user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); flogger_mutex_unlock(&lock_operations); @@ -2480,6 +2713,13 @@ static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)), } +struct st_my_thread_var *loc_thread_var(void) +{ + return 0; +} + + + #ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { @@ -2497,9 +2737,18 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) goto exit; started_mariadb= strstr(serv_ver, "MariaDB") != 0; + debug_server_started= strstr(serv_ver, "debug") != 0; - if (!started_mariadb) + if (started_mariadb) { + if (serv_ver[0] == '1') + use_event_data_for_disconnect= 1; + else + maria_55_started= 1; + } + else + { + /* Started MySQL. */ if (serv_ver[0] == '5' && serv_ver[2] == '5') { int sc= serv_ver[4] - '0'; @@ -2516,7 +2765,25 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) mysql_descriptor.event_notify= (void *) auditing_v13; } } + else if (serv_ver[0] == '5' && serv_ver[2] == '6') + { + int sc= serv_ver[4] - '0'; + if (serv_ver[5] >= '0' && serv_ver[5] <= '9') + sc= sc * 10 + serv_ver[5] - '0'; + if (sc >= 24) + use_event_data_for_disconnect= 1; + } + else if (serv_ver[0] == '5' && serv_ver[2] == '7') + { + mysql_57_started= 1; + _mysql_plugin_declarations_[0].info= mysql_v4_descriptor; + use_event_data_for_disconnect= 1; + } } + + memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1); + locinfo_ini_value[sizeof(locinfo_ini_value)]= 0; + exit: #ifdef _WIN32 return 1; diff --git a/plugin/server_audit/test_audit_v4.c b/plugin/server_audit/test_audit_v4.c new file mode 100644 index 00000000000..ae7527f8449 --- /dev/null +++ b/plugin/server_audit/test_audit_v4.c @@ -0,0 +1,162 @@ +#define PLUGIN_CONTEXT + +#include + +typedef void *MYSQL_THD; +struct st_mysql_const_lex_string +{ + const char *str; + size_t length; +}; +typedef struct st_mysql_const_lex_string MYSQL_LEX_CSTRING; +enum enum_sql_command{ SQLCOM_A, SQLCOM_B }; +enum enum_server_command{ SERVCOM_A, SERVCOM_B }; + +#include "plugin_audit_v4.h" + +extern void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev); +extern int get_db_mysql57(MYSQL_THD thd, char **name, int *len); + + +struct mysql_event_general_302 +{ + unsigned int event_subclass; + int general_error_code; + unsigned long general_thread_id; + const char *general_user; + unsigned int general_user_length; + const char *general_command; + unsigned int general_command_length; + const char *general_query; + unsigned int general_query_length; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; + unsigned long long query_id; + char *database; + int database_length; +}; + + +static int auditing_v4(MYSQL_THD thd, mysql_event_class_t class, const void *ev) +{ + int *subclass= (int *)ev; + struct mysql_event_general_302 ev_302; + int subclass_v3, subclass_orig; + + if (class != MYSQL_AUDIT_GENERAL_CLASS && + class != MYSQL_AUDIT_CONNECTION_CLASS) + return 0; + + subclass_orig= *subclass; + + if (class == MYSQL_AUDIT_GENERAL_CLASS) + { + struct mysql_event_general *event= (struct mysql_event_general *) ev; + ev_302.general_error_code= event->general_error_code; + ev_302.general_thread_id= event->general_thread_id; + ev_302.general_user= event->general_user.str; + ev_302.general_user_length= event->general_user.length; + ev_302.general_command= event->general_command.str; + ev_302.general_command_length= event->general_command.length; + ev_302.general_query= event->general_query.str; + ev_302.general_query_length= event->general_query.length; + ev_302.general_charset= event->general_charset; + ev_302.general_time= event->general_time; + ev_302.general_rows= event->general_rows; + if (get_db_mysql57(thd, &ev_302.database, &ev_302.database_length)) + { + ev_302.database= 0; + ev_302.database_length= 0; + } + ev= &ev_302; + switch (subclass_orig) + { + case MYSQL_AUDIT_GENERAL_LOG: + subclass_v3= 0; + ev_302.event_subclass= 0; + break; + case MYSQL_AUDIT_GENERAL_ERROR: + subclass_v3= 1; + ev_302.event_subclass= 1; + break; + case MYSQL_AUDIT_GENERAL_RESULT: + subclass_v3= 2; + ev_302.event_subclass= 2; + break; + case MYSQL_AUDIT_GENERAL_STATUS: + { + subclass_v3= 3; + ev_302.event_subclass= 3; + break; + } + default: + return 0; + } + } + else /* if (class == MYSQL_AUDIT_CONNECTION_CLASS) */ + { + switch (subclass_orig) + { + case MYSQL_AUDIT_CONNECTION_CONNECT: + subclass_v3= 0; + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + subclass_v3= 1; + break; + default: + return 0; + } + } + + *subclass= subclass_v3; + + auditing(thd, (int) class, ev); + + *subclass= subclass_orig; + return 0; +} + + +static struct st_mysql_audit mysql_descriptor = +{ + MYSQL_AUDIT_INTERFACE_VERSION, + NULL, + auditing_v4, + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) MYSQL_AUDIT_CONNECTION_ALL, + (unsigned long) MYSQL_AUDIT_PARSE_ALL, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL, + (unsigned long) MYSQL_AUDIT_COMMAND_ALL, + (unsigned long) MYSQL_AUDIT_QUERY_ALL, + (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL } +#ifdef WHEN_MYSQL_BUG_FIXED + /* + By this moment MySQL just sends no notifications at all + when we request only those we actually need. + So we have to request everything and filter them inside the + handling function. + */ + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) (MYSQL_AUDIT_CONNECTION_CONNECT | + MYSQL_AUDIT_CONNECTION_DISCONNECT), + 0, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + 0, + 0, + 0, + 0, + 0, + 0 + } +#endif /*WHEN_MYSQL_BUG_FIXED*/ +}; + + +void *mysql_v4_descriptor= &mysql_descriptor; + From cdc9aa5b5b83e33d5db3d32ef8b58d9de1dc8db9 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 13 Jan 2016 15:24:33 +0400 Subject: [PATCH 12/83] MDEV-9106 Audit Plugin doesn't run with MySQL 5.7. MariaDB 5.5 built in debug gets unhappy with mutexes. Although everything is correct, some DBUG_ASSERT can happen. So this patch keeps safe_mutex silent. --- plugin/server_audit/server_audit.c | 56 +++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index fc224db72ee..00958007ff6 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -1920,11 +1920,8 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) flogger_mutex_lock(&lock_operations); - cn= get_loc_info(thd); - - if (ci_needs_setup(cn) && maria_55_started && - (event_class == MYSQL_AUDIT_GENERAL_CLASS && - *((int*)ev) == MYSQL_AUDIT_GENERAL_ERROR)) + if (maria_55_started && debug_server_started && + event_class == MYSQL_AUDIT_GENERAL_CLASS) { /* There's a bug in MariaDB 5.5 that prevents using thread local @@ -1932,8 +1929,23 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) The 'select * from notexisting_table;' query produces such case. So just use the static buffer in this case. */ - cn= &cn_error_buffer; - cn->header= 1; + const struct mysql_event_general *event = + (const struct mysql_event_general *) ev; + + if (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR || + (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event->general_query_length == 0 && + cn_error_buffer.query_id == event->query_id)) + { + cn= &cn_error_buffer; + cn->header= 1; + } + else + cn= get_loc_info(thd); + } + else + { + cn= get_loc_info(thd); } update_connection_info(cn, event_class, ev, &after_action); @@ -2474,7 +2486,8 @@ static void update_file_path(MYSQL_THD thd, { char *new_name= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; error_header(); fprintf(stderr, "Log file name was changed to '%s'.\n", new_name); @@ -2510,7 +2523,8 @@ static void update_file_path(MYSQL_THD thd, file_path= path_buffer; exit_func: internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2554,14 +2568,16 @@ static void update_incl_users(MYSQL_THD thd, void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)); incl_users= incl_user_buffer; user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2570,14 +2586,16 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)); excl_users= excl_user_buffer; user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2653,7 +2671,8 @@ static void update_logging(MYSQL_THD thd, if (new_logging == logging) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; if ((logging= new_logging)) { @@ -2670,7 +2689,8 @@ static void update_logging(MYSQL_THD thd, } internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2682,14 +2702,16 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)), if (mode_readonly || new_mode == mode) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; mark_always_logged(thd); error_header(); fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode); mode= new_mode; internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } From fe4823d28e1fdd5f76ddf9b6d63b3108d9763cef Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 13 Jan 2016 18:02:44 +0400 Subject: [PATCH 13/83] MDEV-9106 Audit plugin doesnt run with MySQL 5.7. updata thread_pool_server_audit test result. --- .../suite/plugins/r/thread_pool_server_audit.result | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/thread_pool_server_audit.result b/mysql-test/suite/plugins/r/thread_pool_server_audit.result index 69c9bc3a4be..67338506001 100644 --- a/mysql-test/suite/plugins/r/thread_pool_server_audit.result +++ b/mysql-test/suite/plugins/r/thread_pool_server_audit.result @@ -8,6 +8,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users +server_audit_loc_info server_audit_logging OFF server_audit_mode 0 server_audit_output_type file @@ -71,6 +72,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 0 server_audit_output_type file @@ -216,6 +218,7 @@ server_audit_file_rotate_now OFF server_audit_file_rotate_size 1000000 server_audit_file_rotations 9 server_audit_incl_users odin, root, dva, tri +server_audit_loc_info server_audit_logging ON server_audit_mode 1 server_audit_output_type file @@ -229,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -281,7 +284,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,sa_db,,0 +TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, From 55d61ec878f94cfbdafee43599809dda98803a9f Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 14 Jan 2016 13:31:08 +0100 Subject: [PATCH 14/83] MDEV-4961 SSPI/GSSAPI/Kerberos authentication plugin --- plugin/auth_gssapi/CMakeLists.txt | 33 ++ plugin/auth_gssapi/README.md | 129 ++++++++ plugin/auth_gssapi/client_plugin.cc | 112 +++++++ plugin/auth_gssapi/cmake/FindGSSAPI.cmake | 78 +++++ plugin/auth_gssapi/common.h | 4 + plugin/auth_gssapi/gssapi_client.cc | 127 +++++++ plugin/auth_gssapi/gssapi_errmsg.cc | 75 +++++ plugin/auth_gssapi/gssapi_errmsg.h | 29 ++ plugin/auth_gssapi/gssapi_server.cc | 247 ++++++++++++++ .../mysql-test/auth_gssapi/basic.result | 18 + .../mysql-test/auth_gssapi/basic.test | 45 +++ .../mysql-test/auth_gssapi/suite.opt | 1 + .../mysql-test/auth_gssapi/suite.pm | 47 +++ plugin/auth_gssapi/server_plugin.cc | 175 ++++++++++ plugin/auth_gssapi/server_plugin.h | 51 +++ plugin/auth_gssapi/sspi.h | 38 +++ plugin/auth_gssapi/sspi_client.cc | 183 ++++++++++ plugin/auth_gssapi/sspi_errmsg.cc | 150 +++++++++ plugin/auth_gssapi/sspi_server.cc | 312 ++++++++++++++++++ 19 files changed, 1854 insertions(+) create mode 100644 plugin/auth_gssapi/CMakeLists.txt create mode 100644 plugin/auth_gssapi/README.md create mode 100644 plugin/auth_gssapi/client_plugin.cc create mode 100644 plugin/auth_gssapi/cmake/FindGSSAPI.cmake create mode 100644 plugin/auth_gssapi/common.h create mode 100644 plugin/auth_gssapi/gssapi_client.cc create mode 100644 plugin/auth_gssapi/gssapi_errmsg.cc create mode 100644 plugin/auth_gssapi/gssapi_errmsg.h create mode 100644 plugin/auth_gssapi/gssapi_server.cc create mode 100644 plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result create mode 100644 plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test create mode 100644 plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt create mode 100644 plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm create mode 100644 plugin/auth_gssapi/server_plugin.cc create mode 100644 plugin/auth_gssapi/server_plugin.h create mode 100644 plugin/auth_gssapi/sspi.h create mode 100644 plugin/auth_gssapi/sspi_client.cc create mode 100644 plugin/auth_gssapi/sspi_errmsg.cc create mode 100644 plugin/auth_gssapi/sspi_server.cc diff --git a/plugin/auth_gssapi/CMakeLists.txt b/plugin/auth_gssapi/CMakeLists.txt new file mode 100644 index 00000000000..61649e4845a --- /dev/null +++ b/plugin/auth_gssapi/CMakeLists.txt @@ -0,0 +1,33 @@ +IF (WIN32) + SET(USE_SSPI 1) +ENDIF() + +IF(USE_SSPI) + SET(GSSAPI_LIBS secur32) + ADD_DEFINITIONS(-DPLUGIN_SSPI) + SET(GSSAPI_CLIENT sspi_client.cc) + SET(GSSAPI_SERVER sspi_server.cc) + SET(GSSAPI_ERRMSG sspi_errmsg.cc) +ELSE() + SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + FIND_PACKAGE(GSSAPI) + IF(GSSAPI_FOUND) + INCLUDE_DIRECTORIES(${GSSAPI_INCS}) + ADD_DEFINITIONS(-DPLUGIN_GSSAPI) + SET(GSSAPI_CLIENT gssapi_client.cc) + SET(GSSAPI_SERVER gssapi_server.cc) + SET(GSSAPI_ERRMSG gssapi_errmsg.cc) + ELSE() + # Can't build plugin + RETURN() + ENDIF() +ENDIF () + + +MYSQL_ADD_PLUGIN(auth_gssapi server_plugin.cc ${GSSAPI_SERVER} ${GSSAPI_ERRMSG} + LINK_LIBRARIES ${GSSAPI_LIBS} + MODULE_ONLY) + +MYSQL_ADD_PLUGIN(auth_gssapi_client client_plugin.cc ${GSSAPI_CLIENT} ${GSSAPI_ERRMSG} + LINK_LIBRARIES ${GSSAPI_LIBS} + MODULE_ONLY) diff --git a/plugin/auth_gssapi/README.md b/plugin/auth_gssapi/README.md new file mode 100644 index 00000000000..f20128f1551 --- /dev/null +++ b/plugin/auth_gssapi/README.md @@ -0,0 +1,129 @@ +# GSSAPI/SSPI authentication for MariaDB + +This article gives instructions on configuring GSSAPI authentication plugin +for MariaDB for passwordless login. + +On Unix systems, GSSAPI is usually synonymous with Kerberos authentication. +Windows has slightly different but very similar API called SSPI, that along with Kerberos, also supports NTLM authentication. + +This plugin includes support for Kerberos on Unix, but also can be used as for Windows authentication with or without domain +environment. + +## Server-side preparations on Unix +To use the plugin, some preparation need to be done on the server side on Unixes. +MariaDB server will read need access to the Kerberos keytab file, that contains service principal name for the MariaDB server. + + +If you are using **Unix Kerberos KDC (MIT,Heimdal)** + +- Create service principal using kadmin tool + +``` +kadmin -q "addprinc -randkey mariadb/host.domain.com" +``` + +(replace host.domain.com with fully qualified DNS name for the server host) + +- Export the newly created user to the keytab file + +``` +kadmin -q "ktadd -k /path/to/mariadb.keytab mariadb/host.domain.com" +``` + +More details can be found [here](http://www.microhowto.info/howto/create_a_service_principal_using_mit_kerberos.html) +and [here](http://www.microhowto.info/howto/add_a_host_or_service_principal_to_a_keytab_using_mit_kerberos.html) + +If you are using **Windows Active Directory KDC** +you can need to create keytab using ktpass.exe tool on Windows, map principal user to an existing domain user like this + +``` +ktpass.exe /princ mariadb/host.domain.com@DOMAIN.COM /mapuser someuser /pass MyPas$w0rd /out mariadb.keytab /crypto all /ptype KRB5_NT_PRINCIPAL /mapop set +``` + +and then transfer the keytab file to the Unix server. See [Microsoft documentation](https://technet.microsoft.com/en-us/library/cc753771.aspx) for details. + + +## Server side preparations on Windows. +Usually nothing need to be done. MariaDB server should to run on a domain joined machine, either as NetworkService account +(which is default if it runs as service) or run under any other domain account credentials. +Creating service principal is not required here (but you can still do it using [_setspn_](https://technet.microsoft.com/en-us/library/cc731241.aspx) tool) + + +# Installing plugin +- Start the server + +- On Unix, edit my the my.cnf/my.ini configuration file, set the parameter gssapi-keytab-path to point to previously +created keytab path. + +``` + gssapi-keytab-path=/path/to/mariadb.keytab +``` + +- Optionally on Unix, in case the service principal name differs from default mariadb/host.domain.com@REALM, +configure alternative principal name with + +``` + gssapi-principal-name=alternative/principalname@REALM +``` + +- In mysql command line client, execute + +``` + INSTALL SONAME 'auth_gssapi' +``` + +#Creating users + +Now, you can create a user for GSSAPI/SSPI authentication. CREATE USER command, for Kerberos user +would be like this (*long* form, see below for short one) + +``` +CREATE USER usr1 IDENTIFIED WITH gssapi AS 'usr1@EXAMPLE.COM'; +``` + +(replace with real username and realm) + +The part after AS is mechanism specific, and needs to be ``machine\\usr1`` for Windows users identified with NTLM. + +You may also use alternative *short* form of CREATE USER + +``` +CREATE USER usr1 IDENTIFIED WITH gssapi; +``` + +If this syntax is used, realm part is used for comparison +thus 'usr1@EXAMPLE.COM', 'usr1@EXAMPLE.CO.UK' and 'mymachine\usr1' will all identify as 'usr1'. + +#Login as GSSAPI user with command line clients + +Using command line client, do + +``` +mysql --plugin-dir=/path/to/plugin-dir -u usr1 +``` + +#Plugin variables +- **gssapi-keytab-path** (Unix only) - Path to the server keytab file +- **gssapi-principal-name** - name of the service principal. +- **gssapi-mech-name** (Windows only) - Name of the SSPI package used by server. Can be either 'Kerberos' or 'Negotiate'. + Defaults to 'Negotiate' (both Kerberos and NTLM users can connect) + Set it to 'Kerberos', to prevent less secure NTLM in domain environments, but leave it as default(Negotiate) + to allow non-domain environment (e.g if server does not run in domain enviroment). + + +#Implementation + +Overview of the protocol between client and server + +1. Server : Construct gssapi-principal-name if not set in my.cnf. On Unixes defaults to hostbased name for service "mariadb". On Windows to user's or machine's domain names. +Acquire credentials for gssapi-principal-name with ```gss_acquire_cred() / AcquireSecurityCredentials()```. +Send packet with principal name and mech ```"gssapi-principal-name\0gssapi-mech-name\0"``` to client ( on Unix, empty string used for gssapi-mech) + +2. Client: execute ```gss_init_sec_context() / InitializeSecurityContext()``` passing gssapi-principal-name / gssapi-mech-name parameters. +Send resulting GSSAPI blob to server. + +3. Server : receive blob from client, execute ```gss_accept_sec_context()/ AcceptSecurityContext()```, send resulting blob back to client + +4. Perform 2. and 3. can until both client and server decide that authentication is done, or until some error occured. If authentication was successful, GSSAPI context (an opaque structure) is generated on both client and server sides. + +5. Server : Client name is extracted from the context, and compared to the name provided by client(with or without realm). If name matches, plugin returns success. diff --git a/plugin/auth_gssapi/client_plugin.cc b/plugin/auth_gssapi/client_plugin.cc new file mode 100644 index 00000000000..aac80e04f92 --- /dev/null +++ b/plugin/auth_gssapi/client_plugin.cc @@ -0,0 +1,112 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + @file + + GSSAPI authentication plugin, client side +*/ +#include +#include +#include +#include +#include +#include +#include "common.h" + +extern int auth_client(char *principal_name, + char *mech, + MYSQL *mysql, + MYSQL_PLUGIN_VIO *vio); + +static void parse_server_packet(char *packet, size_t packet_len, char *spn, char *mech) +{ + size_t spn_len; + spn_len = strnlen(packet, packet_len); + strncpy(spn, packet, PRINCIPAL_NAME_MAX); + if (spn_len == packet_len - 1) + { + /* Mechanism not included into packet */ + *mech = 0; + } + else + { + strncpy(mech, packet + spn_len + 1, MECH_NAME_MAX); + } +} + +/** + Set client error message. + */ +void log_client_error(MYSQL *mysql, const char *format, ...) +{ + NET *net= &mysql->net; + va_list args; + + net->last_errno= ER_UNKNOWN_ERROR; + va_start(args, format); + vsnprintf(net->last_error, sizeof(net->last_error) - 1, + format, args); + va_end(args); + memcpy(net->sqlstate, "HY000", sizeof(net->sqlstate)); +} + +/** + The main client function of the GSSAPI plugin. + */ +static int gssapi_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + int packet_len; + unsigned char *packet; + char spn[PRINCIPAL_NAME_MAX + 1]; + char mech[MECH_NAME_MAX + 1]; + + /* read from server for service principal name */ + packet_len= vio->read_packet(vio, &packet); + if (packet_len < 0) + { + return CR_ERROR; + } + parse_server_packet((char *)packet, (size_t)packet_len, spn, mech); + return auth_client(spn, mech, mysql, vio); +} + + +/* register client plugin */ +mysql_declare_client_plugin(AUTHENTICATION) + "auth_gssapi_client", + "Shuang Qiu, Robbie Harwood, Vladislav Vaintroub", + "GSSAPI/SSPI based authentication", + {0, 1, 0}, + "BSD", + NULL, + NULL, + NULL, + NULL, + gssapi_auth_client +mysql_end_client_plugin; diff --git a/plugin/auth_gssapi/cmake/FindGSSAPI.cmake b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake new file mode 100644 index 00000000000..faee4289722 --- /dev/null +++ b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake @@ -0,0 +1,78 @@ +# - Try to detect the GSSAPI support +# Once done this will define +# +# GSSAPI_FOUND - system supports GSSAPI +# GSSAPI_INCS - the GSSAPI include directory +# GSSAPI_LIBS - the libraries needed to use GSSAPI +# GSSAPI_FLAVOR - the type of API - MIT or HEIMDAL + +# Copyright (c) 2006, Pino Toscano, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if(GSSAPI_LIBS AND GSSAPI_FLAVOR) + + # in cache already + set(GSSAPI_FOUND TRUE) + +else(GSSAPI_LIBS AND GSSAPI_FLAVOR) + + find_program(KRB5_CONFIG NAMES krb5-config PATHS + /opt/local/bin + ONLY_CMAKE_FIND_ROOT_PATH # this is required when cross compiling with cmake 2.6 and ignored with cmake 2.4, Alex + ) + mark_as_advanced(KRB5_CONFIG) + + #reset vars + set(GSSAPI_INCS) + set(GSSAPI_LIBS) + set(GSSAPI_FLAVOR) + + if(KRB5_CONFIG) + + set(HAVE_KRB5_GSSAPI TRUE) + exec_program(${KRB5_CONFIG} ARGS --libs gssapi RETURN_VALUE _return_VALUE OUTPUT_VARIABLE GSSAPI_LIBS) + if(_return_VALUE) + message(STATUS "GSSAPI configure check failed.") + set(HAVE_KRB5_GSSAPI FALSE) + endif(_return_VALUE) + + exec_program(${KRB5_CONFIG} ARGS --cflags gssapi RETURN_VALUE _return_VALUE OUTPUT_VARIABLE GSSAPI_INCS) + string(REGEX REPLACE "(\r?\n)+$" "" GSSAPI_INCS "${GSSAPI_INCS}") + string(REGEX REPLACE " *-I" ";" GSSAPI_INCS "${GSSAPI_INCS}") + + exec_program(${KRB5_CONFIG} ARGS --vendor RETURN_VALUE _return_VALUE OUTPUT_VARIABLE gssapi_flavor_tmp) + set(GSSAPI_FLAVOR_MIT) + if(gssapi_flavor_tmp MATCHES ".*Massachusetts.*") + set(GSSAPI_FLAVOR "MIT") + else(gssapi_flavor_tmp MATCHES ".*Massachusetts.*") + set(GSSAPI_FLAVOR "HEIMDAL") + endif(gssapi_flavor_tmp MATCHES ".*Massachusetts.*") + + if(NOT HAVE_KRB5_GSSAPI) + if (gssapi_flavor_tmp MATCHES "Sun Microsystems.*") + message(STATUS "Solaris Kerberos does not have GSSAPI; this is normal.") + set(GSSAPI_LIBS) + set(GSSAPI_INCS) + else(gssapi_flavor_tmp MATCHES "Sun Microsystems.*") + message(WARNING "${KRB5_CONFIG} failed unexpectedly.") + endif(gssapi_flavor_tmp MATCHES "Sun Microsystems.*") + endif(NOT HAVE_KRB5_GSSAPI) + + if(GSSAPI_LIBS) # GSSAPI_INCS can be also empty, so don't rely on that + set(GSSAPI_FOUND TRUE CACHE STRING "") + message(STATUS "Found GSSAPI: ${GSSAPI_LIBS}") + + set(GSSAPI_INCS ${GSSAPI_INCS} CACHE STRING "") + set(GSSAPI_LIBS ${GSSAPI_LIBS} CACHE STRING "") + set(GSSAPI_FLAVOR ${GSSAPI_FLAVOR} CACHE STRING "") + + mark_as_advanced(GSSAPI_INCS GSSAPI_LIBS GSSAPI_FLAVOR) + + endif(GSSAPI_LIBS) + + endif(KRB5_CONFIG) + +endif(GSSAPI_LIBS AND GSSAPI_FLAVOR) \ No newline at end of file diff --git a/plugin/auth_gssapi/common.h b/plugin/auth_gssapi/common.h new file mode 100644 index 00000000000..c04241acff9 --- /dev/null +++ b/plugin/auth_gssapi/common.h @@ -0,0 +1,4 @@ +/** Maximal length of the target name */ +#define PRINCIPAL_NAME_MAX 256 +/** Maximal length of the mech string */ +#define MECH_NAME_MAX 30 diff --git a/plugin/auth_gssapi/gssapi_client.cc b/plugin/auth_gssapi/gssapi_client.cc new file mode 100644 index 00000000000..d449bb83a84 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_client.cc @@ -0,0 +1,127 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include "gssapi_errmsg.h" + +extern void log_client_error(MYSQL *mysql,const char *fmt,...); + + +/* This sends the error to the client */ +static void log_error(MYSQL *mysql, OM_uint32 major, OM_uint32 minor, const char *msg) +{ + if (GSS_ERROR(major)) + { + char sysmsg[1024]; + gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg)); + log_client_error(mysql, + "Client GSSAPI error (major %u, minor %u) : %s - %s", + major, minor, msg, sysmsg); + } + else + { + log_client_error(mysql, "Client GSSAPI error : %s", msg); + } +} + +int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio) +{ + + int ret= CR_ERROR; + OM_uint32 major= 0, minor= 0; + gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT; + gss_name_t service_name= GSS_C_NO_NAME; + + if (principal_name && principal_name[0]) + { + /* import principal from plain text */ + gss_buffer_desc principal_name_buf; + principal_name_buf.length= strlen(principal_name); + principal_name_buf.value= (void *) principal_name; + major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name); + if (GSS_ERROR(major)) + { + log_error(mysql, major, minor, "gss_import_name"); + return CR_ERROR; + } + } + + gss_buffer_desc input= {0,0}; + do + { + gss_buffer_desc output= {0,0}; + major= gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ctxt, service_name, + GSS_C_NO_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, + &input, NULL, &output, NULL, NULL); + if (output.length) + { + /* send credential */ + if(vio->write_packet(vio, (unsigned char *)output.value, output.length)) + { + /* Server error packet contains detailed message. */ + ret= CR_OK_HANDSHAKE_COMPLETE; + gss_release_buffer (&minor, &output); + goto cleanup; + } + } + gss_release_buffer (&minor, &output); + + if (GSS_ERROR(major)) + { + log_error(mysql, major, minor,"gss_init_sec_context"); + goto cleanup; + } + + if (major & GSS_S_CONTINUE_NEEDED) + { + int len= vio->read_packet(vio, (unsigned char **) &input.value); + if (len <= 0) + { + /* Server error packet contains detailed message. */ + ret= CR_OK_HANDSHAKE_COMPLETE; + goto cleanup; + } + input.length= len; + } + } while (major & GSS_S_CONTINUE_NEEDED); + + ret= CR_OK; + +cleanup: + if (service_name != GSS_C_NO_NAME) + gss_release_name(&minor, &service_name); + if (ctxt != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER); + + return ret; +} diff --git a/plugin/auth_gssapi/gssapi_errmsg.cc b/plugin/auth_gssapi/gssapi_errmsg.cc new file mode 100644 index 00000000000..29adf607503 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_errmsg.cc @@ -0,0 +1,75 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +void gssapi_errmsg(OM_uint32 major, OM_uint32 minor, char *buf, size_t size) +{ + OM_uint32 message_context; + OM_uint32 status_code; + OM_uint32 maj_status; + OM_uint32 min_status; + gss_buffer_desc status_string; + char *p= buf; + char *end= buf + size - 1; + int types[] = {GSS_C_GSS_CODE,GSS_C_MECH_CODE}; + + for(int i= 0; i < 2;i++) + { + message_context= 0; + status_code= types[i] == GSS_C_GSS_CODE?major:minor; + + if(!status_code) + continue; + do + { + maj_status = gss_display_status( + &min_status, + status_code, + types[i], + GSS_C_NO_OID, + &message_context, + &status_string); + + if(maj_status) + break; + + if(p + status_string.length + 2 < end) + { + memcpy(p,status_string.value, status_string.length); + p += status_string.length; + *p++ = '.'; + *p++ = ' '; + } + + gss_release_buffer(&min_status, &status_string); + } + while (message_context != 0); + } + *p= 0; +} diff --git a/plugin/auth_gssapi/gssapi_errmsg.h b/plugin/auth_gssapi/gssapi_errmsg.h new file mode 100644 index 00000000000..786b2f6c0f9 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_errmsg.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +extern void gssapi_errmsg(OM_uint32 major, OM_uint32 minor, char *buf, size_t size); diff --git a/plugin/auth_gssapi/gssapi_server.cc b/plugin/auth_gssapi/gssapi_server.cc new file mode 100644 index 00000000000..d325b2a6a76 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_server.cc @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include "server_plugin.h" +#include "gssapi_errmsg.h" + +static gss_name_t service_name = GSS_C_NO_NAME; + +/* This sends the error to the client */ +static void log_error( OM_uint32 major, OM_uint32 minor, const char *msg) +{ + if (GSS_ERROR(major)) + { + char sysmsg[1024]; + gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg)); + my_printf_error(ER_UNKNOWN_ERROR,"Server GSSAPI error (major %u, minor %u) : %s -%s", + MYF(0), major, minor, msg, sysmsg); + } + else + { + my_printf_error(ER_UNKNOWN_ERROR, "Server GSSAPI error : %s", MYF(0), msg); + } +} + + +/* + Generate default principal service name formatted as principal name "mariadb/server.fqdn@REALM" +*/ +#include +static char* get_default_principal_name() +{ + static char default_name[1024]; + char *unparsed_name= NULL; + krb5_context context= NULL; + krb5_principal principal= NULL; + krb5_keyblock *key= NULL; + + if(krb5_init_context(&context)) + { + sql_print_warning("GSSAPI plugin : krb5_init_context failed"); + goto cleanup; + } + + if (krb5_sname_to_principal(context, NULL, "mariadb", KRB5_NT_SRV_HST, &principal)) + { + sql_print_warning("GSSAPI plugin : krb5_sname_to_principal failed"); + goto cleanup; + } + + if (krb5_unparse_name(context, principal, &unparsed_name)) + { + sql_print_warning("GSSAPI plugin : krb5_unparse_name failed"); + goto cleanup; + } + + /* Check for entry in keytab */ + if (krb5_kt_read_service_key(context, NULL, principal, 0, 0, &key)) + { + sql_print_warning("GSSAPI plugin : default principal '%s' not found in keytab", unparsed_name); + goto cleanup; + } + + strncpy(default_name, unparsed_name, sizeof(default_name)-1); + +cleanup: + if (key) + krb5_free_keyblock(context, key); + if (unparsed_name) + krb5_free_unparsed_name(context, unparsed_name); + if (principal) + krb5_free_principal(context, principal); + if (context) + krb5_free_context(context); + + return default_name; +} + + +int plugin_init() +{ + gss_buffer_desc principal_name_buf; + OM_uint32 major= 0, minor= 0; + gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; + + if(srv_keytab_path && srv_keytab_path[0]) + { + setenv("KRB5_KTNAME", srv_keytab_path, 1); + } + + if(!srv_principal_name || !srv_principal_name[0]) + srv_principal_name= get_default_principal_name(); + + /* import service principal from plain text */ + if(srv_principal_name && srv_principal_name[0]) + { + sql_print_information("GSSAPI plugin : using principal name '%s'", srv_principal_name); + principal_name_buf.length= strlen(srv_principal_name); + principal_name_buf.value= srv_principal_name; + major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name); + if(GSS_ERROR(major)) + { + log_error(major, minor, "gss_import_name"); + return -1; + } + } + else + { + service_name= GSS_C_NO_NAME; + } + + + + /* Check if SPN configuration is OK */ + major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL, + NULL); + + if (GSS_ERROR(major)) + { + log_error(major, minor, "gss_acquire_cred failed"); + return -1; + } + gss_release_cred(&minor, &cred); + + return 0; +} + +int plugin_deinit() +{ + if (service_name != GSS_C_NO_NAME) + { + OM_uint32 minor; + gss_release_name(&minor, &service_name); + } + return 0; +} + + +int auth_server(MYSQL_PLUGIN_VIO *vio,const char *user, size_t userlen, int use_full_name) +{ + + int rc= CR_ERROR; /* return code */ + + /* GSSAPI related fields */ + OM_uint32 major= 0, minor= 0, flags= 0; + gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; /* credential identifier */ + gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT; /* context identifier */ + gss_name_t client_name; + gss_buffer_desc client_name_buf, input, output; + char *client_name_str; + + /* server acquires credential */ + major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL, + NULL); + + if (GSS_ERROR(major)) + { + log_error(major, minor, "gss_acquire_cred failed"); + goto cleanup; + } + + input.length= 0; + input.value= NULL; + do + { + /* receive token from peer */ + int len= vio->read_packet(vio, (unsigned char **) &input.value); + if (len < 0) + { + log_error(0, 0, "fail to read token from client"); + goto cleanup; + } + + input.length= len; + major= gss_accept_sec_context(&minor, &ctxt, cred, &input, + GSS_C_NO_CHANNEL_BINDINGS, &client_name, + NULL, &output, &flags, NULL, NULL); + if (GSS_ERROR(major)) + { + + log_error(major, minor, "gss_accept_sec_context"); + rc= CR_ERROR; + goto cleanup; + } + + /* send token to peer */ + if (output.length) + { + if (vio->write_packet(vio, (const uchar *) output.value, output.length)) + { + gss_release_buffer(&minor, &output); + log_error(major, minor, "communication error(write)"); + goto cleanup; + } + gss_release_buffer(&minor, &output); + } + } while (major & GSS_S_CONTINUE_NEEDED); + + /* extract plain text client name */ + major= gss_display_name(&minor, client_name, &client_name_buf, NULL); + if (GSS_ERROR(major)) + { + log_error(major, minor, "gss_display_name"); + goto cleanup; + } + + client_name_str= (char *)client_name_buf.value; + + /* + * Compare input user name with the actual one. Return success if + * the names match exactly, or if use_full_name parameter is not set + * up to the '@' separator. + */ + if ((userlen == client_name_buf.length) || + (!use_full_name + && userlen < client_name_buf.length + && client_name_str[userlen] == '@')) + { + if (strncmp(client_name_str, user, userlen) == 0) + { + rc= CR_OK; + } + } + + if(rc != CR_OK) + { + my_printf_error(ER_ACCESS_DENIED_ERROR, + "GSSAPI name mismatch, requested '%s', actual name '%.*s'", + MYF(0), user, (int)client_name_buf.length, client_name_str); + } + + gss_release_buffer(&minor, &client_name_buf); + + +cleanup: + if (ctxt != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER); + if (cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&minor, &cred); + + return(rc); +} diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result new file mode 100644 index 00000000000..a859ce563c7 --- /dev/null +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result @@ -0,0 +1,18 @@ +INSTALL SONAME 'auth_gssapi'; +CREATE USER GSSAPI_SHORTNAME IDENTIFIED WITH gssapi; +SELECT USER(),CURRENT_USER(); +USER() CURRENT_USER() +GSSAPI_SHORTNAME@localhost GSSAPI_SHORTNAME@% +DROP USER GSSAPI_SHORTNAME; +CREATE USER nosuchuser IDENTIFIED WITH gssapi; +ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser', actual name 'GSSAPI_SHORTNAME' +DROP USER nosuchuser; +CREATE USER usr1 IDENTIFIED WITH gssapi as 'GSSAPI_FULLNAME'; +SELECT USER(),CURRENT_USER(); +USER() CURRENT_USER() +usr1@localhost usr1@% +DROP USER usr1; +CREATE USER nosuchuser IDENTIFIED WITH gssapi AS 'nosuchuser@EXAMPLE.COM'; +ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser@EXAMPLE.COM', actual name 'GSSAPI_FULLNAME' +DROP USER nosuchuser; +UNINSTALL SONAME 'auth_gssapi'; diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test new file mode 100644 index 00000000000..cb49c2e145b --- /dev/null +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test @@ -0,0 +1,45 @@ +INSTALL SONAME 'auth_gssapi'; + +# +# CREATE USER without 'AS' clause +# +--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME +eval CREATE USER $GSSAPI_SHORTNAME IDENTIFIED WITH gssapi; +connect (con1,localhost,$GSSAPI_SHORTNAME,,); +--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME +SELECT USER(),CURRENT_USER(); +disconnect con1; + +connection default; +--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME +eval DROP USER $GSSAPI_SHORTNAME; + +CREATE USER nosuchuser IDENTIFIED WITH gssapi; +--disable_query_log +--replace_regex /actual name '.*'/actual name 'GSSAPI_SHORTNAME'/ +--error ER_ACCESS_DENIED_ERROR +connect (con1,localhost,nosuchuser,,); +--enable_query_log +DROP USER nosuchuser; + +# +# CREATE USER with 'AS' clause +# +--replace_result $GSSAPI_FULLNAME GSSAPI_FULLNAME +eval CREATE USER usr1 IDENTIFIED WITH gssapi as '$GSSAPI_FULLNAME'; +connect (con1,localhost,usr1,,); +--replace_result $GSSAPI_FULLNAME GSSAPI_FULLNAME +SELECT USER(),CURRENT_USER(); +disconnect con1; +connection default; +DROP USER usr1; + +CREATE USER nosuchuser IDENTIFIED WITH gssapi AS 'nosuchuser@EXAMPLE.COM'; +--disable_query_log +--replace_regex /actual name '.*'/actual name 'GSSAPI_FULLNAME'/ +--error ER_ACCESS_DENIED_ERROR +connect (con1,localhost,nosuchuser,,); +--enable_query_log +DROP USER nosuchuser; + +UNINSTALL SONAME 'auth_gssapi'; \ No newline at end of file diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt new file mode 100644 index 00000000000..3077d70c9c3 --- /dev/null +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt @@ -0,0 +1 @@ +--loose-gssapi-keytab-path=$GSSAPI_KEYTAB_PATH --loose-gssapi-principal-name=$GSSAPI_PRINCIPAL_NAME diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm new file mode 100644 index 00000000000..3ffc6f1d696 --- /dev/null +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm @@ -0,0 +1,47 @@ +package My::Suite::AuthGSSAPI; + +@ISA = qw(My::Suite); + +return "No AUTH_GSSAPI plugin" unless $ENV{AUTH_GSSAPI_SO}; + +return "Not run for embedded server" if $::opt_embedded_server; + +# Following environment variables may need to be set +if ($^O eq "MSWin32") +{ + chomp(my $whoami =`whoami /UPN 2>NUL` || `whoami`); + my $fullname = $whoami; + $fullname =~ s/\\/\\\\/; # SQL escaping for backslash + $ENV{'GSSAPI_FULLNAME'} = $fullname; + $ENV{'GSSAPI_SHORTNAME'} = $ENV{'USERNAME'}; +} +else +{ + if (!$ENV{'GSSAPI_FULLNAME'}) + { + my $s = `klist |grep 'Default principal: '`; + if ($s) + { + chomp($s); + my $fullname = substr($s,19); + $ENV{'GSSAPI_FULLNAME'} = $fullname; + } + } + $ENV{'GSSAPI_SHORTNAME'} = (split /@/, $ENV{'GSSAPI_FULLNAME'}) [0]; +} + + +if (!$ENV{'GSSAPI_FULLNAME'} || !$ENV{'GSSAPI_SHORTNAME'}) +{ + return "Environment variable GSSAPI_SHORTNAME and GSSAPI_FULLNAME need to be set" +} + +foreach $var ('GSSAPI_SHORTNAME','GSSAPI_FULLNAME','GSSAPI_KEYTAB_PATH','GSSAPI_PRINCIPAL_NAME') +{ + print "$var=$ENV{$var}\n"; +} + +sub is_default { 1 } + +bless { }; + diff --git a/plugin/auth_gssapi/server_plugin.cc b/plugin/auth_gssapi/server_plugin.cc new file mode 100644 index 00000000000..64f52a332fc --- /dev/null +++ b/plugin/auth_gssapi/server_plugin.cc @@ -0,0 +1,175 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, + Vladislav Vaintroub & MariaDB Corporation + + All rights reserved. + + 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 reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/** + @file + + GSSAPI authentication plugin, server side +*/ +#include +#include +#include +#include "server_plugin.h" +#include "common.h" + +/* First packet sent from server to client, contains srv_principal_name\0mech\0 */ +static char first_packet[PRINCIPAL_NAME_MAX + MECH_NAME_MAX +2]; +static int first_packet_len; + +/* + Target name in GSSAPI/SSPI , for Kerberos it is service principal name + (often user principal name of the server user will work) +*/ +char *srv_principal_name; +char *srv_keytab_path; +char *srv_mech_name=(char *)""; +unsigned long srv_mech; + +/** + The main server function of the GSSAPI plugin. + */ +static int gssapi_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *auth_info) +{ + int use_full_name; + const char *user; + int user_len; + + /* Send first packet with target name and mech name */ + if (vio->write_packet(vio, (unsigned char *)first_packet, first_packet_len)) + { + return CR_ERROR; + } + + /* Figure out whether to use full name (as given in IDENTIFIED AS clause) + * or just short username auth_string + */ + if (auth_info->auth_string_length > 0) + { + use_full_name= 1; + user= auth_info->auth_string; + user_len= auth_info->auth_string_length; + } + else + { + use_full_name= 0; + user= auth_info->user_name; + user_len= auth_info->user_name_length; + } + + return auth_server(vio, user, user_len, use_full_name); +} + +static int initialize_plugin(void *unused) +{ + int rc; + rc = plugin_init(); + if (rc) + return rc; + + strcpy(first_packet, srv_principal_name); + strcpy(first_packet + strlen(srv_principal_name) + 1,srv_mech_name); + first_packet_len = strlen(srv_principal_name) + strlen(srv_mech_name) + 2; + + return 0; +} + +static int deinitialize_plugin(void *unused) +{ + return plugin_deinit(); +} + +/* system variable */ +static MYSQL_SYSVAR_STR(keytab_path, srv_keytab_path, + PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, + "Keytab file path (Kerberos)", + NULL, + NULL, + ""); +static MYSQL_SYSVAR_STR(principal_name, srv_principal_name, + PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, + "GSSAPI target name - service principal name for Kerberos authentication.", + NULL, + NULL, + ""); +#ifdef PLUGIN_SSPI +static const char* mech_names[] = { + "Kerberos", + "Negotiate", + "", + NULL +}; +static TYPELIB mech_name_typelib = { + array_elements(mech_names) - 1, + "mech_name_typelib", + mech_names, + NULL +}; +static MYSQL_SYSVAR_ENUM(mech_name, srv_mech, + PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, + "GSSAPI mechanism : either Kerberos or Negotiate", + NULL, + NULL, + 2,&mech_name_typelib); +#endif + +static struct st_mysql_sys_var *system_variables[]= { + MYSQL_SYSVAR(principal_name), +#ifdef PLUGIN_SSPI + MYSQL_SYSVAR(mech_name), +#endif +#ifdef PLUGIN_GSSAPI + MYSQL_SYSVAR(keytab_path), +#endif + NULL +}; + +/* Register authentication plugin */ +static struct st_mysql_auth server_handler= { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "auth_gssapi_client", + gssapi_auth +}; + +maria_declare_plugin(gssapi_server) +{ + MYSQL_AUTHENTICATION_PLUGIN, + &server_handler, + "gssapi", + "Shuang Qiu, Robbie Harwood, Vladislav Vaintroub", + "Plugin for GSSAPI/SSPI based authentication.", + PLUGIN_LICENSE_BSD, + initialize_plugin, + deinitialize_plugin, /* destructor */ + 0x0100, /* version */ + NULL, /* status variables */ + system_variables, /* system variables */ + "1.0", + MariaDB_PLUGIN_MATURITY_EXPERIMENTAL +} +maria_declare_plugin_end; + diff --git a/plugin/auth_gssapi/server_plugin.h b/plugin/auth_gssapi/server_plugin.h new file mode 100644 index 00000000000..6f1a2fc7d54 --- /dev/null +++ b/plugin/auth_gssapi/server_plugin.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Plugin variables*/ +#include +typedef enum +{ + PLUGIN_MECH_KERBEROS = 0, + PLUGIN_MECH_SPNEGO = 1, + PLUGIN_MECH_DEFAULT = 2 +}PLUGIN_MECH; + +extern unsigned long srv_mech; +extern char *srv_principal_name; +extern char *srv_mech_name; +extern char *srv_keytab_path; +/* + Check, with GSSAPI/SSPI username of logged on user. + + Depending on use_full_name parameter, compare either full name + (principal name like user@real), or local name (first component) +*/ +int plugin_init(); +int plugin_deinit(); + +int auth_server(MYSQL_PLUGIN_VIO *vio, const char *username, size_t username_len, int use_full_name); diff --git a/plugin/auth_gssapi/sspi.h b/plugin/auth_gssapi/sspi.h new file mode 100644 index 00000000000..ceb668750af --- /dev/null +++ b/plugin/auth_gssapi/sspi.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#define SECURITY_WIN32 +#include +#include +#include +#include +#include + +#define SSPI_MAX_TOKEN_SIZE 50000 +#define SEC_ERROR(err) (err < 0) +extern void sspi_errmsg(int err, char *buf, size_t size); \ No newline at end of file diff --git a/plugin/auth_gssapi/sspi_client.cc b/plugin/auth_gssapi/sspi_client.cc new file mode 100644 index 00000000000..4946a0f4a98 --- /dev/null +++ b/plugin/auth_gssapi/sspi_client.cc @@ -0,0 +1,183 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#define SECURITY_WIN32 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sspi.h" + +extern void log_client_error(MYSQL *mysql, const char *fmt, ...); +static void log_error(MYSQL *mysql, SECURITY_STATUS err, const char *msg) +{ + if (err) + { + char buf[1024]; + sspi_errmsg(err, buf, sizeof(buf)); + log_client_error(mysql, "SSPI client error 0x%x - %s - %s", err, msg, buf); + } + else + { + log_client_error(mysql, "SSPI client error %s", msg); + } +} + + +/** Client side authentication*/ +int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio) +{ + + int ret; + CredHandle cred; + CtxtHandle ctxt; + ULONG attribs = 0; + TimeStamp lifetime; + SECURITY_STATUS sspi_err; + + SecBufferDesc inbuf_desc; + SecBuffer inbuf; + SecBufferDesc outbuf_desc; + SecBuffer outbuf; + PBYTE out = NULL; + + ret= CR_ERROR; + SecInvalidateHandle(&ctxt); + SecInvalidateHandle(&cred); + + if (!mech || strcmp(mech, "Negotiate") != 0) + { + mech= "Kerberos"; + } + + sspi_err = AcquireCredentialsHandle( + NULL, + mech, + SECPKG_CRED_OUTBOUND, + NULL, + NULL, + NULL, + NULL, + &cred, + &lifetime); + + if (SEC_ERROR(sspi_err)) + { + log_error(mysql, sspi_err, "AcquireCredentialsHandle"); + return CR_ERROR; + } + + out = (PBYTE)malloc(SSPI_MAX_TOKEN_SIZE); + if (!out) + { + log_error(mysql, SEC_E_OK, "memory allocation error"); + goto cleanup; + } + + /* Prepare buffers */ + inbuf_desc.ulVersion = SECBUFFER_VERSION; + inbuf_desc.cBuffers = 1; + inbuf_desc.pBuffers = &inbuf; + inbuf.BufferType = SECBUFFER_TOKEN; + inbuf.cbBuffer = 0; + inbuf.pvBuffer = NULL; + + outbuf_desc.ulVersion = SECBUFFER_VERSION; + outbuf_desc.cBuffers = 1; + outbuf_desc.pBuffers = &outbuf; + outbuf.BufferType = SECBUFFER_TOKEN; + outbuf.pvBuffer = out; + + do + { + outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE; + sspi_err= InitializeSecurityContext( + &cred, + SecIsValidHandle(&ctxt) ? &ctxt : NULL, + principal_name, + 0, + 0, + SECURITY_NATIVE_DREP, + inbuf.cbBuffer ? &inbuf_desc : NULL, + 0, + &ctxt, + &outbuf_desc, + &attribs, + &lifetime); + if (SEC_ERROR(sspi_err)) + { + log_error(mysql, sspi_err, "InitializeSecurityContext"); + goto cleanup; + } + if (sspi_err != SEC_E_OK && sspi_err != SEC_I_CONTINUE_NEEDED) + { + log_error(mysql, sspi_err, "Unexpected response from InitializeSecurityContext"); + goto cleanup; + } + + if (outbuf.cbBuffer) + { + /* send credential to server */ + if (vio->write_packet(vio, (unsigned char *)outbuf.pvBuffer, outbuf.cbBuffer)) + { + /* Server error packet contains detailed message. */ + ret= CR_OK_HANDSHAKE_COMPLETE; + goto cleanup; + } + } + + if (sspi_err == SEC_I_CONTINUE_NEEDED) + { + int len= vio->read_packet(vio, (unsigned char **)&inbuf.pvBuffer); + if (len <= 0) + { + /* Server side error is in the last server packet. */ + ret= CR_OK_HANDSHAKE_COMPLETE; + goto cleanup; + } + inbuf.cbBuffer= len; + } + } while (sspi_err == SEC_I_CONTINUE_NEEDED); + + ret= CR_OK; + +cleanup: + + if (SecIsValidHandle(&ctxt)) + DeleteSecurityContext(&ctxt); + if (SecIsValidHandle(&cred)) + FreeCredentialsHandle(&cred); + free(out); + return ret; +} \ No newline at end of file diff --git a/plugin/auth_gssapi/sspi_errmsg.cc b/plugin/auth_gssapi/sspi_errmsg.cc new file mode 100644 index 00000000000..8c3eb99ed6b --- /dev/null +++ b/plugin/auth_gssapi/sspi_errmsg.cc @@ -0,0 +1,150 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#define ERRSYM(x) {x, #x} +static struct { + int error; + const char *sym; +} error_symbols[] = +{ + ERRSYM(SEC_E_OK), + ERRSYM(SEC_E_INSUFFICIENT_MEMORY), + ERRSYM(SEC_E_INVALID_HANDLE), + ERRSYM(SEC_E_UNSUPPORTED_FUNCTION), + ERRSYM(SEC_E_TARGET_UNKNOWN), + ERRSYM(SEC_E_INTERNAL_ERROR), + ERRSYM(SEC_E_SECPKG_NOT_FOUND), + ERRSYM(SEC_E_NOT_OWNER), + ERRSYM(SEC_E_CANNOT_INSTALL), + ERRSYM(SEC_E_INVALID_TOKEN), + ERRSYM(SEC_E_CANNOT_PACK), + ERRSYM(SEC_E_QOP_NOT_SUPPORTED), + ERRSYM(SEC_E_NO_IMPERSONATION), + ERRSYM(SEC_E_LOGON_DENIED), + ERRSYM(SEC_E_UNKNOWN_CREDENTIALS), + ERRSYM(SEC_E_NO_CREDENTIALS), + ERRSYM(SEC_E_MESSAGE_ALTERED), + ERRSYM(SEC_E_OUT_OF_SEQUENCE), + ERRSYM(SEC_E_NO_AUTHENTICATING_AUTHORITY), + ERRSYM(SEC_E_BAD_PKGID), + ERRSYM(SEC_E_CONTEXT_EXPIRED), + ERRSYM(SEC_E_INCOMPLETE_MESSAGE), + ERRSYM(SEC_E_INCOMPLETE_CREDENTIALS), + ERRSYM(SEC_E_BUFFER_TOO_SMALL), + ERRSYM(SEC_E_WRONG_PRINCIPAL), + ERRSYM(SEC_E_TIME_SKEW), + ERRSYM(SEC_E_UNTRUSTED_ROOT), + ERRSYM(SEC_E_ILLEGAL_MESSAGE), + ERRSYM(SEC_E_CERT_UNKNOWN), + ERRSYM(SEC_E_CERT_EXPIRED), + ERRSYM(SEC_E_ENCRYPT_FAILURE), + ERRSYM(SEC_E_DECRYPT_FAILURE), + ERRSYM(SEC_E_ALGORITHM_MISMATCH), + ERRSYM(SEC_E_SECURITY_QOS_FAILED), + ERRSYM(SEC_E_UNFINISHED_CONTEXT_DELETED), + ERRSYM(SEC_E_NO_TGT_REPLY), + ERRSYM(SEC_E_NO_IP_ADDRESSES), + ERRSYM(SEC_E_WRONG_CREDENTIAL_HANDLE), + ERRSYM(SEC_E_CRYPTO_SYSTEM_INVALID), + ERRSYM(SEC_E_MAX_REFERRALS_EXCEEDED), + ERRSYM(SEC_E_MUST_BE_KDC), + ERRSYM(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED), + ERRSYM(SEC_E_TOO_MANY_PRINCIPALS), + ERRSYM(SEC_E_NO_PA_DATA), + ERRSYM(SEC_E_PKINIT_NAME_MISMATCH), + ERRSYM(SEC_E_SMARTCARD_LOGON_REQUIRED), + ERRSYM(SEC_E_SHUTDOWN_IN_PROGRESS), + ERRSYM(SEC_E_KDC_INVALID_REQUEST), + ERRSYM(SEC_E_KDC_UNABLE_TO_REFER), + ERRSYM(SEC_E_KDC_UNKNOWN_ETYPE), + ERRSYM(SEC_E_UNSUPPORTED_PREAUTH), + ERRSYM(SEC_E_DELEGATION_REQUIRED), + ERRSYM(SEC_E_BAD_BINDINGS), + ERRSYM(SEC_E_MULTIPLE_ACCOUNTS), + ERRSYM(SEC_E_NO_KERB_KEY), + ERRSYM(SEC_E_CERT_WRONG_USAGE), + ERRSYM(SEC_E_DOWNGRADE_DETECTED), + ERRSYM(SEC_E_SMARTCARD_CERT_REVOKED), + ERRSYM(SEC_E_ISSUING_CA_UNTRUSTED), + ERRSYM(SEC_E_REVOCATION_OFFLINE_C), + ERRSYM(SEC_E_PKINIT_CLIENT_FAILURE), + ERRSYM(SEC_E_SMARTCARD_CERT_EXPIRED), + ERRSYM(SEC_E_NO_S4U_PROT_SUPPORT), + ERRSYM(SEC_E_CROSSREALM_DELEGATION_FAILURE), + ERRSYM(SEC_E_REVOCATION_OFFLINE_KDC), + ERRSYM(SEC_E_ISSUING_CA_UNTRUSTED_KDC), + ERRSYM(SEC_E_KDC_CERT_EXPIRED), + ERRSYM(SEC_E_KDC_CERT_REVOKED), + ERRSYM(SEC_E_INVALID_PARAMETER), + ERRSYM(SEC_E_DELEGATION_POLICY), + ERRSYM(SEC_E_POLICY_NLTM_ONLY), + ERRSYM(SEC_E_NO_CONTEXT), + ERRSYM(SEC_E_PKU2U_CERT_FAILURE), + ERRSYM(SEC_E_MUTUAL_AUTH_FAILED), + ERRSYM(SEC_E_NO_SPM), + ERRSYM(SEC_E_NOT_SUPPORTED), + {0,0} +}; + +void sspi_errmsg(int err, char *buf, size_t size) +{ + buf[size - 1] = 0; + size_t len; + + for (size_t i= 0; error_symbols[i].sym; i++) + { + if (error_symbols[i].error == err) + { + size_t len= strlen(error_symbols[i].sym); + if (len + 2 < size) + { + memcpy(buf, error_symbols[i].sym, len); + buf[len]= ' '; + buf += len + 1; + size-= len + 1; + } + break; + } + } + + len = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + err, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + buf, size, NULL); + + if(len > 0) + { + /* Trim trailing \n\r*/ + char *p; + for(p= buf + len;p > buf && (*p == '\n' || *p=='\r' || *p == 0);p--) + *p= 0; + } +} diff --git a/plugin/auth_gssapi/sspi_server.cc b/plugin/auth_gssapi/sspi_server.cc new file mode 100644 index 00000000000..1d51a66c3d8 --- /dev/null +++ b/plugin/auth_gssapi/sspi_server.cc @@ -0,0 +1,312 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +Vladislav Vaintroub & MariaDB Corporation + +All rights reserved. + +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 reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "sspi.h" +#include "common.h" +#include "server_plugin.h" +#include +#include +#include +#include + + +/* This sends the error to the client */ +static void log_error(SECURITY_STATUS err, const char *msg) +{ + if (err) + { + char buf[1024]; + sspi_errmsg(err, buf, sizeof(buf)); + my_printf_error(ER_UNKNOWN_ERROR, "SSPI server error 0x%x - %s - %s", MYF(0), msg, buf); + } + else + { + my_printf_error(ER_UNKNOWN_ERROR, "SSPI server error %s", MYF(0), msg); + } + +} + +static char INVALID_KERBEROS_PRINCIPAL[] = "localhost"; + +static char *get_default_principal_name() +{ + static char default_principal[PRINCIPAL_NAME_MAX +1]; + ULONG size= sizeof(default_principal); + + if (GetUserNameEx(NameUserPrincipal,default_principal,&size)) + return default_principal; + + size= sizeof(default_principal); + if (GetUserNameEx(NameServicePrincipal,default_principal,&size)) + return default_principal; + + char domain[PRINCIPAL_NAME_MAX+1]; + char host[PRINCIPAL_NAME_MAX+1]; + size= sizeof(domain); + if (GetComputerNameEx(ComputerNameDnsDomain,domain,&size) && size > 0) + { + size= sizeof(host); + if (GetComputerNameEx(ComputerNameDnsHostname,host,&size)) + { + _snprintf(default_principal,sizeof(default_principal),"%s$@%s",host, domain); + return default_principal; + } + } + /* Unable to retrieve useful name, return something */ + return INVALID_KERBEROS_PRINCIPAL; +} + + +/* Extract client name from SSPI context */ +static int get_client_name_from_context(CtxtHandle *ctxt, + char *name, + size_t name_len, + int use_full_name) +{ + SecPkgContext_NativeNames native_names; + SECURITY_STATUS sspi_ret; + char *p; + + sspi_ret= QueryContextAttributes(ctxt, SECPKG_ATTR_NATIVE_NAMES, &native_names); + if (sspi_ret == SEC_E_OK) + { + /* Extract user from Kerberos principal name user@realm */ + if(!use_full_name) + { + p = strrchr(native_names.sClientName,'@'); + if(p) + *p = 0; + } + strncpy(name, native_names.sClientName, name_len); + FreeContextBuffer(&native_names); + return CR_OK; + } + + sspi_ret= ImpersonateSecurityContext(ctxt); + if (sspi_ret == SEC_E_OK) + { + ULONG len= name_len; + if (!GetUserNameEx(NameSamCompatible, name, &len)) + { + log_error(GetLastError(), "GetUserNameEx"); + RevertSecurityContext(ctxt); + return CR_ERROR; + } + RevertSecurityContext(ctxt); + + /* Extract user from Windows name realm\user */ + if (!use_full_name) + { + p = strrchr(name, '\\'); + if (p) + { + p++; + memmove(name, p, name + len + 1 - p); + } + } + return CR_OK; + } + + log_error(sspi_ret, "ImpersonateSecurityContext"); + return CR_ERROR; +} + + +int auth_server(MYSQL_PLUGIN_VIO *vio, const char *user, size_t user_len, int compare_full_name) +{ + int ret; + SECURITY_STATUS sspi_ret; + ULONG attribs = 0; + TimeStamp lifetime; + CredHandle cred; + CtxtHandle ctxt; + + SecBufferDesc inbuf_desc; + SecBuffer inbuf; + SecBufferDesc outbuf_desc; + SecBuffer outbuf; + void* out= NULL; + char client_name[MYSQL_USERNAME_LENGTH + 1]; + + ret= CR_ERROR; + SecInvalidateHandle(&cred); + SecInvalidateHandle(&ctxt); + + out= malloc(SSPI_MAX_TOKEN_SIZE); + if (!out) + { + log_error(SEC_E_OK, "memory allocation failed"); + goto cleanup; + } + sspi_ret= AcquireCredentialsHandle( + srv_principal_name, + srv_mech_name, + SECPKG_CRED_INBOUND, + NULL, + NULL, + NULL, + NULL, + &cred, + &lifetime); + + if (SEC_ERROR(sspi_ret)) + { + log_error(sspi_ret, "AcquireCredentialsHandle failed"); + goto cleanup; + } + + inbuf.cbBuffer= 0; + inbuf.BufferType= SECBUFFER_TOKEN; + inbuf.pvBuffer= NULL; + inbuf_desc.ulVersion= SECBUFFER_VERSION; + inbuf_desc.cBuffers= 1; + inbuf_desc.pBuffers= &inbuf; + + outbuf.BufferType= SECBUFFER_TOKEN; + outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE; + outbuf.pvBuffer= out; + + outbuf_desc.ulVersion= SECBUFFER_VERSION; + outbuf_desc.cBuffers= 1; + outbuf_desc.pBuffers= &outbuf; + + do + { + /* Read SSPI blob from client. */ + int len= vio->read_packet(vio, (unsigned char **)&inbuf.pvBuffer); + if (len < 0) + { + log_error(SEC_E_OK, "communication error(read)"); + goto cleanup; + } + inbuf.cbBuffer= len; + outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE; + sspi_ret= AcceptSecurityContext( + &cred, + SecIsValidHandle(&ctxt) ? &ctxt : NULL, + &inbuf_desc, + attribs, + SECURITY_NATIVE_DREP, + &ctxt, + &outbuf_desc, + &attribs, + &lifetime); + + if (SEC_ERROR(sspi_ret)) + { + log_error(sspi_ret, "AcceptSecurityContext"); + goto cleanup; + } + if (sspi_ret != SEC_E_OK && sspi_ret != SEC_I_CONTINUE_NEEDED) + { + log_error(sspi_ret, "AcceptSecurityContext unexpected return value"); + goto cleanup; + } + if (outbuf.cbBuffer) + { + /* Send generated blob to client. */ + if (vio->write_packet(vio, (unsigned char *)outbuf.pvBuffer, outbuf.cbBuffer)) + { + log_error(SEC_E_OK, "communicaton error(write)"); + goto cleanup; + } + } + } while (sspi_ret == SEC_I_CONTINUE_NEEDED); + + /* Authentication done, now extract and compare user name. */ + ret= get_client_name_from_context(&ctxt, client_name, MYSQL_USERNAME_LENGTH, compare_full_name); + if (ret != CR_OK) + goto cleanup; + + /* Always compare case-insensitive on Windows. */ + ret= _stricmp(client_name, user) == 0 ? CR_OK : CR_ERROR; + if (ret != CR_OK) + { + my_printf_error(ER_ACCESS_DENIED_ERROR, + "GSSAPI name mismatch, requested '%s', actual name '%s'", + MYF(0), user, client_name); + } + +cleanup: + if (SecIsValidHandle(&ctxt)) + DeleteSecurityContext(&ctxt); + + if (SecIsValidHandle(&cred)) + FreeCredentialsHandle(&cred); + + free(out); + return ret; +} + +int plugin_init() +{ + CredHandle cred; + SECURITY_STATUS ret; + + /* + Use negotiate by default, which accepts raw kerberos + and also NTLM. + */ + if (srv_mech == PLUGIN_MECH_DEFAULT) + srv_mech= PLUGIN_MECH_SPNEGO; + + if(srv_mech == PLUGIN_MECH_KERBEROS) + srv_mech_name= "Kerberos"; + else if(srv_mech == PLUGIN_MECH_SPNEGO ) + srv_mech_name= "Negotiate"; + + if(!srv_principal_name[0]) + { + srv_principal_name= get_default_principal_name(); + } + sql_print_information("SSPI: using principal name '%s', mech '%s'", + srv_principal_name, srv_mech_name); + + ret = AcquireCredentialsHandle( + srv_principal_name, + srv_mech_name, + SECPKG_CRED_INBOUND, + NULL, + NULL, + NULL, + NULL, + &cred, + NULL); + if (SEC_ERROR(ret)) + { + log_error(ret, "AcquireCredentialsHandle"); + return -1; + } + FreeCredentialsHandle(&cred); + return 0; +} + +int plugin_deinit() +{ + return 0; +} From 9c9d10b441fe79b111509949ca40afe1a6284c0f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Fri, 15 Jan 2016 09:50:27 +0400 Subject: [PATCH 15/83] MDEV-9106 Audit plugin not working with MySQL 5.7. fixing Windows crash. --- plugin/server_audit/server_audit.c | 47 ++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 00958007ff6..d988f7e8317 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -116,18 +116,41 @@ static char *default_home= (char *)"."; #define FLOGGER_SKIP_INCLUDES #define my_open(A, B, C) loc_open(A, B) -#define my_close(A, B) close(A) +#define my_close(A, B) loc_close(A) #define my_rename(A, B, C) loc_rename(A, B) #define my_tell(A, B) loc_tell(A) -#define my_write(A, B, C, D) write(A, B, C) +#define my_write(A, B, C, D) loc_write(A, B, C) #define my_malloc(A, B) malloc(A) #define my_free(A) free(A) +#ifdef my_errno + #undef my_errno +#endif +static int loc_file_errno; +#define my_errno loc_file_errno #ifdef my_vsnprintf #undef my_vsnprintf #endif #define my_vsnprintf vsnprintf +#define logger_open loc_logger_open +#define logger_close loc_logger_close +#define logger_write loc_logger_write +#define logger_rotate loc_logger_rotate +#define logger_init_mutexts loc_logger_init_mutexts -File loc_open(const char *FileName, int Flags) + +static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count) +{ + size_t writtenbytes; +#ifdef _WIN32 + writtenbytes= my_win_write(Filedes, Buffer, Count); +#else + writtenbytes= write(Filedes, Buffer, Count); +#endif + return writtenbytes; +} + + +static File loc_open(const char *FileName, int Flags) /* Path-name of file */ /* Read | write .. */ /* Special flags */ @@ -140,9 +163,27 @@ File loc_open(const char *FileName, int Flags) #else fd = open((char *) FileName, Flags); #endif + my_errno= errno; return fd; } + +static int loc_close(File fd) +{ + int err; +#ifndef _WIN32 + do + { + err= close(fd); + } while (err == -1 && errno == EINTR); +#else + err= my_win_close(fd); +#endif + my_errno=errno; + return err; +} + + static int loc_rename(const char *from, const char *to) { int error = 0; From 06b2e327fcb9ee8737e66cf0893766df41c9d8f8 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 15 Jan 2016 12:42:51 +0100 Subject: [PATCH 16/83] Fix error handling for GTID and domain-based parallel replication This occurs when replication stops with an error, domain-based parallel replication is used, and the GTID position contains more than one domain. Furthermore, it relates to the case where the SQL thread is restarted without first stopping the IO thread. In this case, the file/offset relay-log position does not correctly represent the slave's multi-dimensional position, because other domains may be far ahead of, or behind, the domain with the failing event. So the code reverts the relay log position back to the start of a relay log file that is known to be before all active domains. There was a bug that when the SQL thread was restarted, the rli->relay_log_state was incorrectly initialised from @@gtid_slave_pos. This position will likely be too far ahead, due to reverting the relay log position. Thus, if the replication fails again after the SQL thread restart, the rli->restart_gtid_pos might be updated incorrectly. This in turn would cause a second SQL thread restart to replicate from the wrong position, if the IO thread was still left running. The fix is to initialise rli->relay_log_state from @@gtid_slave_pos only when we actually purge and re-fetch relay logs from the master, not at every SQL thread start. A related problem is the use of sql_slave_skip_counter to resolve replication failures in this kind of scenario. Since the slave position is multi-dimensional, sql_slave_skip_counter can not work properly - it is indeterminate exactly which event is to be skipped, and is unlikely to work as expected for the user. So make this an error in the case where domain-based parallel replication is used with multiple domains, suggesting instead the user to set @@gtid_slave_pos to reliably skip the desired event. --- mysql-test/suite/rpl/r/rpl_parallel.result | 2 ++ mysql-test/suite/rpl/t/rpl_parallel.test | 7 +++++++ sql/rpl_rli.cc | 2 ++ sql/share/errmsg-utf8.txt | 2 +- sql/slave.cc | 10 +--------- sql/sys_vars.cc | 23 ++++++++++++++++++++++ 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_parallel.result b/mysql-test/suite/rpl/r/rpl_parallel.result index de67f5f0610..d915337725d 100644 --- a/mysql-test/suite/rpl/r/rpl_parallel.result +++ b/mysql-test/suite/rpl/r/rpl_parallel.result @@ -929,6 +929,8 @@ a 31 32 SET sql_slave_skip_counter= 1; +ERROR HY000: When using parallel replication and GTID with multiple replication domains, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position. +include/stop_slave_io.inc include/start_slave.inc include/sync_with_master_gtid.inc SELECT * FROM t2 WHERE a >= 30 ORDER BY a; diff --git a/mysql-test/suite/rpl/t/rpl_parallel.test b/mysql-test/suite/rpl/t/rpl_parallel.test index e70dcfa5bb0..eec45bc0367 100644 --- a/mysql-test/suite/rpl/t/rpl_parallel.test +++ b/mysql-test/suite/rpl/t/rpl_parallel.test @@ -1423,6 +1423,7 @@ SELECT * FROM t6 ORDER BY a; --connection server_1 INSERT INTO t2 VALUES (31); +--let $gtid1= `SELECT @@LAST_GTID` --source include/save_master_gtid.inc --connection server_2 @@ -1438,6 +1439,7 @@ SET sql_log_bin= 1; --connection server_1 INSERT INTO t2 VALUES (32); +--let $gtid2= `SELECT @@LAST_GTID` # Rotate the binlog; the bug is triggered when the master binlog file changes # after the event group that causes the duplicate key error. FLUSH LOGS; @@ -1470,7 +1472,12 @@ START SLAVE SQL_THREAD; SELECT * FROM t2 WHERE a >= 30 ORDER BY a; # Skip the duplicate error, so we can proceed. +--error ER_SLAVE_SKIP_NOT_IN_GTID SET sql_slave_skip_counter= 1; +--source include/stop_slave_io.inc +--disable_query_log +eval SET GLOBAL gtid_slave_pos = REPLACE(@@gtid_slave_pos, "$gtid1", "$gtid2"); +--enable_query_log --source include/start_slave.inc --source include/sync_with_master_gtid.inc diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 3207c858b20..7e0a1279e05 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -389,6 +389,7 @@ Failed to open the existing relay log info file '%s' (errno %d)", if (rli->is_relay_log_recovery && init_recovery(rli->mi, &msg)) goto err; + rli->relay_log_state.load(rpl_global_gtid_slave_state); if (init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos, @@ -1137,6 +1138,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, error=1; goto err; } + rli->relay_log_state.load(rpl_global_gtid_slave_state); if (!just_reset) { /* Save name of used relay log file */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 5fed6b10425..4394e796a1a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7108,7 +7108,7 @@ ER_PRIOR_COMMIT_FAILED ER_IT_IS_A_VIEW 42S02 eng "'%-.192s' is a view" ER_SLAVE_SKIP_NOT_IN_GTID - eng "When using GTID, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position." + eng "When using parallel replication and GTID with multiple replication domains, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position." ER_TABLE_DEFINITION_TOO_BIG eng "The definition for table %`s is too big" ER_PLUGIN_INSTALLED diff --git a/sql/slave.cc b/sql/slave.cc index 0243272c443..7b493c4c0be 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4527,15 +4527,6 @@ pthread_handler_t handle_slave_sql(void *arg) serial_rgi->gtid_sub_id= 0; serial_rgi->gtid_pending= false; - if (mi->using_gtid != Master_info::USE_GTID_NO) - { - /* - We initialize the relay log state from the know starting position. - It will then be updated as required by GTID and GTID_LIST events found - while applying events read from relay logs. - */ - rli->relay_log_state.load(&rpl_global_gtid_slave_state); - } rli->gtid_skip_flag = GTID_SKIP_NOT; if (init_relay_log_pos(rli, rli->group_relay_log_name, @@ -4791,6 +4782,7 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME, } strmake_buf(rli->group_relay_log_name, ir->name); rli->group_relay_log_pos= BIN_LOG_HEADER_SIZE; + rli->relay_log_state.load(ir->relay_log_state, ir->relay_log_state_count); } } } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index d0ee489aa82..81c71ce23e3 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4319,6 +4319,29 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi) mi->connection_name.str); return true; } + if (mi->using_gtid != Master_info::USE_GTID_NO && + opt_slave_parallel_threads > 0) + { + ulong domain_count; + mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); + domain_count= rpl_global_gtid_slave_state->count(); + mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state); + if (domain_count > 1) + { + /* + With domain-based parallel replication, the slave position is + multi-dimensional, so the relay log position is not very meaningful. + It might not even correspond to the next GTID to execute in _any_ + domain (the case after error stop). So slave_skip_counter will most + likely not do what the user intends. Instead give an error, with a + suggestion to instead set @@gtid_slave_pos past the point of error; + this works reliably also in the case of multiple domains. + */ + my_error(ER_SLAVE_SKIP_NOT_IN_GTID, MYF(0)); + return true; + } + } + /* The value was stored temporarily in thd */ mi->rli.slave_skip_counter= thd->variables.slave_skip_counter; return false; From 38bcb4464eadc4e2a22e835ef009cbd1f4ae8846 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 15 Jan 2016 14:24:11 +0100 Subject: [PATCH 17/83] After-merge fix. --- sql/sys_vars.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 6c9921b394a..42bcfa2ee95 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4489,8 +4489,7 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi) mi->connection_name.str); return true; } - if (mi->using_gtid != Master_info::USE_GTID_NO && - opt_slave_parallel_threads > 0) + if (mi->using_gtid != Master_info::USE_GTID_NO && mi->using_parallel()) { ulong domain_count; mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state); From 727f92fe00ebfa1e81f3cc6c1a73af520f42a0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Wed, 9 Dec 2015 23:36:16 +0200 Subject: [PATCH 18/83] [Code cleanup] Refactor duplicate code within myisam and maria sort.cc Setting read and write methods for the SORT_PARAM struct based on variable length key is done within a single function. --- storage/maria/ma_sort.c | 62 +++++++++++++++++---------------------- storage/myisam/sort.c | 64 +++++++++++++++++------------------------ 2 files changed, 53 insertions(+), 73 deletions(-) diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c index 967d4a151be..ef6e8506ac6 100644 --- a/storage/maria/ma_sort.c +++ b/storage/maria/ma_sort.c @@ -89,6 +89,27 @@ static int write_merge_key_varlen(MARIA_SORT_PARAM *info, static inline int my_var_write(MARIA_SORT_PARAM *info, IO_CACHE *to_file, uchar *bufs); +/* + Sets the appropriate read and write methods for the MARIA_SORT_PARAM + based on the variable length key flag. +*/ +static void set_sort_param_read_write(MARIA_SORT_PARAM *sort_param) +{ + if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sort_param->write_keys= write_keys_varlen; + sort_param->read_to_buffer= read_to_buffer_varlen; + sort_param->write_key= write_merge_key_varlen; + } + else + { + sort_param->write_keys= write_keys; + sort_param->read_to_buffer= read_to_buffer; + sort_param->write_key= write_merge_key; + } +} + + /* Creates a index of sorted keys @@ -118,18 +139,7 @@ int _ma_create_index_by_sort(MARIA_SORT_PARAM *info, my_bool no_messages, (ulong) sortbuff_size, info->key_length, (ulong) info->sort_info->max_records)); - if (info->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - info->write_keys= write_keys_varlen; - info->read_to_buffer= read_to_buffer_varlen; - info->write_key=write_merge_key_varlen; - } - else - { - info->write_keys= write_keys; - info->read_to_buffer=read_to_buffer; - info->write_key=write_merge_key; - } + set_sort_param_read_write(info); my_b_clear(&tempfile); my_b_clear(&tempfile_for_exceptions); @@ -378,18 +388,7 @@ pthread_handler_t _ma_thr_find_all_keys(void *arg) if (sort_param->sort_info->got_error) goto err; - if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sort_param->write_keys= write_keys_varlen; - sort_param->read_to_buffer= read_to_buffer_varlen; - sort_param->write_key= write_merge_key_varlen; - } - else - { - sort_param->write_keys= write_keys; - sort_param->read_to_buffer= read_to_buffer; - sort_param->write_key= write_merge_key; - } + set_sort_param_read_write(sort_param); my_b_clear(&sort_param->tempfile); my_b_clear(&sort_param->tempfile_for_exceptions); @@ -614,18 +613,9 @@ int _ma_thr_write_keys(MARIA_SORT_PARAM *sort_param) { if (got_error) continue; - if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sinfo->write_keys=write_keys_varlen; - sinfo->read_to_buffer=read_to_buffer_varlen; - sinfo->write_key=write_merge_key_varlen; - } - else - { - sinfo->write_keys=write_keys; - sinfo->read_to_buffer=read_to_buffer; - sinfo->write_key=write_merge_key; - } + + set_sort_param_read_write(sinfo); + if (sinfo->buffpek.elements) { uint maxbuffer=sinfo->buffpek.elements-1; diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index 604910e5099..f490e5f0b44 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -86,6 +86,28 @@ static int write_merge_key_varlen(MI_SORT_PARAM *info, static inline int my_var_write(MI_SORT_PARAM *info, IO_CACHE *to_file, uchar *bufs); + +/* + Sets the appropriate read and write methods for the MI_SORT_PARAM + based on the variable length key flag. +*/ +static void set_sort_param_read_write(MI_SORT_PARAM *sort_param) +{ + if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sort_param->write_keys= write_keys_varlen; + sort_param->read_to_buffer= read_to_buffer_varlen; + sort_param->write_key= write_merge_key_varlen; + } + else + { + sort_param->write_keys= write_keys; + sort_param->read_to_buffer= read_to_buffer; + sort_param->write_key= write_merge_key; + } +} + + /* Creates a index of sorted keys @@ -113,18 +135,7 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages, DBUG_ENTER("_create_index_by_sort"); DBUG_PRINT("enter",("sort_length: %u", info->key_length)); - if (info->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - info->write_keys=write_keys_varlen; - info->read_to_buffer=read_to_buffer_varlen; - info->write_key= write_merge_key_varlen; - } - else - { - info->write_keys=write_keys; - info->read_to_buffer=read_to_buffer; - info->write_key=write_merge_key; - } + set_sort_param_read_write(info); my_b_clear(&tempfile); my_b_clear(&tempfile_for_exceptions); @@ -333,7 +344,6 @@ static ha_rows find_all_keys(MI_SORT_PARAM *info, ha_rows keys, DBUG_RETURN((*maxbuffer)*(keys-1)+idx); } /* find_all_keys */ - /* Search after all keys and place them in a temp. file */ pthread_handler_t thr_find_all_keys(void *arg) @@ -357,18 +367,7 @@ pthread_handler_t thr_find_all_keys(void *arg) if (sort_param->sort_info->got_error) goto err; - if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sort_param->write_keys= write_keys_varlen; - sort_param->read_to_buffer= read_to_buffer_varlen; - sort_param->write_key= write_merge_key_varlen; - } - else - { - sort_param->write_keys= write_keys; - sort_param->read_to_buffer= read_to_buffer; - sort_param->write_key= write_merge_key; - } + set_sort_param_read_write(sort_param); my_b_clear(&sort_param->tempfile); my_b_clear(&sort_param->tempfile_for_exceptions); @@ -593,18 +592,9 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) { if (got_error) continue; - if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sinfo->write_keys=write_keys_varlen; - sinfo->read_to_buffer=read_to_buffer_varlen; - sinfo->write_key=write_merge_key_varlen; - } - else - { - sinfo->write_keys=write_keys; - sinfo->read_to_buffer=read_to_buffer; - sinfo->write_key=write_merge_key; - } + + set_sort_param_read_write(sinfo); + if (sinfo->buffpek.elements) { uint maxbuffer=sinfo->buffpek.elements-1; From df32495c85d75b736ee8d251d07b70b3682dda4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 10 Dec 2015 03:56:31 +0200 Subject: [PATCH 19/83] Fixed compilation failure on MacOSX Due to a hack that has propagated to the maria storage engine, undefined behaviour would result by bypassing the initialization code of variables after my_thread_init(). By refactoring the nested logic into a separate function, this problem is resolved. --- include/myisam.h | 3 +- storage/maria/ma_sort.c | 367 ++++++++++++++++++++------------------ storage/maria/maria_def.h | 3 +- storage/myisam/sort.c | 366 +++++++++++++++++++------------------ 4 files changed, 388 insertions(+), 351 deletions(-) diff --git a/include/myisam.h b/include/myisam.h index 88ce401fabc..f7a3ae8cc8a 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -331,7 +331,8 @@ typedef struct st_sort_info my_off_t filelength, dupp, buff_length; ha_rows max_records; uint current_key, total_keys; - uint got_error, threads_running; + volatile uint got_error; + uint threads_running; myf myf_rw; enum data_file_type new_data_file_type; } MI_SORT_INFO; diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c index ef6e8506ac6..19dcd17cd08 100644 --- a/storage/maria/ma_sort.c +++ b/storage/maria/ma_sort.c @@ -364,192 +364,211 @@ err: } /* find_all_keys */ +static my_bool _ma_thr_find_all_keys_exec(MARIA_SORT_PARAM* sort_param) +{ + DBUG_ENTER("_ma_thr_find_all_keys"); + DBUG_PRINT("enter", ("master: %d", sort_param->master)); + + my_bool error= FALSE; + if (sort_param->sort_info->got_error) + error= TRUE; + if (error) + DBUG_RETURN(error); + + set_sort_param_read_write(sort_param); + + ulonglong memavl, old_memavl, sortbuff_size; + ha_keys UNINIT_VAR(keys), idx; + uint sort_length; + uint maxbuffer; + uchar **sort_keys= NULL; + + my_b_clear(&sort_param->tempfile); + my_b_clear(&sort_param->tempfile_for_exceptions); + bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); + bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); + + + sortbuff_size= sort_param->sortbuff_size; + memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); + idx= (ha_keys) sort_param->sort_info->max_records; + sort_length= sort_param->key_length; + maxbuffer= 1; + + while (memavl >= MIN_SORT_BUFFER) + { + if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= + (my_off_t) memavl) + keys= idx+1; + else if ((sort_param->sort_info->param->testflag & + (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == + T_FORCE_SORT_MEMORY) + { + /* + Use all of the given sort buffer for key data. + Allocate 1000 buffers at a start for new data. More buffers + will be allocated when needed. + */ + keys= memavl / (sort_length+sizeof(char*)); + maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); + } + else + { + uint maxbuffer_org; + do + { + maxbuffer_org= maxbuffer; + if (memavl < sizeof(BUFFPEK)*maxbuffer || + (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ + (sort_length+sizeof(char*))) <= 1 || + keys < (uint) maxbuffer) + { + mysql_mutex_lock(&sort_param->sort_info->mutex); + _ma_check_print_error(sort_param->sort_info->param, + "aria_sort_buffer_size is too small. Current " + "aria_sort_buffer_size: %llu rows: %llu " + "sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_RETURN(TRUE); + } + } + while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); + } + if ((sort_keys= (uchar **) + my_malloc(keys*(sort_length+sizeof(char*))+ + ((sort_param->keyinfo->flag & HA_FULLTEXT) ? + HA_FT_MAXBYTELEN : 0), MYF(0)))) + { + if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), + maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) + { + my_free(sort_keys); + sort_keys= NULL; /* Safety against double free on error. */ + } + else + break; + } + old_memavl= memavl; + if ((memavl= memavl/4*3) < MIN_SORT_MEMORY && + old_memavl > MIN_SORT_MEMORY) + memavl= MIN_SORT_MEMORY; + } + if (memavl < MIN_SORT_MEMORY) + { + /* purecov: begin inspected */ + mysql_mutex_lock(&sort_param->sort_info->mutex); + _ma_check_print_error(sort_param->sort_info->param, + "aria_sort_buffer_size is too small. Current " + "aria_sort_buffer_size: %llu rows: %llu " + "sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + my_errno= ENOMEM; + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_RETURN(TRUE); + /* purecov: end inspected */ + } + if (sort_param->sort_info->param->testflag & T_VERBOSE) + my_fprintf(stdout, + "Key %d - Allocating buffer for %llu keys\n", + sort_param->key + 1, (ulonglong) keys); + sort_param->sort_keys= sort_keys; + + idx= error= 0; + sort_keys[0]= (uchar*) (sort_keys+keys); + + DBUG_PRINT("info", ("reading keys")); + while (!(error= sort_param->sort_info->got_error) && + !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) + { + if (sort_param->real_key_length > sort_param->key_length) + { + if (write_key(sort_param, sort_keys[idx], + &sort_param->tempfile_for_exceptions)) + goto err; + continue; + } + + if (++idx == keys) + { + if (sort_param->write_keys(sort_param, sort_keys, idx - 1, + (BUFFPEK *)alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) + goto err; + sort_keys[0]= (uchar*) (sort_keys+keys); + memcpy(sort_keys[0], sort_keys[idx - 1], (size_t) sort_param->key_length); + idx= 1; + } + sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; + } + if (error > 0) + goto err; + if (sort_param->buffpek.elements) + { + if (sort_param->write_keys(sort_param,sort_keys, idx, + (BUFFPEK *) alloc_dynamic(&sort_param-> + buffpek), + &sort_param->tempfile)) + goto err; + sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; + } + else + sort_param->keys= idx; + + DBUG_RETURN(FALSE); + +err: + DBUG_PRINT("error", ("got some error")); + my_free(sort_keys); + sort_param->sort_keys= 0; + delete_dynamic(& sort_param->buffpek); + close_cached_file(&sort_param->tempfile); + close_cached_file(&sort_param->tempfile_for_exceptions); + + DBUG_RETURN(TRUE); +} + /* Search after all keys and place them in a temp. file */ pthread_handler_t _ma_thr_find_all_keys(void *arg) { MARIA_SORT_PARAM *sort_param= (MARIA_SORT_PARAM*) arg; - int error; - size_t memavl, old_memavl; - longlong sortbuff_size; - ha_keys UNINIT_VAR(keys), idx; - uint sort_length; - uint maxbuffer; - uchar **sort_keys=0; - - error=1; - + my_bool error= FALSE; + /* If my_thread_init fails */ if (my_thread_init()) - goto err; + error= TRUE; - { /* Add extra block since DBUG_ENTER declare variables */ - DBUG_ENTER("_ma_thr_find_all_keys"); - DBUG_PRINT("enter", ("master: %d", sort_param->master)); - if (sort_param->sort_info->got_error) - goto err; + if (error || _ma_thr_find_all_keys_exec(sort_param)) + error= TRUE; - set_sort_param_read_write(sort_param); + /* + Thread must clean up after itself. + */ + free_root(&sort_param->wordroot, MYF(0)); + /* + Detach from the share if the writer is involved. Avoid others to + be blocked. This includes a flush of the write buffer. This will + also indicate EOF to the readers. + That means that a writer always gets here first and readers - + only when they see EOF. But if a reader finishes prematurely + because of an error it may reach this earlier - don't allow it + to detach the writer thread. + */ + if (sort_param->master && sort_param->sort_info->info->rec_cache.share) + remove_io_thread(&sort_param->sort_info->info->rec_cache); - my_b_clear(&sort_param->tempfile); - my_b_clear(&sort_param->tempfile_for_exceptions); - bzero((char*) &sort_param->buffpek,sizeof(sort_param->buffpek)); - bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); + /* Readers detach from the share if any. Avoid others to be blocked. */ + if (sort_param->read_cache.share) + remove_io_thread(&sort_param->read_cache); - sortbuff_size= sort_param->sortbuff_size; - memavl= MY_MAX(sortbuff_size, MIN_SORT_MEMORY); - idx= (ha_keys) sort_param->sort_info->max_records; - sort_length= sort_param->key_length; - maxbuffer= 1; + mysql_mutex_lock(&sort_param->sort_info->mutex); + if (error) + sort_param->sort_info->got_error= 1; - while (memavl >= MIN_SORT_MEMORY) - { - if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= (my_off_t) memavl) - keys= idx+1; - else if ((sort_param->sort_info->param->testflag & - (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == - T_FORCE_SORT_MEMORY) - { - /* - Use all of the given sort buffer for key data. - Allocate 1000 buffers at a start for new data. More buffers - will be allocated when needed. - */ - keys= memavl / (sort_length+sizeof(char*)); - maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); - } - else - { - uint maxbuffer_org; - do - { - maxbuffer_org= maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer || - (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (sort_length+sizeof(char*))) <= 1 || - keys < maxbuffer) - { - _ma_check_print_error(sort_param->sort_info->param, - "aria_sort_buffer_size is too small. Current aria_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - goto err; - } - } - while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); - } - if ((sort_keys= (uchar **) - my_malloc(keys*(sort_length+sizeof(char*))+ - ((sort_param->keyinfo->flag & HA_FULLTEXT) ? - HA_FT_MAXBYTELEN : 0), MYF(0)))) - { - if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), - maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) - { - my_free(sort_keys); - sort_keys= (uchar **) NULL; /* for err: label */ - } - else - break; - } - old_memavl= memavl; - if ((memavl= memavl/4*3) < MIN_SORT_MEMORY && - old_memavl > MIN_SORT_MEMORY) - memavl= MIN_SORT_MEMORY; - } - if (memavl < MIN_SORT_MEMORY) - { - /* purecov: begin inspected */ - _ma_check_print_error(sort_param->sort_info->param, - "aria_sort_buffer_size is too small. Current aria_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - my_errno= ENOMEM; - goto err; - /* purecov: end inspected */ - } + if (!--sort_param->sort_info->threads_running) + mysql_cond_signal(&sort_param->sort_info->cond); + mysql_mutex_unlock(&sort_param->sort_info->mutex); - if (sort_param->sort_info->param->testflag & T_VERBOSE) - my_fprintf(stdout, - "Key %d - Allocating buffer for %llu keys\n", - sort_param->key + 1, (ulonglong) keys); - sort_param->sort_keys= sort_keys; - - idx= error= 0; - sort_keys[0]= (uchar*) (sort_keys+keys); - - DBUG_PRINT("info", ("reading keys")); - while (!(error= sort_param->sort_info->got_error) && - !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) - { - if (sort_param->real_key_length > sort_param->key_length) - { - if (write_key(sort_param, sort_keys[idx], - &sort_param->tempfile_for_exceptions)) - goto err; - continue; - } - - if (++idx == keys) - { - if (sort_param->write_keys(sort_param, sort_keys, idx - 1, - (BUFFPEK *)alloc_dynamic(&sort_param-> - buffpek), - &sort_param->tempfile)) - goto err; - sort_keys[0]= (uchar*) (sort_keys+keys); - memcpy(sort_keys[0], sort_keys[idx - 1], - (size_t) sort_param->key_length); - idx= 1; - } - sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; - } - if (error > 0) - goto err; - if (sort_param->buffpek.elements) - { - if (sort_param->write_keys(sort_param,sort_keys, idx, - (BUFFPEK *) alloc_dynamic(&sort_param-> - buffpek), - &sort_param->tempfile)) - goto err; - sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; - } - else - sort_param->keys= idx; - - goto ok; - -err: - DBUG_PRINT("error", ("got some error")); - sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ - my_free(sort_keys); - sort_param->sort_keys= 0; - delete_dynamic(& sort_param->buffpek); - close_cached_file(&sort_param->tempfile); - close_cached_file(&sort_param->tempfile_for_exceptions); - -ok: - free_root(&sort_param->wordroot, MYF(0)); - /* - Detach from the share if the writer is involved. Avoid others to - be blocked. This includes a flush of the write buffer. This will - also indicate EOF to the readers. - That means that a writer always gets here first and readers - - only when they see EOF. But if a reader finishes prematurely - because of an error it may reach this earlier - don't allow it - to detach the writer thread. - */ - if (sort_param->master && sort_param->sort_info->info->rec_cache.share) - remove_io_thread(&sort_param->sort_info->info->rec_cache); - - /* Readers detach from the share if any. Avoid others to be blocked. */ - if (sort_param->read_cache.share) - remove_io_thread(&sort_param->read_cache); - - mysql_mutex_lock(&sort_param->sort_info->mutex); - if (!--sort_param->sort_info->threads_running) - mysql_cond_signal(&sort_param->sort_info->cond); - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_PRINT("exit", ("======== ending thread ========")); - } my_thread_end(); return NULL; } diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 676927b119a..fa7b954f85f 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -67,7 +67,8 @@ typedef struct st_maria_sort_info pgcache_page_no_t page; ha_rows max_records; uint current_key, total_keys; - uint got_error, threads_running; + volatile uint got_error; + uint threads_running; myf myf_rw; enum data_file_type new_data_file_type, org_data_file_type; } MARIA_SORT_INFO; diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index f490e5f0b44..f6e9bfe1565 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -344,192 +344,208 @@ static ha_rows find_all_keys(MI_SORT_PARAM *info, ha_rows keys, DBUG_RETURN((*maxbuffer)*(keys-1)+idx); } /* find_all_keys */ +static my_bool thr_find_all_keys_exec(MI_SORT_PARAM *sort_param) +{ + DBUG_ENTER("thr_find_all_keys"); + DBUG_PRINT("enter", ("master: %d", sort_param->master)); + + my_bool error= FALSE; + if (sort_param->sort_info->got_error) + error= TRUE; + if (error) + DBUG_RETURN(error); + + set_sort_param_read_write(sort_param); + + ulonglong memavl, old_memavl, sortbuff_size; + ha_keys UNINIT_VAR(keys), idx; + uint sort_length; + uint maxbuffer; + uchar **sort_keys= NULL; + + my_b_clear(&sort_param->tempfile); + my_b_clear(&sort_param->tempfile_for_exceptions); + bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); + bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); + + + sortbuff_size= sort_param->sortbuff_size; + memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); + idx= (ha_keys) sort_param->sort_info->max_records; + sort_length= sort_param->key_length; + maxbuffer= 1; + + while (memavl >= MIN_SORT_BUFFER) + { + if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= + (my_off_t) memavl) + keys= idx+1; + else if ((sort_param->sort_info->param->testflag & + (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == + T_FORCE_SORT_MEMORY) + { + /* + Use all of the given sort buffer for key data. + Allocate 1000 buffers at a start for new data. More buffers + will be allocated when needed. + */ + keys= memavl / (sort_length+sizeof(char*)); + maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); + } + else + { + uint maxbuffer_org; + do + { + maxbuffer_org= maxbuffer; + if (memavl < sizeof(BUFFPEK)*maxbuffer || + (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ + (sort_length+sizeof(char*))) <= 1 || + keys < (uint) maxbuffer) + { + mysql_mutex_lock(&sort_param->sort_info->mutex); + mi_check_print_error(sort_param->sort_info->param, + "myisam_sort_buffer_size is too small. Current " + "myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_RETURN(TRUE); + } + } + while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); + } + if ((sort_keys= my_malloc(keys * (sort_length + sizeof(char *)) + + ((sort_param->keyinfo->flag & HA_FULLTEXT) ? + HA_FT_MAXBYTELEN : 0), MYF(0)))) + { + if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), + maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) + { + my_free(sort_keys); + sort_keys= NULL; /* Safety against double free on error. */ + } + else + break; + } + old_memavl= memavl; + if ((memavl= memavl / 4 * 3) < MIN_SORT_BUFFER && + old_memavl > MIN_SORT_BUFFER) + memavl= MIN_SORT_BUFFER; + } + if (memavl < MIN_SORT_BUFFER) + { + /* purecov: begin inspected */ + mysql_mutex_lock(&sort_param->sort_info->mutex); + mi_check_print_error(sort_param->sort_info->param, + "myisam_sort_buffer_size is too small. Current " + "myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + my_errno= ENOMEM; + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_RETURN(TRUE); + /* purecov: end inspected */ + } + + if (sort_param->sort_info->param->testflag & T_VERBOSE) + my_fprintf(stdout, + "Key %d - Allocating buffer for %llu keys\n", + sort_param->key + 1, (ulonglong) keys); + sort_param->sort_keys= sort_keys; + + idx= error= 0; + sort_keys[0]= (uchar*) (sort_keys+keys); + + DBUG_PRINT("info", ("reading keys")); + while (!(error= sort_param->sort_info->got_error) && + !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) + { + if (sort_param->real_key_length > sort_param->key_length) + { + if (write_key(sort_param, sort_keys[idx], + &sort_param->tempfile_for_exceptions)) + goto err; + continue; + } + + if (++idx == keys) + { + if (sort_param->write_keys(sort_param, sort_keys, idx - 1, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) + goto err; + sort_keys[0]= (uchar*) (sort_keys+keys); + memcpy(sort_keys[0], sort_keys[idx - 1], (size_t) sort_param->key_length); + idx= 1; + } + sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; + } + if (error > 0) + goto err; + if (sort_param->buffpek.elements) + { + if (sort_param->write_keys(sort_param, sort_keys, idx, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) + goto err; + sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; + } + else + sort_param->keys= idx; + + DBUG_RETURN(FALSE); + +err: + DBUG_PRINT("error", ("got some error")); + sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ + my_free(sort_keys); + sort_param->sort_keys= 0; + delete_dynamic(& sort_param->buffpek); + close_cached_file(&sort_param->tempfile); + close_cached_file(&sort_param->tempfile_for_exceptions); + + DBUG_RETURN(TRUE); +} + /* Search after all keys and place them in a temp. file */ pthread_handler_t thr_find_all_keys(void *arg) { MI_SORT_PARAM *sort_param= (MI_SORT_PARAM*) arg; - int error; - ulonglong memavl, old_memavl, sortbuff_size; - ha_keys UNINIT_VAR(keys), idx; - uint sort_length; - uint maxbuffer; - uchar **sort_keys=0; - - error=1; - + my_bool error= FALSE; + /* If my_thread_init fails */ if (my_thread_init()) - goto err; + error= TRUE; - { /* Add extra block since DBUG_ENTER declare variables */ - DBUG_ENTER("thr_find_all_keys"); - DBUG_PRINT("enter", ("master: %d", sort_param->master)); - if (sort_param->sort_info->got_error) - goto err; + if (error || thr_find_all_keys_exec(sort_param)) + error= TRUE; - set_sort_param_read_write(sort_param); + /* + Thread must clean up after itself. + */ + free_root(&sort_param->wordroot, MYF(0)); + /* + Detach from the share if the writer is involved. Avoid others to + be blocked. This includes a flush of the write buffer. This will + also indicate EOF to the readers. + That means that a writer always gets here first and readers - + only when they see EOF. But if a reader finishes prematurely + because of an error it may reach this earlier - don't allow it + to detach the writer thread. + */ + if (sort_param->master && sort_param->sort_info->info->rec_cache.share) + remove_io_thread(&sort_param->sort_info->info->rec_cache); - my_b_clear(&sort_param->tempfile); - my_b_clear(&sort_param->tempfile_for_exceptions); - bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); - bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); - sort_keys= (uchar **) NULL; + /* Readers detach from the share if any. Avoid others to be blocked. */ + if (sort_param->read_cache.share) + remove_io_thread(&sort_param->read_cache); - sortbuff_size= sort_param->sortbuff_size; - memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); - idx= (ha_keys) sort_param->sort_info->max_records; - sort_length= sort_param->key_length; - maxbuffer= 1; + mysql_mutex_lock(&sort_param->sort_info->mutex); + if (error) + sort_param->sort_info->got_error= 1; - while (memavl >= MIN_SORT_BUFFER) - { - if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= - (my_off_t) memavl) - keys= idx+1; - else if ((sort_param->sort_info->param->testflag & - (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == - T_FORCE_SORT_MEMORY) - { - /* - Use all of the given sort buffer for key data. - Allocate 1000 buffers at a start for new data. More buffers - will be allocated when needed. - */ - keys= memavl / (sort_length+sizeof(char*)); - maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); - } - else - { - uint maxbuffer_org; - do - { - maxbuffer_org= maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer || - (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (sort_length+sizeof(char*))) <= 1 || - keys < (uint) maxbuffer) - { - mi_check_print_error(sort_param->sort_info->param, - "myisam_sort_buffer_size is too small. Current myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - goto err; - } - } - while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); - } - if ((sort_keys= (uchar**) - my_malloc(keys*(sort_length+sizeof(char*))+ - ((sort_param->keyinfo->flag & HA_FULLTEXT) ? - HA_FT_MAXBYTELEN : 0), MYF(0)))) - { - if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), - maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) - { - my_free(sort_keys); - sort_keys= (uchar **) NULL; /* for err: label */ - } - else - break; - } - old_memavl= memavl; - if ((memavl= memavl / 4 * 3) < MIN_SORT_BUFFER && - old_memavl > MIN_SORT_BUFFER) - memavl= MIN_SORT_BUFFER; - } - if (memavl < MIN_SORT_BUFFER) - { - /* purecov: begin inspected */ - mi_check_print_error(sort_param->sort_info->param, - "myisam_sort_buffer_size is too small. Current myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - my_errno= ENOMEM; - goto err; - /* purecov: end inspected */ - } - - if (sort_param->sort_info->param->testflag & T_VERBOSE) - my_fprintf(stdout, - "Key %d - Allocating buffer for %llu keys\n", - sort_param->key + 1, (ulonglong) keys); - sort_param->sort_keys= sort_keys; - - idx= error= 0; - sort_keys[0]= (uchar*) (sort_keys+keys); - - DBUG_PRINT("info", ("reading keys")); - while (!(error= sort_param->sort_info->got_error) && - !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) - { - if (sort_param->real_key_length > sort_param->key_length) - { - if (write_key(sort_param, sort_keys[idx], - &sort_param->tempfile_for_exceptions)) - goto err; - continue; - } - - if (++idx == keys) - { - if (sort_param->write_keys(sort_param, sort_keys, idx - 1, - (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), - &sort_param->tempfile)) - goto err; - sort_keys[0]= (uchar*) (sort_keys+keys); - memcpy(sort_keys[0], sort_keys[idx - 1], - (size_t) sort_param->key_length); - idx= 1; - } - sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; - } - if (error > 0) - goto err; - if (sort_param->buffpek.elements) - { - if (sort_param->write_keys(sort_param, sort_keys, idx, - (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), - &sort_param->tempfile)) - goto err; - sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; - } - else - sort_param->keys= idx; - - goto ok; - -err: - DBUG_PRINT("error", ("got some error")); - sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ - my_free(sort_keys); - sort_param->sort_keys= 0; - delete_dynamic(& sort_param->buffpek); - close_cached_file(&sort_param->tempfile); - close_cached_file(&sort_param->tempfile_for_exceptions); - -ok: - free_root(&sort_param->wordroot, MYF(0)); - /* - Detach from the share if the writer is involved. Avoid others to - be blocked. This includes a flush of the write buffer. This will - also indicate EOF to the readers. - That means that a writer always gets here first and readers - - only when they see EOF. But if a reader finishes prematurely - because of an error it may reach this earlier - don't allow it - to detach the writer thread. - */ - if (sort_param->master && sort_param->sort_info->info->rec_cache.share) - remove_io_thread(&sort_param->sort_info->info->rec_cache); - - /* Readers detach from the share if any. Avoid others to be blocked. */ - if (sort_param->read_cache.share) - remove_io_thread(&sort_param->read_cache); - - mysql_mutex_lock(&sort_param->sort_info->mutex); - if (!--sort_param->sort_info->threads_running) - mysql_cond_signal(&sort_param->sort_info->cond); - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_PRINT("exit", ("======== ending thread ========")); - DBUG_LEAVE; - } + if (!--sort_param->sort_info->threads_running) + mysql_cond_signal(&sort_param->sort_info->cond); + mysql_mutex_unlock(&sort_param->sort_info->mutex); my_thread_end(); return NULL; } From 6d3ffd2e3a76873acb4f232f52ccca6a75bf737d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 11 Dec 2015 09:16:42 +0200 Subject: [PATCH 20/83] Fixed a crash during stacktrace printing if addr2line failed to start. In order to get all the input from addr2line we must read in a loop, until the response is complete. Also, in case that the response is malformed, we must not end up reading invalid memory. --- mysys/my_addr_resolve.c | 68 ++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 9c9a7d9b97c..f9f40bc6ba5 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -145,30 +145,68 @@ static int initialized= 0; static char output[1024]; int my_addr_resolve(void *ptr, my_addr_loc *loc) { - char input[32], *s; + char input[32]; size_t len; len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); if (write(in[1], input, len) <= 0) return 1; - if (read(out[0], output, sizeof(output)) <= 0) - return 1; - loc->func= s= output; - while (*s != '\n') - s++; - *s++= 0; - loc->file= s; - while (*s != ':') - s++; - *s++= 0; + ssize_t total_bytes_read = 0; + ssize_t extra_bytes_read = 0; + + fd_set set; + struct timeval timeout; + FD_ZERO(&set); + FD_SET(out[0], &set); + + /* 10 ms should be plenty of time for addr2line to issue a response. */ + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + /* Read in a loop till all the output from addr2line is complete. */ + while (select(out[0] + 1, &set, NULL, NULL, &timeout) > 0) + { + extra_bytes_read= read(out[0], output + total_bytes_read, + sizeof(output) - total_bytes_read); + if (extra_bytes_read < 0) + return 1; + total_bytes_read += extra_bytes_read; + } + + /* Failed starting addr2line. */ + if (total_bytes_read == 0) + return 1; + + int filename_start = -1; + int line_number_start = -1; + /* Go through the addr2line response and get the required data. + The response is structured in 2 lnes. The first line contains the function + name, while the second one contains : */ + for (ssize_t i = 0; i < total_bytes_read; i++) { + if (output[i] == '\n') { + filename_start = i + 1; + output[i] = '\0'; + } + if (filename_start != -1 && output[i] == ':') { + line_number_start = i + 1; + output[i] = '\0'; + } + if (line_number_start != -1) { + loc->line= atoi(output + line_number_start); + break; + } + } + /* Response is malformed. */ + if (filename_start == -1 || line_number_start == -1) + return 1; + + loc->func= output; + loc->file= output + filename_start; + + /* Addr2line was unable to extract any meaningful information. */ if (strcmp(loc->file, "??") == 0) return 1; - loc->line= 0; - while (isdigit(*s)) - loc->line = loc->line * 10 + (*s++ - '0'); - *s = 0; loc->file= strip_path(loc->file); return 0; From b2bd10d4d614924ff22746ce3a49b4e5f755ddc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Sun, 17 Jan 2016 22:23:21 +0200 Subject: [PATCH 21/83] [MDEV-9427] Server does not build on OpenSUSE 42.1 Changed code to comply to C90 standard. --- mysys/my_addr_resolve.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index f9f40bc6ba5..d3d445ee95c 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -148,10 +148,6 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) char input[32]; size_t len; - len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); - if (write(in[1], input, len) <= 0) - return 1; - ssize_t total_bytes_read = 0; ssize_t extra_bytes_read = 0; @@ -160,6 +156,14 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) FD_ZERO(&set); FD_SET(out[0], &set); + int filename_start = -1; + int line_number_start = -1; + ssize_t i; + + len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); + if (write(in[1], input, len) <= 0) + return 1; + /* 10 ms should be plenty of time for addr2line to issue a response. */ timeout.tv_sec = 0; timeout.tv_usec = 10000; @@ -177,12 +181,10 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) if (total_bytes_read == 0) return 1; - int filename_start = -1; - int line_number_start = -1; /* Go through the addr2line response and get the required data. The response is structured in 2 lnes. The first line contains the function name, while the second one contains : */ - for (ssize_t i = 0; i < total_bytes_read; i++) { + for (i = 0; i < total_bytes_read; i++) { if (output[i] == '\n') { filename_start = i + 1; output[i] = '\0'; From 275f7d7d828c117a3e1ad7b1f5932ce2edaa09ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Sun, 17 Jan 2016 22:33:36 +0200 Subject: [PATCH 22/83] Remove warning in my_addr_resolve ISO C90 does not allow mixed declarations and code. --- mysys/my_addr_resolve.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index d3d445ee95c..0b1c60c4131 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -153,13 +153,14 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) fd_set set; struct timeval timeout; - FD_ZERO(&set); - FD_SET(out[0], &set); int filename_start = -1; int line_number_start = -1; ssize_t i; + FD_ZERO(&set); + FD_SET(out[0], &set); + len= my_snprintf(input, sizeof(input), "%p\n", ptr - offset); if (write(in[1], input, len) <= 0) return 1; From acc837981fc31a3f5f28c392d6f821f0929b12a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Sun, 17 Jan 2016 22:57:27 +0200 Subject: [PATCH 23/83] Revert "Fixed compilation failure on MacOSX" This reverts commit df32495c85d75b736ee8d251d07b70b3682dda4d. --- include/myisam.h | 3 +- storage/maria/ma_sort.c | 367 ++++++++++++++++++-------------------- storage/maria/maria_def.h | 3 +- storage/myisam/sort.c | 366 ++++++++++++++++++------------------- 4 files changed, 351 insertions(+), 388 deletions(-) diff --git a/include/myisam.h b/include/myisam.h index f7a3ae8cc8a..88ce401fabc 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -331,8 +331,7 @@ typedef struct st_sort_info my_off_t filelength, dupp, buff_length; ha_rows max_records; uint current_key, total_keys; - volatile uint got_error; - uint threads_running; + uint got_error, threads_running; myf myf_rw; enum data_file_type new_data_file_type; } MI_SORT_INFO; diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c index 19dcd17cd08..ef6e8506ac6 100644 --- a/storage/maria/ma_sort.c +++ b/storage/maria/ma_sort.c @@ -364,211 +364,192 @@ err: } /* find_all_keys */ -static my_bool _ma_thr_find_all_keys_exec(MARIA_SORT_PARAM* sort_param) -{ - DBUG_ENTER("_ma_thr_find_all_keys"); - DBUG_PRINT("enter", ("master: %d", sort_param->master)); - - my_bool error= FALSE; - if (sort_param->sort_info->got_error) - error= TRUE; - if (error) - DBUG_RETURN(error); - - set_sort_param_read_write(sort_param); - - ulonglong memavl, old_memavl, sortbuff_size; - ha_keys UNINIT_VAR(keys), idx; - uint sort_length; - uint maxbuffer; - uchar **sort_keys= NULL; - - my_b_clear(&sort_param->tempfile); - my_b_clear(&sort_param->tempfile_for_exceptions); - bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); - bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); - - - sortbuff_size= sort_param->sortbuff_size; - memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); - idx= (ha_keys) sort_param->sort_info->max_records; - sort_length= sort_param->key_length; - maxbuffer= 1; - - while (memavl >= MIN_SORT_BUFFER) - { - if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= - (my_off_t) memavl) - keys= idx+1; - else if ((sort_param->sort_info->param->testflag & - (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == - T_FORCE_SORT_MEMORY) - { - /* - Use all of the given sort buffer for key data. - Allocate 1000 buffers at a start for new data. More buffers - will be allocated when needed. - */ - keys= memavl / (sort_length+sizeof(char*)); - maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); - } - else - { - uint maxbuffer_org; - do - { - maxbuffer_org= maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer || - (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (sort_length+sizeof(char*))) <= 1 || - keys < (uint) maxbuffer) - { - mysql_mutex_lock(&sort_param->sort_info->mutex); - _ma_check_print_error(sort_param->sort_info->param, - "aria_sort_buffer_size is too small. Current " - "aria_sort_buffer_size: %llu rows: %llu " - "sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_RETURN(TRUE); - } - } - while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); - } - if ((sort_keys= (uchar **) - my_malloc(keys*(sort_length+sizeof(char*))+ - ((sort_param->keyinfo->flag & HA_FULLTEXT) ? - HA_FT_MAXBYTELEN : 0), MYF(0)))) - { - if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), - maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) - { - my_free(sort_keys); - sort_keys= NULL; /* Safety against double free on error. */ - } - else - break; - } - old_memavl= memavl; - if ((memavl= memavl/4*3) < MIN_SORT_MEMORY && - old_memavl > MIN_SORT_MEMORY) - memavl= MIN_SORT_MEMORY; - } - if (memavl < MIN_SORT_MEMORY) - { - /* purecov: begin inspected */ - mysql_mutex_lock(&sort_param->sort_info->mutex); - _ma_check_print_error(sort_param->sort_info->param, - "aria_sort_buffer_size is too small. Current " - "aria_sort_buffer_size: %llu rows: %llu " - "sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - my_errno= ENOMEM; - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_RETURN(TRUE); - /* purecov: end inspected */ - } - if (sort_param->sort_info->param->testflag & T_VERBOSE) - my_fprintf(stdout, - "Key %d - Allocating buffer for %llu keys\n", - sort_param->key + 1, (ulonglong) keys); - sort_param->sort_keys= sort_keys; - - idx= error= 0; - sort_keys[0]= (uchar*) (sort_keys+keys); - - DBUG_PRINT("info", ("reading keys")); - while (!(error= sort_param->sort_info->got_error) && - !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) - { - if (sort_param->real_key_length > sort_param->key_length) - { - if (write_key(sort_param, sort_keys[idx], - &sort_param->tempfile_for_exceptions)) - goto err; - continue; - } - - if (++idx == keys) - { - if (sort_param->write_keys(sort_param, sort_keys, idx - 1, - (BUFFPEK *)alloc_dynamic(&sort_param->buffpek), - &sort_param->tempfile)) - goto err; - sort_keys[0]= (uchar*) (sort_keys+keys); - memcpy(sort_keys[0], sort_keys[idx - 1], (size_t) sort_param->key_length); - idx= 1; - } - sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; - } - if (error > 0) - goto err; - if (sort_param->buffpek.elements) - { - if (sort_param->write_keys(sort_param,sort_keys, idx, - (BUFFPEK *) alloc_dynamic(&sort_param-> - buffpek), - &sort_param->tempfile)) - goto err; - sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; - } - else - sort_param->keys= idx; - - DBUG_RETURN(FALSE); - -err: - DBUG_PRINT("error", ("got some error")); - my_free(sort_keys); - sort_param->sort_keys= 0; - delete_dynamic(& sort_param->buffpek); - close_cached_file(&sort_param->tempfile); - close_cached_file(&sort_param->tempfile_for_exceptions); - - DBUG_RETURN(TRUE); -} - /* Search after all keys and place them in a temp. file */ pthread_handler_t _ma_thr_find_all_keys(void *arg) { MARIA_SORT_PARAM *sort_param= (MARIA_SORT_PARAM*) arg; - my_bool error= FALSE; - /* If my_thread_init fails */ + int error; + size_t memavl, old_memavl; + longlong sortbuff_size; + ha_keys UNINIT_VAR(keys), idx; + uint sort_length; + uint maxbuffer; + uchar **sort_keys=0; + + error=1; + if (my_thread_init()) - error= TRUE; + goto err; - if (error || _ma_thr_find_all_keys_exec(sort_param)) - error= TRUE; + { /* Add extra block since DBUG_ENTER declare variables */ + DBUG_ENTER("_ma_thr_find_all_keys"); + DBUG_PRINT("enter", ("master: %d", sort_param->master)); + if (sort_param->sort_info->got_error) + goto err; - /* - Thread must clean up after itself. - */ - free_root(&sort_param->wordroot, MYF(0)); - /* - Detach from the share if the writer is involved. Avoid others to - be blocked. This includes a flush of the write buffer. This will - also indicate EOF to the readers. - That means that a writer always gets here first and readers - - only when they see EOF. But if a reader finishes prematurely - because of an error it may reach this earlier - don't allow it - to detach the writer thread. - */ - if (sort_param->master && sort_param->sort_info->info->rec_cache.share) - remove_io_thread(&sort_param->sort_info->info->rec_cache); + set_sort_param_read_write(sort_param); - /* Readers detach from the share if any. Avoid others to be blocked. */ - if (sort_param->read_cache.share) - remove_io_thread(&sort_param->read_cache); + my_b_clear(&sort_param->tempfile); + my_b_clear(&sort_param->tempfile_for_exceptions); + bzero((char*) &sort_param->buffpek,sizeof(sort_param->buffpek)); + bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); - mysql_mutex_lock(&sort_param->sort_info->mutex); - if (error) - sort_param->sort_info->got_error= 1; + sortbuff_size= sort_param->sortbuff_size; + memavl= MY_MAX(sortbuff_size, MIN_SORT_MEMORY); + idx= (ha_keys) sort_param->sort_info->max_records; + sort_length= sort_param->key_length; + maxbuffer= 1; - if (!--sort_param->sort_info->threads_running) - mysql_cond_signal(&sort_param->sort_info->cond); - mysql_mutex_unlock(&sort_param->sort_info->mutex); + while (memavl >= MIN_SORT_MEMORY) + { + if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= (my_off_t) memavl) + keys= idx+1; + else if ((sort_param->sort_info->param->testflag & + (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == + T_FORCE_SORT_MEMORY) + { + /* + Use all of the given sort buffer for key data. + Allocate 1000 buffers at a start for new data. More buffers + will be allocated when needed. + */ + keys= memavl / (sort_length+sizeof(char*)); + maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); + } + else + { + uint maxbuffer_org; + do + { + maxbuffer_org= maxbuffer; + if (memavl < sizeof(BUFFPEK)*maxbuffer || + (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ + (sort_length+sizeof(char*))) <= 1 || + keys < maxbuffer) + { + _ma_check_print_error(sort_param->sort_info->param, + "aria_sort_buffer_size is too small. Current aria_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + goto err; + } + } + while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); + } + if ((sort_keys= (uchar **) + my_malloc(keys*(sort_length+sizeof(char*))+ + ((sort_param->keyinfo->flag & HA_FULLTEXT) ? + HA_FT_MAXBYTELEN : 0), MYF(0)))) + { + if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), + maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) + { + my_free(sort_keys); + sort_keys= (uchar **) NULL; /* for err: label */ + } + else + break; + } + old_memavl= memavl; + if ((memavl= memavl/4*3) < MIN_SORT_MEMORY && + old_memavl > MIN_SORT_MEMORY) + memavl= MIN_SORT_MEMORY; + } + if (memavl < MIN_SORT_MEMORY) + { + /* purecov: begin inspected */ + _ma_check_print_error(sort_param->sort_info->param, + "aria_sort_buffer_size is too small. Current aria_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + my_errno= ENOMEM; + goto err; + /* purecov: end inspected */ + } + if (sort_param->sort_info->param->testflag & T_VERBOSE) + my_fprintf(stdout, + "Key %d - Allocating buffer for %llu keys\n", + sort_param->key + 1, (ulonglong) keys); + sort_param->sort_keys= sort_keys; + + idx= error= 0; + sort_keys[0]= (uchar*) (sort_keys+keys); + + DBUG_PRINT("info", ("reading keys")); + while (!(error= sort_param->sort_info->got_error) && + !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) + { + if (sort_param->real_key_length > sort_param->key_length) + { + if (write_key(sort_param, sort_keys[idx], + &sort_param->tempfile_for_exceptions)) + goto err; + continue; + } + + if (++idx == keys) + { + if (sort_param->write_keys(sort_param, sort_keys, idx - 1, + (BUFFPEK *)alloc_dynamic(&sort_param-> + buffpek), + &sort_param->tempfile)) + goto err; + sort_keys[0]= (uchar*) (sort_keys+keys); + memcpy(sort_keys[0], sort_keys[idx - 1], + (size_t) sort_param->key_length); + idx= 1; + } + sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; + } + if (error > 0) + goto err; + if (sort_param->buffpek.elements) + { + if (sort_param->write_keys(sort_param,sort_keys, idx, + (BUFFPEK *) alloc_dynamic(&sort_param-> + buffpek), + &sort_param->tempfile)) + goto err; + sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; + } + else + sort_param->keys= idx; + + goto ok; + +err: + DBUG_PRINT("error", ("got some error")); + sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ + my_free(sort_keys); + sort_param->sort_keys= 0; + delete_dynamic(& sort_param->buffpek); + close_cached_file(&sort_param->tempfile); + close_cached_file(&sort_param->tempfile_for_exceptions); + +ok: + free_root(&sort_param->wordroot, MYF(0)); + /* + Detach from the share if the writer is involved. Avoid others to + be blocked. This includes a flush of the write buffer. This will + also indicate EOF to the readers. + That means that a writer always gets here first and readers - + only when they see EOF. But if a reader finishes prematurely + because of an error it may reach this earlier - don't allow it + to detach the writer thread. + */ + if (sort_param->master && sort_param->sort_info->info->rec_cache.share) + remove_io_thread(&sort_param->sort_info->info->rec_cache); + + /* Readers detach from the share if any. Avoid others to be blocked. */ + if (sort_param->read_cache.share) + remove_io_thread(&sort_param->read_cache); + + mysql_mutex_lock(&sort_param->sort_info->mutex); + if (!--sort_param->sort_info->threads_running) + mysql_cond_signal(&sort_param->sort_info->cond); + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_PRINT("exit", ("======== ending thread ========")); + } my_thread_end(); return NULL; } diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index fa7b954f85f..676927b119a 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -67,8 +67,7 @@ typedef struct st_maria_sort_info pgcache_page_no_t page; ha_rows max_records; uint current_key, total_keys; - volatile uint got_error; - uint threads_running; + uint got_error, threads_running; myf myf_rw; enum data_file_type new_data_file_type, org_data_file_type; } MARIA_SORT_INFO; diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index f6e9bfe1565..f490e5f0b44 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -344,208 +344,192 @@ static ha_rows find_all_keys(MI_SORT_PARAM *info, ha_rows keys, DBUG_RETURN((*maxbuffer)*(keys-1)+idx); } /* find_all_keys */ -static my_bool thr_find_all_keys_exec(MI_SORT_PARAM *sort_param) -{ - DBUG_ENTER("thr_find_all_keys"); - DBUG_PRINT("enter", ("master: %d", sort_param->master)); - - my_bool error= FALSE; - if (sort_param->sort_info->got_error) - error= TRUE; - if (error) - DBUG_RETURN(error); - - set_sort_param_read_write(sort_param); - - ulonglong memavl, old_memavl, sortbuff_size; - ha_keys UNINIT_VAR(keys), idx; - uint sort_length; - uint maxbuffer; - uchar **sort_keys= NULL; - - my_b_clear(&sort_param->tempfile); - my_b_clear(&sort_param->tempfile_for_exceptions); - bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); - bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); - - - sortbuff_size= sort_param->sortbuff_size; - memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); - idx= (ha_keys) sort_param->sort_info->max_records; - sort_length= sort_param->key_length; - maxbuffer= 1; - - while (memavl >= MIN_SORT_BUFFER) - { - if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= - (my_off_t) memavl) - keys= idx+1; - else if ((sort_param->sort_info->param->testflag & - (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == - T_FORCE_SORT_MEMORY) - { - /* - Use all of the given sort buffer for key data. - Allocate 1000 buffers at a start for new data. More buffers - will be allocated when needed. - */ - keys= memavl / (sort_length+sizeof(char*)); - maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); - } - else - { - uint maxbuffer_org; - do - { - maxbuffer_org= maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer || - (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (sort_length+sizeof(char*))) <= 1 || - keys < (uint) maxbuffer) - { - mysql_mutex_lock(&sort_param->sort_info->mutex); - mi_check_print_error(sort_param->sort_info->param, - "myisam_sort_buffer_size is too small. Current " - "myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_RETURN(TRUE); - } - } - while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); - } - if ((sort_keys= my_malloc(keys * (sort_length + sizeof(char *)) + - ((sort_param->keyinfo->flag & HA_FULLTEXT) ? - HA_FT_MAXBYTELEN : 0), MYF(0)))) - { - if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), - maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) - { - my_free(sort_keys); - sort_keys= NULL; /* Safety against double free on error. */ - } - else - break; - } - old_memavl= memavl; - if ((memavl= memavl / 4 * 3) < MIN_SORT_BUFFER && - old_memavl > MIN_SORT_BUFFER) - memavl= MIN_SORT_BUFFER; - } - if (memavl < MIN_SORT_BUFFER) - { - /* purecov: begin inspected */ - mysql_mutex_lock(&sort_param->sort_info->mutex); - mi_check_print_error(sort_param->sort_info->param, - "myisam_sort_buffer_size is too small. Current " - "myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", - sortbuff_size, (ulonglong) idx, sort_length); - my_errno= ENOMEM; - mysql_mutex_unlock(&sort_param->sort_info->mutex); - DBUG_RETURN(TRUE); - /* purecov: end inspected */ - } - - if (sort_param->sort_info->param->testflag & T_VERBOSE) - my_fprintf(stdout, - "Key %d - Allocating buffer for %llu keys\n", - sort_param->key + 1, (ulonglong) keys); - sort_param->sort_keys= sort_keys; - - idx= error= 0; - sort_keys[0]= (uchar*) (sort_keys+keys); - - DBUG_PRINT("info", ("reading keys")); - while (!(error= sort_param->sort_info->got_error) && - !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) - { - if (sort_param->real_key_length > sort_param->key_length) - { - if (write_key(sort_param, sort_keys[idx], - &sort_param->tempfile_for_exceptions)) - goto err; - continue; - } - - if (++idx == keys) - { - if (sort_param->write_keys(sort_param, sort_keys, idx - 1, - (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), - &sort_param->tempfile)) - goto err; - sort_keys[0]= (uchar*) (sort_keys+keys); - memcpy(sort_keys[0], sort_keys[idx - 1], (size_t) sort_param->key_length); - idx= 1; - } - sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; - } - if (error > 0) - goto err; - if (sort_param->buffpek.elements) - { - if (sort_param->write_keys(sort_param, sort_keys, idx, - (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), - &sort_param->tempfile)) - goto err; - sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; - } - else - sort_param->keys= idx; - - DBUG_RETURN(FALSE); - -err: - DBUG_PRINT("error", ("got some error")); - sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ - my_free(sort_keys); - sort_param->sort_keys= 0; - delete_dynamic(& sort_param->buffpek); - close_cached_file(&sort_param->tempfile); - close_cached_file(&sort_param->tempfile_for_exceptions); - - DBUG_RETURN(TRUE); -} - /* Search after all keys and place them in a temp. file */ pthread_handler_t thr_find_all_keys(void *arg) { MI_SORT_PARAM *sort_param= (MI_SORT_PARAM*) arg; - my_bool error= FALSE; - /* If my_thread_init fails */ + int error; + ulonglong memavl, old_memavl, sortbuff_size; + ha_keys UNINIT_VAR(keys), idx; + uint sort_length; + uint maxbuffer; + uchar **sort_keys=0; + + error=1; + if (my_thread_init()) - error= TRUE; + goto err; - if (error || thr_find_all_keys_exec(sort_param)) - error= TRUE; + { /* Add extra block since DBUG_ENTER declare variables */ + DBUG_ENTER("thr_find_all_keys"); + DBUG_PRINT("enter", ("master: %d", sort_param->master)); + if (sort_param->sort_info->got_error) + goto err; - /* - Thread must clean up after itself. - */ - free_root(&sort_param->wordroot, MYF(0)); - /* - Detach from the share if the writer is involved. Avoid others to - be blocked. This includes a flush of the write buffer. This will - also indicate EOF to the readers. - That means that a writer always gets here first and readers - - only when they see EOF. But if a reader finishes prematurely - because of an error it may reach this earlier - don't allow it - to detach the writer thread. - */ - if (sort_param->master && sort_param->sort_info->info->rec_cache.share) - remove_io_thread(&sort_param->sort_info->info->rec_cache); + set_sort_param_read_write(sort_param); - /* Readers detach from the share if any. Avoid others to be blocked. */ - if (sort_param->read_cache.share) - remove_io_thread(&sort_param->read_cache); + my_b_clear(&sort_param->tempfile); + my_b_clear(&sort_param->tempfile_for_exceptions); + bzero((char*) &sort_param->buffpek, sizeof(sort_param->buffpek)); + bzero((char*) &sort_param->unique, sizeof(sort_param->unique)); + sort_keys= (uchar **) NULL; - mysql_mutex_lock(&sort_param->sort_info->mutex); - if (error) - sort_param->sort_info->got_error= 1; + sortbuff_size= sort_param->sortbuff_size; + memavl= MY_MAX(sortbuff_size, MIN_SORT_BUFFER); + idx= (ha_keys) sort_param->sort_info->max_records; + sort_length= sort_param->key_length; + maxbuffer= 1; - if (!--sort_param->sort_info->threads_running) - mysql_cond_signal(&sort_param->sort_info->cond); - mysql_mutex_unlock(&sort_param->sort_info->mutex); + while (memavl >= MIN_SORT_BUFFER) + { + if ((my_off_t) (idx+1)*(sort_length+sizeof(char*)) <= + (my_off_t) memavl) + keys= idx+1; + else if ((sort_param->sort_info->param->testflag & + (T_FORCE_SORT_MEMORY | T_CREATE_MISSING_KEYS)) == + T_FORCE_SORT_MEMORY) + { + /* + Use all of the given sort buffer for key data. + Allocate 1000 buffers at a start for new data. More buffers + will be allocated when needed. + */ + keys= memavl / (sort_length+sizeof(char*)); + maxbuffer= (uint) MY_MIN((ulonglong) 1000, (idx / keys)+1); + } + else + { + uint maxbuffer_org; + do + { + maxbuffer_org= maxbuffer; + if (memavl < sizeof(BUFFPEK)*maxbuffer || + (keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ + (sort_length+sizeof(char*))) <= 1 || + keys < (uint) maxbuffer) + { + mi_check_print_error(sort_param->sort_info->param, + "myisam_sort_buffer_size is too small. Current myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + goto err; + } + } + while ((maxbuffer= (uint) (idx/(keys-1)+1)) != maxbuffer_org); + } + if ((sort_keys= (uchar**) + my_malloc(keys*(sort_length+sizeof(char*))+ + ((sort_param->keyinfo->flag & HA_FULLTEXT) ? + HA_FT_MAXBYTELEN : 0), MYF(0)))) + { + if (my_init_dynamic_array(&sort_param->buffpek, sizeof(BUFFPEK), + maxbuffer, MY_MIN(maxbuffer/2, 1000), MYF(0))) + { + my_free(sort_keys); + sort_keys= (uchar **) NULL; /* for err: label */ + } + else + break; + } + old_memavl= memavl; + if ((memavl= memavl / 4 * 3) < MIN_SORT_BUFFER && + old_memavl > MIN_SORT_BUFFER) + memavl= MIN_SORT_BUFFER; + } + if (memavl < MIN_SORT_BUFFER) + { + /* purecov: begin inspected */ + mi_check_print_error(sort_param->sort_info->param, + "myisam_sort_buffer_size is too small. Current myisam_sort_buffer_size: %llu rows: %llu sort_length: %u", + sortbuff_size, (ulonglong) idx, sort_length); + my_errno= ENOMEM; + goto err; + /* purecov: end inspected */ + } + + if (sort_param->sort_info->param->testflag & T_VERBOSE) + my_fprintf(stdout, + "Key %d - Allocating buffer for %llu keys\n", + sort_param->key + 1, (ulonglong) keys); + sort_param->sort_keys= sort_keys; + + idx= error= 0; + sort_keys[0]= (uchar*) (sort_keys+keys); + + DBUG_PRINT("info", ("reading keys")); + while (!(error= sort_param->sort_info->got_error) && + !(error= (*sort_param->key_read)(sort_param, sort_keys[idx]))) + { + if (sort_param->real_key_length > sort_param->key_length) + { + if (write_key(sort_param, sort_keys[idx], + &sort_param->tempfile_for_exceptions)) + goto err; + continue; + } + + if (++idx == keys) + { + if (sort_param->write_keys(sort_param, sort_keys, idx - 1, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) + goto err; + sort_keys[0]= (uchar*) (sort_keys+keys); + memcpy(sort_keys[0], sort_keys[idx - 1], + (size_t) sort_param->key_length); + idx= 1; + } + sort_keys[idx]= sort_keys[idx - 1] + sort_param->key_length; + } + if (error > 0) + goto err; + if (sort_param->buffpek.elements) + { + if (sort_param->write_keys(sort_param, sort_keys, idx, + (BUFFPEK*) alloc_dynamic(&sort_param->buffpek), + &sort_param->tempfile)) + goto err; + sort_param->keys= (sort_param->buffpek.elements - 1) * (keys - 1) + idx; + } + else + sort_param->keys= idx; + + goto ok; + +err: + DBUG_PRINT("error", ("got some error")); + sort_param->sort_info->got_error= 1; /* no need to protect with a mutex */ + my_free(sort_keys); + sort_param->sort_keys= 0; + delete_dynamic(& sort_param->buffpek); + close_cached_file(&sort_param->tempfile); + close_cached_file(&sort_param->tempfile_for_exceptions); + +ok: + free_root(&sort_param->wordroot, MYF(0)); + /* + Detach from the share if the writer is involved. Avoid others to + be blocked. This includes a flush of the write buffer. This will + also indicate EOF to the readers. + That means that a writer always gets here first and readers - + only when they see EOF. But if a reader finishes prematurely + because of an error it may reach this earlier - don't allow it + to detach the writer thread. + */ + if (sort_param->master && sort_param->sort_info->info->rec_cache.share) + remove_io_thread(&sort_param->sort_info->info->rec_cache); + + /* Readers detach from the share if any. Avoid others to be blocked. */ + if (sort_param->read_cache.share) + remove_io_thread(&sort_param->read_cache); + + mysql_mutex_lock(&sort_param->sort_info->mutex); + if (!--sort_param->sort_info->threads_running) + mysql_cond_signal(&sort_param->sort_info->cond); + mysql_mutex_unlock(&sort_param->sort_info->mutex); + DBUG_PRINT("exit", ("======== ending thread ========")); + DBUG_LEAVE; + } my_thread_end(); return NULL; } From e7a89b46d4ece1351cc60d8bafb8bf90ca9d8cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Sun, 17 Jan 2016 22:57:37 +0200 Subject: [PATCH 24/83] Revert "[Code cleanup] Refactor duplicate code within myisam and maria sort.cc" This reverts commit 727f92fe00ebfa1e81f3cc6c1a73af520f42a0bd. --- storage/maria/ma_sort.c | 62 ++++++++++++++++++++++----------------- storage/myisam/sort.c | 64 ++++++++++++++++++++++++----------------- 2 files changed, 73 insertions(+), 53 deletions(-) diff --git a/storage/maria/ma_sort.c b/storage/maria/ma_sort.c index ef6e8506ac6..967d4a151be 100644 --- a/storage/maria/ma_sort.c +++ b/storage/maria/ma_sort.c @@ -89,27 +89,6 @@ static int write_merge_key_varlen(MARIA_SORT_PARAM *info, static inline int my_var_write(MARIA_SORT_PARAM *info, IO_CACHE *to_file, uchar *bufs); -/* - Sets the appropriate read and write methods for the MARIA_SORT_PARAM - based on the variable length key flag. -*/ -static void set_sort_param_read_write(MARIA_SORT_PARAM *sort_param) -{ - if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sort_param->write_keys= write_keys_varlen; - sort_param->read_to_buffer= read_to_buffer_varlen; - sort_param->write_key= write_merge_key_varlen; - } - else - { - sort_param->write_keys= write_keys; - sort_param->read_to_buffer= read_to_buffer; - sort_param->write_key= write_merge_key; - } -} - - /* Creates a index of sorted keys @@ -139,7 +118,18 @@ int _ma_create_index_by_sort(MARIA_SORT_PARAM *info, my_bool no_messages, (ulong) sortbuff_size, info->key_length, (ulong) info->sort_info->max_records)); - set_sort_param_read_write(info); + if (info->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + info->write_keys= write_keys_varlen; + info->read_to_buffer= read_to_buffer_varlen; + info->write_key=write_merge_key_varlen; + } + else + { + info->write_keys= write_keys; + info->read_to_buffer=read_to_buffer; + info->write_key=write_merge_key; + } my_b_clear(&tempfile); my_b_clear(&tempfile_for_exceptions); @@ -388,7 +378,18 @@ pthread_handler_t _ma_thr_find_all_keys(void *arg) if (sort_param->sort_info->got_error) goto err; - set_sort_param_read_write(sort_param); + if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sort_param->write_keys= write_keys_varlen; + sort_param->read_to_buffer= read_to_buffer_varlen; + sort_param->write_key= write_merge_key_varlen; + } + else + { + sort_param->write_keys= write_keys; + sort_param->read_to_buffer= read_to_buffer; + sort_param->write_key= write_merge_key; + } my_b_clear(&sort_param->tempfile); my_b_clear(&sort_param->tempfile_for_exceptions); @@ -613,9 +614,18 @@ int _ma_thr_write_keys(MARIA_SORT_PARAM *sort_param) { if (got_error) continue; - - set_sort_param_read_write(sinfo); - + if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sinfo->write_keys=write_keys_varlen; + sinfo->read_to_buffer=read_to_buffer_varlen; + sinfo->write_key=write_merge_key_varlen; + } + else + { + sinfo->write_keys=write_keys; + sinfo->read_to_buffer=read_to_buffer; + sinfo->write_key=write_merge_key; + } if (sinfo->buffpek.elements) { uint maxbuffer=sinfo->buffpek.elements-1; diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index f490e5f0b44..604910e5099 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -86,28 +86,6 @@ static int write_merge_key_varlen(MI_SORT_PARAM *info, static inline int my_var_write(MI_SORT_PARAM *info, IO_CACHE *to_file, uchar *bufs); - -/* - Sets the appropriate read and write methods for the MI_SORT_PARAM - based on the variable length key flag. -*/ -static void set_sort_param_read_write(MI_SORT_PARAM *sort_param) -{ - if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) - { - sort_param->write_keys= write_keys_varlen; - sort_param->read_to_buffer= read_to_buffer_varlen; - sort_param->write_key= write_merge_key_varlen; - } - else - { - sort_param->write_keys= write_keys; - sort_param->read_to_buffer= read_to_buffer; - sort_param->write_key= write_merge_key; - } -} - - /* Creates a index of sorted keys @@ -135,7 +113,18 @@ int _create_index_by_sort(MI_SORT_PARAM *info,my_bool no_messages, DBUG_ENTER("_create_index_by_sort"); DBUG_PRINT("enter",("sort_length: %u", info->key_length)); - set_sort_param_read_write(info); + if (info->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + info->write_keys=write_keys_varlen; + info->read_to_buffer=read_to_buffer_varlen; + info->write_key= write_merge_key_varlen; + } + else + { + info->write_keys=write_keys; + info->read_to_buffer=read_to_buffer; + info->write_key=write_merge_key; + } my_b_clear(&tempfile); my_b_clear(&tempfile_for_exceptions); @@ -344,6 +333,7 @@ static ha_rows find_all_keys(MI_SORT_PARAM *info, ha_rows keys, DBUG_RETURN((*maxbuffer)*(keys-1)+idx); } /* find_all_keys */ + /* Search after all keys and place them in a temp. file */ pthread_handler_t thr_find_all_keys(void *arg) @@ -367,7 +357,18 @@ pthread_handler_t thr_find_all_keys(void *arg) if (sort_param->sort_info->got_error) goto err; - set_sort_param_read_write(sort_param); + if (sort_param->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sort_param->write_keys= write_keys_varlen; + sort_param->read_to_buffer= read_to_buffer_varlen; + sort_param->write_key= write_merge_key_varlen; + } + else + { + sort_param->write_keys= write_keys; + sort_param->read_to_buffer= read_to_buffer; + sort_param->write_key= write_merge_key; + } my_b_clear(&sort_param->tempfile); my_b_clear(&sort_param->tempfile_for_exceptions); @@ -592,9 +593,18 @@ int thr_write_keys(MI_SORT_PARAM *sort_param) { if (got_error) continue; - - set_sort_param_read_write(sinfo); - + if (sinfo->keyinfo->flag & HA_VAR_LENGTH_KEY) + { + sinfo->write_keys=write_keys_varlen; + sinfo->read_to_buffer=read_to_buffer_varlen; + sinfo->write_key=write_merge_key_varlen; + } + else + { + sinfo->write_keys=write_keys; + sinfo->read_to_buffer=read_to_buffer; + sinfo->write_key=write_merge_key; + } if (sinfo->buffpek.elements) { uint maxbuffer=sinfo->buffpek.elements-1; From f602c3938c22c23416104020a908f686032b091b Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 19 Jan 2016 14:19:31 +1100 Subject: [PATCH 25/83] innodbchecksum: add fclose and handle errors --- extra/innochecksum.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc index f3ba4234d2e..1ebcbf198ef 100644 --- a/extra/innochecksum.cc +++ b/extra/innochecksum.cc @@ -694,14 +694,14 @@ int main(int argc, char **argv) if (*filename == '\0') { fprintf(stderr, "Error; File name missing\n"); - goto error; + goto error_out; } /* stat the file to get size and page count */ if (stat(filename, &st)) { fprintf(stderr, "Error; %s cannot be found\n", filename); - goto error; + goto error_out; } size= st.st_size; @@ -711,7 +711,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error; %s cannot be opened", filename); perror(" "); - goto error; + goto error_out; } big_buf = (unsigned char *)malloc(2 * UNIV_PAGE_SIZE_MAX); @@ -719,7 +719,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error; failed to allocate memory\n"); perror(""); - goto error; + goto error_f; } /* Make sure the page is aligned */ @@ -731,7 +731,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error; failed to allocate memory\n"); perror(""); - return 1; + goto error_big_buf; } /* Make sure the page is aligned */ @@ -983,12 +983,17 @@ ok: print_stats(); free(big_xdes); free(big_buf); + fclose(f); my_end(0); exit(0); error: free(big_xdes); +error_big_buf: free(big_buf); +error_f: + fclose(f); +error_out: my_end(0); exit(1); } From 67cf76ad9b17f2825ac8759ead4928ed3d0f9ce7 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 18 Jan 2016 19:30:46 +0100 Subject: [PATCH 26/83] MDEV 4691- address review comments --- cmake/install_macros.cmake | 9 +++--- cmake/plugin.cmake | 29 ++++++++++--------- plugin/auth_dialog/CMakeLists.txt | 2 +- plugin/auth_examples/CMakeLists.txt | 2 +- plugin/auth_gssapi/CMakeLists.txt | 5 +++- plugin/auth_gssapi/README.md | 4 +-- plugin/auth_gssapi/client_plugin.cc | 2 +- plugin/auth_gssapi/cmake/FindGSSAPI.cmake | 24 +++++++++++++-- plugin/auth_gssapi/gssapi_errmsg.cc | 2 +- plugin/auth_gssapi/gssapi_errmsg.h | 2 +- .../mysql-test/auth_gssapi/suite.pm | 8 +++-- plugin/auth_gssapi/server_plugin.cc | 16 +++++++--- plugin/auth_gssapi/server_plugin.h | 2 +- plugin/auth_gssapi/sspi.h | 2 +- plugin/auth_gssapi/sspi_client.cc | 2 +- plugin/auth_gssapi/sspi_errmsg.cc | 2 +- plugin/auth_gssapi/sspi_server.cc | 2 +- win/packaging/CPackWixConfig.cmake | 21 +++++--------- 18 files changed, 83 insertions(+), 53 deletions(-) diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 2680d9ccbb4..00edcc737ce 100644 --- a/cmake/install_macros.cmake +++ b/cmake/install_macros.cmake @@ -44,11 +44,10 @@ FUNCTION (INSTALL_DEBUG_SYMBOLS) ENDIF() set(comp "") - IF(ARG_COMPONENT STREQUAL "Server") - IF(target MATCHES "mysqld" OR type MATCHES "MODULE") - #MESSAGE("PDB: ${targets}") - SET(comp Server) - ENDIF() + + IF(target MATCHES "mysqld" OR type MATCHES "MODULE") + #MESSAGE("PDB: ${targets}") + SET(comp Server) ENDIF() IF(NOT comp MATCHES Server) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 9d24dacc7e1..95cf9e2cd6e 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -19,6 +19,7 @@ INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) # MYSQL_ADD_PLUGIN(plugin_name source1...sourceN # [STORAGE_ENGINE] +# [CLIENT] # [MANDATORY|DEFAULT] # [STATIC_ONLY|DYNAMIC_ONLY] # [MODULE_OUTPUT_NAME module_name] @@ -30,7 +31,7 @@ INCLUDE(${MYSQL_CMAKE_SCRIPT_DIR}/cmake_parse_arguments.cmake) MACRO(MYSQL_ADD_PLUGIN) MYSQL_PARSE_ARGUMENTS(ARG "LINK_LIBRARIES;DEPENDENCIES;MODULE_OUTPUT_NAME;STATIC_OUTPUT_NAME;COMPONENT;CONFIG" - "STORAGE_ENGINE;STATIC_ONLY;MODULE_ONLY;MANDATORY;DEFAULT;DISABLED;RECOMPILE_FOR_EMBEDDED" + "STORAGE_ENGINE;STATIC_ONLY;MODULE_ONLY;MANDATORY;DEFAULT;DISABLED;RECOMPILE_FOR_EMBEDDED;CLIENT" ${ARGN} ) @@ -120,7 +121,7 @@ MACRO(MYSQL_ADD_PLUGIN) # Build either static library or module IF (PLUGIN_${plugin} MATCHES "(STATIC|AUTO|YES)" AND NOT ARG_MODULE_ONLY - AND NOT ARG_DISABLED) + AND NOT ARG_DISABLED AND NOT ARG_CLIENT) IF(CMAKE_GENERATOR MATCHES "Makefiles|Ninja") # If there is a shared library from previous shared build, @@ -188,14 +189,14 @@ MACRO(MYSQL_ADD_PLUGIN) TARGET_LINK_LIBRARIES (${target} mysqlservices ${ARG_LINK_LIBRARIES}) - # Plugin uses symbols defined in mysqld executable. + # Server plugins use symbols defined in mysqld executable. # Some operating systems like Windows and OSX and are pretty strict about # unresolved symbols. Others are less strict and allow unresolved symbols # in shared libraries. On Linux for example, CMake does not even add # executable to the linker command line (it would result into link error). # Thus we skip TARGET_LINK_LIBRARIES on Linux, as it would only generate # an additional dependency. - IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" AND NOT ARG_CLIENT) TARGET_LINK_LIBRARIES (${target} mysqld) ENDIF() ADD_DEPENDENCIES(${target} GenError ${ARG_DEPENDENCIES}) @@ -206,19 +207,21 @@ MACRO(MYSQL_ADD_PLUGIN) IF(ARG_COMPONENT) IF(CPACK_COMPONENTS_ALL AND NOT CPACK_COMPONENTS_ALL MATCHES ${ARG_COMPONENT}) - SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} ${ARG_COMPONENT} PARENT_SCOPE) + + SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} ${ARG_COMPONENT}) + SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} PARENT_SCOPE) SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_REQUIRES "MariaDB" PARENT_SCOPE) - - IF (NOT ARG_CONFIG) - SET(ARG_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/${target}.cnf") - FILE(WRITE ${ARG_CONFIG} "[mariadb]\nplugin-load-add=${ARG_MODULE_OUTPUT_NAME}.so\n") - ENDIF() - INSTALL(FILES ${ARG_CONFIG} COMPONENT ${ARG_COMPONENT} DESTINATION ${INSTALL_SYSCONF2DIR}) - + # workarounds for cmake issues #13248 and #12864: SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_PROVIDES "cmake_bug_13248" PARENT_SCOPE) SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_OBSOLETES "cmake_bug_13248" PARENT_SCOPE) - SET(CPACK_RPM_${ARG_COMPONENT}_USER_FILELIST ${ignored} "%config(noreplace) ${INSTALL_SYSCONF2DIR}/*" PARENT_SCOPE) + + IF(NOT ARG_CLIENT AND NOT ARG_CONFIG AND UNIX) + SET(ARG_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/${target}.cnf") + FILE(WRITE ${ARG_CONFIG} "[mariadb]\nplugin-load-add=${ARG_MODULE_OUTPUT_NAME}.so\n") + INSTALL(FILES ${ARG_CONFIG} COMPONENT ${ARG_COMPONENT} DESTINATION ${INSTALL_SYSCONF2DIR}) + SET(CPACK_RPM_${ARG_COMPONENT}_USER_FILELIST ${ignored} "%config(noreplace) ${INSTALL_SYSCONF2DIR}/*" PARENT_SCOPE) + ENDIF() ENDIF() ELSE() SET(ARG_COMPONENT Server) diff --git a/plugin/auth_dialog/CMakeLists.txt b/plugin/auth_dialog/CMakeLists.txt index a23518060be..7253b2b2f97 100644 --- a/plugin/auth_dialog/CMakeLists.txt +++ b/plugin/auth_dialog/CMakeLists.txt @@ -15,4 +15,4 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA MYSQL_ADD_PLUGIN(dialog dialog.c ${CMAKE_SOURCE_DIR}/libmysql/get_password.c - MODULE_ONLY COMPONENT ClientPlugins) + MODULE_ONLY CLIENT COMPONENT ClientPlugins) diff --git a/plugin/auth_examples/CMakeLists.txt b/plugin/auth_examples/CMakeLists.txt index c7b7e5be62d..d1152227eff 100644 --- a/plugin/auth_examples/CMakeLists.txt +++ b/plugin/auth_examples/CMakeLists.txt @@ -30,4 +30,4 @@ MYSQL_ADD_PLUGIN(qa_auth_client qa_auth_client.c MYSQL_ADD_PLUGIN(auth_0x0100 auth_0x0100.c MODULE_ONLY COMPONENT Test) MYSQL_ADD_PLUGIN(mysql_clear_password clear_password_client.c - MODULE_ONLY COMPONENT ClientPlugins) + MODULE_ONLY CLIENT COMPONENT ClientPlugins) diff --git a/plugin/auth_gssapi/CMakeLists.txt b/plugin/auth_gssapi/CMakeLists.txt index 61649e4845a..f85d0deaea1 100644 --- a/plugin/auth_gssapi/CMakeLists.txt +++ b/plugin/auth_gssapi/CMakeLists.txt @@ -25,9 +25,12 @@ ENDIF () MYSQL_ADD_PLUGIN(auth_gssapi server_plugin.cc ${GSSAPI_SERVER} ${GSSAPI_ERRMSG} - LINK_LIBRARIES ${GSSAPI_LIBS} + LINK_LIBRARIES ${GSSAPI_LIBS} + COMPONENT gssapi-server MODULE_ONLY) MYSQL_ADD_PLUGIN(auth_gssapi_client client_plugin.cc ${GSSAPI_CLIENT} ${GSSAPI_ERRMSG} LINK_LIBRARIES ${GSSAPI_LIBS} + COMPONENT gssapi-client + CLIENT MODULE_ONLY) diff --git a/plugin/auth_gssapi/README.md b/plugin/auth_gssapi/README.md index f20128f1551..7f678400f0d 100644 --- a/plugin/auth_gssapi/README.md +++ b/plugin/auth_gssapi/README.md @@ -11,7 +11,7 @@ environment. ## Server-side preparations on Unix To use the plugin, some preparation need to be done on the server side on Unixes. -MariaDB server will read need access to the Kerberos keytab file, that contains service principal name for the MariaDB server. +MariaDB server will need read access to the Kerberos keytab file, that contains service principal name for the MariaDB server. If you are using **Unix Kerberos KDC (MIT,Heimdal)** @@ -91,7 +91,7 @@ You may also use alternative *short* form of CREATE USER CREATE USER usr1 IDENTIFIED WITH gssapi; ``` -If this syntax is used, realm part is used for comparison +If this syntax is used, realm part is *not* used for comparison thus 'usr1@EXAMPLE.COM', 'usr1@EXAMPLE.CO.UK' and 'mymachine\usr1' will all identify as 'usr1'. #Login as GSSAPI user with command line clients diff --git a/plugin/auth_gssapi/client_plugin.cc b/plugin/auth_gssapi/client_plugin.cc index aac80e04f92..0ab619a08e6 100644 --- a/plugin/auth_gssapi/client_plugin.cc +++ b/plugin/auth_gssapi/client_plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/cmake/FindGSSAPI.cmake b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake index faee4289722..78111fc2368 100644 --- a/plugin/auth_gssapi/cmake/FindGSSAPI.cmake +++ b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake @@ -8,8 +8,28 @@ # Copyright (c) 2006, Pino Toscano, # -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(GSSAPI_LIBS AND GSSAPI_FLAVOR) diff --git a/plugin/auth_gssapi/gssapi_errmsg.cc b/plugin/auth_gssapi/gssapi_errmsg.cc index 29adf607503..8ea4cab5b02 100644 --- a/plugin/auth_gssapi/gssapi_errmsg.cc +++ b/plugin/auth_gssapi/gssapi_errmsg.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/gssapi_errmsg.h b/plugin/auth_gssapi/gssapi_errmsg.h index 786b2f6c0f9..26db8439e04 100644 --- a/plugin/auth_gssapi/gssapi_errmsg.h +++ b/plugin/auth_gssapi/gssapi_errmsg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm index 3ffc6f1d696..50137ca0211 100644 --- a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm @@ -36,11 +36,13 @@ if (!$ENV{'GSSAPI_FULLNAME'} || !$ENV{'GSSAPI_SHORTNAME'}) return "Environment variable GSSAPI_SHORTNAME and GSSAPI_FULLNAME need to be set" } -foreach $var ('GSSAPI_SHORTNAME','GSSAPI_FULLNAME','GSSAPI_KEYTAB_PATH','GSSAPI_PRINCIPAL_NAME') +if ($::opt_verbose) { - print "$var=$ENV{$var}\n"; + foreach $var ('GSSAPI_SHORTNAME','GSSAPI_FULLNAME','GSSAPI_KEYTAB_PATH','GSSAPI_PRINCIPAL_NAME') + { + print "$var=$ENV{$var}\n"; + } } - sub is_default { 1 } bless { }; diff --git a/plugin/auth_gssapi/server_plugin.cc b/plugin/auth_gssapi/server_plugin.cc index 64f52a332fc..a79074abe79 100644 --- a/plugin/auth_gssapi/server_plugin.cc +++ b/plugin/auth_gssapi/server_plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. @@ -59,6 +59,14 @@ static int gssapi_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *auth_info) const char *user; int user_len; + /* No user name yet ? Read the client handshake packet with the user name. */ + if (auth_info->user_name == 0) + { + unsigned char *pkt; + if (vio->read_packet(vio, &pkt) < 0) + return CR_ERROR; + } + /* Send first packet with target name and mech name */ if (vio->write_packet(vio, (unsigned char *)first_packet, first_packet_len)) { @@ -106,7 +114,7 @@ static int deinitialize_plugin(void *unused) /* system variable */ static MYSQL_SYSVAR_STR(keytab_path, srv_keytab_path, PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, - "Keytab file path (Kerberos)", + "Keytab file path for Kerberos authentication", NULL, NULL, ""); @@ -131,7 +139,7 @@ static TYPELIB mech_name_typelib = { }; static MYSQL_SYSVAR_ENUM(mech_name, srv_mech, PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY, - "GSSAPI mechanism : either Kerberos or Negotiate", + "GSSAPI mechanism", NULL, NULL, 2,&mech_name_typelib); @@ -169,7 +177,7 @@ maria_declare_plugin(gssapi_server) NULL, /* status variables */ system_variables, /* system variables */ "1.0", - MariaDB_PLUGIN_MATURITY_EXPERIMENTAL + MariaDB_PLUGIN_MATURITY_BETA } maria_declare_plugin_end; diff --git a/plugin/auth_gssapi/server_plugin.h b/plugin/auth_gssapi/server_plugin.h index 6f1a2fc7d54..1348835e653 100644 --- a/plugin/auth_gssapi/server_plugin.h +++ b/plugin/auth_gssapi/server_plugin.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/sspi.h b/plugin/auth_gssapi/sspi.h index ceb668750af..34b8a56a32e 100644 --- a/plugin/auth_gssapi/sspi.h +++ b/plugin/auth_gssapi/sspi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/sspi_client.cc b/plugin/auth_gssapi/sspi_client.cc index 4946a0f4a98..34c1ce2e7ee 100644 --- a/plugin/auth_gssapi/sspi_client.cc +++ b/plugin/auth_gssapi/sspi_client.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/sspi_errmsg.cc b/plugin/auth_gssapi/sspi_errmsg.cc index 8c3eb99ed6b..961ef51f42e 100644 --- a/plugin/auth_gssapi/sspi_errmsg.cc +++ b/plugin/auth_gssapi/sspi_errmsg.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/plugin/auth_gssapi/sspi_server.cc b/plugin/auth_gssapi/sspi_server.cc index 1d51a66c3d8..1dfd2986aaa 100644 --- a/plugin/auth_gssapi/sspi_server.cc +++ b/plugin/auth_gssapi/sspi_server.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. diff --git a/win/packaging/CPackWixConfig.cmake b/win/packaging/CPackWixConfig.cmake index 58a2ef44eef..af2cb1af802 100644 --- a/win/packaging/CPackWixConfig.cmake +++ b/win/packaging/CPackWixConfig.cmake @@ -9,7 +9,7 @@ IF(ESSENTIALS) ENDIF() ELSE() SET(CPACK_COMPONENTS_USED - "Server;Client;Development;SharedLibraries;Embedded;Documentation;IniFiles;Readme;Debuginfo;Common;connect-engine") + "Server;Client;Development;SharedLibraries;Embedded;Documentation;IniFiles;Readme;Debuginfo;Common;connect-engine;ClientPlugins;gssapi-server;gssapi-client") ENDIF() SET( WIX_FEATURE_MySQLServer_EXTRA_FEATURES "DBInstance;SharedClientServerComponents") @@ -54,20 +54,15 @@ SET(CPACK_COMPONENT_GROUP_MYSQLSERVER_DESCRIPTION "Install server") SET(CPACK_COMPONENT_DEBUGBINARIES_DESCRIPTION "Debug/trace versions of executables and libraries" ) #SET(CPACK_COMPONENT_DEBUGBINARIES_WIX_LEVEL 2) - - - #Subfeature "Data Files" - SET(CPACK_COMPONENT_DATAFILES_GROUP "MySQLServer") - SET(CPACK_COMPONENT_DATAFILES_DISPLAY_NAME "Server data files") - SET(CPACK_COMPONENT_DATAFILES_DESCRIPTION "Server data files" ) - SET(CPACK_COMPONENT_DATAFILES_HIDDEN 1) - #Subfeature "Connect Engine" - SET(CPACK_COMPONENT_CONNECT-ENGINE_GROUP "MySQLServer") - SET(CPACK_COMPONENT_CONNECT-ENGINE_DISPLAY_NAME "Server data files") - SET(CPACK_COMPONENT_CONNECT-ENGINE_DESCRIPTION "Server data files" ) - SET(CPACK_COMPONENT_CONNECT-ENGINE_HIDDEN 1) + #Miscellaneous (hidden) components, part of server / or client programs + FOREACH(comp connect-engine ClientPlugins gssapi-server gssapi-client) + STRING(TOUPPER "${comp}" comp) + SET(CPACK_COMPONENT_${comp}_GROUP "MySQLServer") + SET(CPACK_COMPONENT_${comp}_HIDDEN 1) + ENDFOREACH() + #Feature "Devel" SET(CPACK_COMPONENT_GROUP_DEVEL_DISPLAY_NAME "Development Components") SET(CPACK_COMPONENT_GROUP_DEVEL_DESCRIPTION "Installs C/C++ header files and libraries") From 4bb62e91f6f93871106118f3ce47add4c109e208 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 20 Jan 2016 14:35:11 +0100 Subject: [PATCH 27/83] Do not require server RPM for client plugins --- cmake/plugin.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 95cf9e2cd6e..cd4e4faaaf0 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -210,8 +210,9 @@ MACRO(MYSQL_ADD_PLUGIN) SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} ${ARG_COMPONENT}) SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} PARENT_SCOPE) - SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_REQUIRES "MariaDB" PARENT_SCOPE) - + IF (NOT ARG_CLIENT) + SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_REQUIRES "MariaDB" PARENT_SCOPE) + ENDIF() # workarounds for cmake issues #13248 and #12864: SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_PROVIDES "cmake_bug_13248" PARENT_SCOPE) SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_OBSOLETES "cmake_bug_13248" PARENT_SCOPE) From 42d9f3d87bc77ce501f7831a3a07c1ead3ab2389 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 20 Jan 2016 13:24:30 -0500 Subject: [PATCH 28/83] Fix spelling of my name --- plugin/auth_gssapi/gssapi_client.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/auth_gssapi/gssapi_client.cc b/plugin/auth_gssapi/gssapi_client.cc index d449bb83a84..a05ea158e4d 100644 --- a/plugin/auth_gssapi/gssapi_client.cc +++ b/plugin/auth_gssapi/gssapi_client.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood, +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, Vladislav Vaintroub & MariaDB Corporation All rights reserved. From 825f51d1aab51d363dc07ec9fe0829af33063883 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Wed, 16 Dec 2015 19:33:41 +0100 Subject: [PATCH 29/83] MDEV-9118 ANALYZE TABLE for Engine independent status fetchs blob/text columns without use Do not include BLOB fields by default. --- mysql-test/r/analyze.result | 2 + mysql-test/r/mysqlcheck.result | 60 +++++++++++++++---- mysql-test/r/statistics.result | 26 +++++++- .../r/statistics_index_crash-7362.result | 2 + .../suite/innodb_fts/r/innodb-fts-fic.result | 1 + .../innodb_fts/r/innodb-fts-stopword.result | 2 + .../suite/innodb_zip/r/innodb-zip.result | 2 + .../parts/r/partition_repair_myisam.result | 1 + mysql-test/t/statistics.test | 11 ++++ sql/share/errmsg-utf8.txt | 3 + sql/sql_admin.cc | 31 ++++++++-- 11 files changed, 123 insertions(+), 18 deletions(-) diff --git a/mysql-test/r/analyze.result b/mysql-test/r/analyze.result index 9dff94ab08c..5a05d1c5da8 100644 --- a/mysql-test/r/analyze.result +++ b/mysql-test/r/analyze.result @@ -34,9 +34,11 @@ create table t1 (a mediumtext, fulltext key key1(a)) charset utf8 collate utf8_g insert into t1 values ('hello'); analyze table t1; Table Op Msg_type Msg_text +test.t1 analyze Warning Engine-independent statistics are not collected for column 'a' test.t1 analyze status OK analyze table t1; Table Op Msg_type Msg_text +test.t1 analyze Warning Engine-independent statistics are not collected for column 'a' test.t1 analyze status Table is already up to date drop table t1; CREATE TABLE t1 (a int); diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index 033b4086985..eb05f056321 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -7,19 +7,34 @@ mtr.test_suppressions OK mysql.column_stats OK mysql.columns_priv OK mysql.db OK -mysql.event OK +mysql.event +Warning : Engine-independent statistics are not collected for column 'body' +Warning : Engine-independent statistics are not collected for column 'body_utf8' +status : OK mysql.func OK mysql.gtid_slave_pos OK -mysql.help_category OK +mysql.help_category +Warning : Engine-independent statistics are not collected for column 'url' +status : OK mysql.help_keyword OK mysql.help_relation OK -mysql.help_topic OK +mysql.help_topic +Warning : Engine-independent statistics are not collected for column 'description' +Warning : Engine-independent statistics are not collected for column 'example' +Warning : Engine-independent statistics are not collected for column 'url' +status : OK mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK mysql.plugin OK -mysql.proc OK +mysql.proc +Warning : Engine-independent statistics are not collected for column 'param_list' +Warning : Engine-independent statistics are not collected for column 'returns' +Warning : Engine-independent statistics are not collected for column 'body' +Warning : Engine-independent statistics are not collected for column 'comment' +Warning : Engine-independent statistics are not collected for column 'body_utf8' +status : OK mysql.procs_priv OK mysql.proxies_priv OK mysql.roles_mapping OK @@ -31,7 +46,12 @@ mysql.time_zone_leap_second OK mysql.time_zone_name OK mysql.time_zone_transition OK mysql.time_zone_transition_type OK -mysql.user OK +mysql.user +Warning : Engine-independent statistics are not collected for column 'ssl_cipher' +Warning : Engine-independent statistics are not collected for column 'x509_issuer' +Warning : Engine-independent statistics are not collected for column 'x509_subject' +Warning : Engine-independent statistics are not collected for column 'authentication_string' +status : OK mtr.global_suppressions Table is already up to date mtr.test_suppressions Table is already up to date mysql.column_stats OK @@ -69,19 +89,34 @@ mysql.user OK mysql.column_stats OK mysql.columns_priv OK mysql.db OK -mysql.event OK +mysql.event +Warning : Engine-independent statistics are not collected for column 'body' +Warning : Engine-independent statistics are not collected for column 'body_utf8' +status : OK mysql.func OK mysql.gtid_slave_pos OK -mysql.help_category OK +mysql.help_category +Warning : Engine-independent statistics are not collected for column 'url' +status : OK mysql.help_keyword OK mysql.help_relation OK -mysql.help_topic OK +mysql.help_topic +Warning : Engine-independent statistics are not collected for column 'description' +Warning : Engine-independent statistics are not collected for column 'example' +Warning : Engine-independent statistics are not collected for column 'url' +status : OK mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK mysql.plugin OK -mysql.proc OK +mysql.proc +Warning : Engine-independent statistics are not collected for column 'param_list' +Warning : Engine-independent statistics are not collected for column 'returns' +Warning : Engine-independent statistics are not collected for column 'body' +Warning : Engine-independent statistics are not collected for column 'comment' +Warning : Engine-independent statistics are not collected for column 'body_utf8' +status : OK mysql.procs_priv OK mysql.proxies_priv OK mysql.roles_mapping OK @@ -93,7 +128,12 @@ mysql.time_zone_leap_second OK mysql.time_zone_name OK mysql.time_zone_transition OK mysql.time_zone_transition_type OK -mysql.user OK +mysql.user +Warning : Engine-independent statistics are not collected for column 'ssl_cipher' +Warning : Engine-independent statistics are not collected for column 'x509_issuer' +Warning : Engine-independent statistics are not collected for column 'x509_subject' +Warning : Engine-independent statistics are not collected for column 'authentication_string' +status : OK mysql.column_stats Table is already up to date mysql.columns_priv Table is already up to date mysql.db Table is already up to date diff --git a/mysql-test/r/statistics.result b/mysql-test/r/statistics.result index bd6a0849e3d..fdda572f396 100644 --- a/mysql-test/r/statistics.result +++ b/mysql-test/r/statistics.result @@ -1212,6 +1212,7 @@ test t2 PRIMARY 1 1.0000 ANALYZE TABLE t1; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'b' test.t1 analyze status OK SELECT * FROM mysql.column_stats; db_name table_name column_name min_value max_value nulls_ratio avg_length avg_frequency hist_size hist_type histogram @@ -1225,7 +1226,6 @@ test t1 c aaaa dddddddd 0.1250 6.6571 7.0000 0 NULL NULL test t1 d 1989-03-12 1999-07-23 0.1500 3.0000 8.5000 0 NULL NULL test t1 e 0.01 0.112 0.2250 8.0000 6.2000 0 NULL NULL test t1 f 1 5 0.2000 1.0000 6.4000 0 NULL NULL -test t1 b NULL NULL 0.2000 17.1250 NULL NULL NULL NULL SELECT * FROM mysql.index_stats; db_name table_name index_name prefix_arity avg_frequency test t1 idx2 1 7.0000 @@ -1265,6 +1265,7 @@ set use_stat_tables='never'; ANALYZE TABLE t1 PERSISTENT FOR ALL; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'b' test.t1 analyze status Table is already up to date SELECT * FROM mysql.table_stats; db_name table_name cardinality @@ -1276,7 +1277,6 @@ test t1 c aaaa dddddddd 0.1250 6.6571 7.0000 0 NULL NULL test t1 d 1989-03-12 1999-07-23 0.1500 3.0000 8.5000 0 NULL NULL test t1 e 0.01 0.112 0.2250 8.0000 6.2000 0 NULL NULL test t1 f 1 5 0.2000 1.0000 6.4000 0 NULL NULL -test t1 b NULL NULL 0.2000 17.1250 NULL NULL NULL NULL SELECT * FROM mysql.index_stats; db_name table_name index_name prefix_arity avg_frequency test t1 PRIMARY 1 1.0000 @@ -1291,6 +1291,28 @@ test t1 idx4 3 NULL DELETE FROM mysql.table_stats; DELETE FROM mysql.column_stats; DELETE FROM mysql.index_stats; +ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'b' +test.t1 analyze status Table is already up to date +ANALYZE TABLE t1 PERSISTENT FOR columns(a,b) INDEXES(); +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'b' +test.t1 analyze status Table is already up to date +ANALYZE TABLE t1 PERSISTENT FOR columns(b) indexes(idx2); +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'b' +test.t1 analyze status Table is already up to date +ANALYZE TABLE t1 PERSISTENT FOR columns() indexes(idx2); +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status Table is already up to date +DELETE FROM mysql.table_stats; +DELETE FROM mysql.column_stats; +DELETE FROM mysql.index_stats; DROP TABLE t1,t2; set names utf8; CREATE DATABASE world; diff --git a/mysql-test/r/statistics_index_crash-7362.result b/mysql-test/r/statistics_index_crash-7362.result index 99f65d7e1b7..c213fa49afe 100644 --- a/mysql-test/r/statistics_index_crash-7362.result +++ b/mysql-test/r/statistics_index_crash-7362.result @@ -3,6 +3,7 @@ INSERT INTO t1 VALUES (unhex('3E0D0A4141414142334E7A6143317963324541414141424977 ANALYZE TABLE t1 PERSISTENT FOR ALL; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'a' test.t1 analyze status OK SELECT * FROM mysql.index_stats WHERE index_name='a' AND table_name='t1'; db_name table_name index_name prefix_arity avg_frequency @@ -13,6 +14,7 @@ INSERT INTO t1 VALUES (unhex('3E0D0A4141414142334E7A6143317963324541414141424977 ANALYZE TABLE t1 PERSISTENT FOR ALL; Table Op Msg_type Msg_text test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'a' test.t1 analyze status OK SELECT * FROM mysql.index_stats WHERE index_name='a' AND table_name='t1'; db_name table_name index_name prefix_arity avg_frequency diff --git a/mysql-test/suite/innodb_fts/r/innodb-fts-fic.result b/mysql-test/suite/innodb_fts/r/innodb-fts-fic.result index e5df6ca8b05..ee7277af640 100644 --- a/mysql-test/suite/innodb_fts/r/innodb-fts-fic.result +++ b/mysql-test/suite/innodb_fts/r/innodb-fts-fic.result @@ -36,6 +36,7 @@ count 2 ANALYZE TABLE articles; Table Op Msg_type Msg_text +test.articles analyze Warning Engine-independent statistics are not collected for column 'body' test.articles analyze status OK SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE); diff --git a/mysql-test/suite/innodb_fts/r/innodb-fts-stopword.result b/mysql-test/suite/innodb_fts/r/innodb-fts-stopword.result index ea0ec381faa..e0ba456b51c 100644 --- a/mysql-test/suite/innodb_fts/r/innodb-fts-stopword.result +++ b/mysql-test/suite/innodb_fts/r/innodb-fts-stopword.result @@ -247,6 +247,7 @@ articles CREATE TABLE `articles` ( ALTER TABLE articles ADD FULLTEXT INDEX idx (title,body); ANALYZE TABLE articles; Table Op Msg_type Msg_text +test.articles analyze Warning Engine-independent statistics are not collected for column 'body' test.articles analyze status OK SELECT * FROM articles WHERE MATCH(title,body) AGAINST ("where will"); id title body @@ -569,6 +570,7 @@ select @@innodb_ft_enable_stopword; 1 ANALYZE TABLE articles; Table Op Msg_type Msg_text +test.articles analyze Warning Engine-independent statistics are not collected for column 'body' test.articles analyze status OK SELECT * FROM articles WHERE MATCH(title,body) AGAINST ("where will"); id title body diff --git a/mysql-test/suite/innodb_zip/r/innodb-zip.result b/mysql-test/suite/innodb_zip/r/innodb-zip.result index dbfae3c0630..2113c6e1069 100644 --- a/mysql-test/suite/innodb_zip/r/innodb-zip.result +++ b/mysql-test/suite/innodb_zip/r/innodb-zip.result @@ -122,9 +122,11 @@ a left(b,40) 1 1abcdefghijklmnopqrstuvwxyzAAAAAAAAAAAAA analyze table t1; Table Op Msg_type Msg_text +mysqltest_innodb_zip.t1 analyze Warning Engine-independent statistics are not collected for column 'b' mysqltest_innodb_zip.t1 analyze status OK analyze table t2; Table Op Msg_type Msg_text +mysqltest_innodb_zip.t2 analyze Warning Engine-independent statistics are not collected for column 'b' mysqltest_innodb_zip.t2 analyze status OK SELECT table_schema, table_name, row_format, data_length, index_length FROM information_schema.tables WHERE engine='innodb' AND table_schema != 'mysql'; table_schema table_name row_format data_length index_length diff --git a/mysql-test/suite/parts/r/partition_repair_myisam.result b/mysql-test/suite/parts/r/partition_repair_myisam.result index 3652698f634..cbc425b48db 100644 --- a/mysql-test/suite/parts/r/partition_repair_myisam.result +++ b/mysql-test/suite/parts/r/partition_repair_myisam.result @@ -322,6 +322,7 @@ FLUSH TABLES; # replacing p6 with a crashed MYD file (1) (splitted dynamic record) ANALYZE TABLE t1_will_crash; Table Op Msg_type Msg_text +test.t1_will_crash analyze Warning Engine-independent statistics are not collected for column 'c' test.t1_will_crash analyze status OK OPTIMIZE TABLE t1_will_crash; Table Op Msg_type Msg_text diff --git a/mysql-test/t/statistics.test b/mysql-test/t/statistics.test index 2c8dec307cc..3b57b8f2c52 100644 --- a/mysql-test/t/statistics.test +++ b/mysql-test/t/statistics.test @@ -494,6 +494,17 @@ DELETE FROM mysql.table_stats; DELETE FROM mysql.column_stats; DELETE FROM mysql.index_stats; + +ANALYZE TABLE t1 PERSISTENT FOR COLUMNS(b) INDEXES(); +ANALYZE TABLE t1 PERSISTENT FOR columns(a,b) INDEXES(); +ANALYZE TABLE t1 PERSISTENT FOR columns(b) indexes(idx2); +ANALYZE TABLE t1 PERSISTENT FOR columns() indexes(idx2); + +DELETE FROM mysql.table_stats; +DELETE FROM mysql.column_stats; +DELETE FROM mysql.index_stats; + + DROP TABLE t1,t2; set names utf8; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 9482bd56926..36d3a0c0813 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7136,3 +7136,6 @@ ER_KILL_QUERY_DENIED_ERROR eng "You are not owner of query %lu" ger "Sie sind nicht Eigentümer von Abfrage %lu" rus "Ð’Ñ‹ не ÑвлÑетеÑÑŒ владельцем запроÑа %lu" +ER_NO_EIS_FOR_FIELD + eng "Engine-independent statistics are not collected for column '%s'" + ukr "Ðезалежна від типу таблиці ÑтатиÑтика не збираєтьÑÑ Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%s'" diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 0787aa9e92f..d8ca8633baa 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -692,10 +692,20 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, } if (!lex->column_list) - { - uint fields= 0; - for ( ; *field_ptr; field_ptr++, fields++) ; - bitmap_set_prefix(tab->read_set, fields); + { + bitmap_clear_all(tab->read_set); + for (uint fields= 0; *field_ptr; field_ptr++, fields++) + { + enum enum_field_types type= (*field_ptr)->type(); + if (type < MYSQL_TYPE_MEDIUM_BLOB || + type > MYSQL_TYPE_BLOB) + bitmap_set_bit(tab->read_set, fields); + else + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NO_EIS_FOR_FIELD, + ER_THD(thd, ER_NO_EIS_FOR_FIELD), + (*field_ptr)->field_name); + } } else { @@ -713,8 +723,17 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, compl_result_code= result_code= HA_ADMIN_INVALID; break; } - bitmap_set_bit(tab->read_set, pos-1); - } + pos--; + enum enum_field_types type= tab->field[pos]->type(); + if (type < MYSQL_TYPE_MEDIUM_BLOB || + type > MYSQL_TYPE_BLOB) + bitmap_set_bit(tab->read_set, pos); + else + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_NO_EIS_FOR_FIELD, + ER_THD(thd, ER_NO_EIS_FOR_FIELD), + column_name->str); + } tab->file->column_bitmaps_signal(); } From ff8d4009a7743b99c1c13831172b08041a4b272e Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Sun, 24 Jan 2016 12:31:11 +0300 Subject: [PATCH 30/83] MDEV-9457: Poor query plan chosen for ORDER BY query by a recent 10.1 Undo the change in test_if_skip_sort_order() that set ref_key=-1 when a variant of index_merge is used (was made in fix for MDEV-9021). It turned out that test_if_cheaper_ordering() call below assumes that ref_key=-1 means "no index is used", that is, "an inefficient full table scan is done". This is not the same as index_merge, index_merge can actually be quite efficient. So, ref_key=MAX_KEY denotes the fact that some index is used, not any given index. --- mysql-test/r/order_by_innodb.result | 37 ++++++++++++++++++++++++++ mysql-test/t/order_by_innodb.test | 40 +++++++++++++++++++++++++++++ sql/sql_select.cc | 21 +++++++++++---- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/order_by_innodb.result b/mysql-test/r/order_by_innodb.result index 3c6c4053741..4f59a2f8c20 100644 --- a/mysql-test/r/order_by_innodb.result +++ b/mysql-test/r/order_by_innodb.result @@ -11,3 +11,40 @@ a b c d 8 NULL 9 NULL 8 NULL 10 NULL DROP TABLE t1; +# +# MDEV-9457: Poor query plan chosen for ORDER BY query by a recent 10.1 +# +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t1 ( +pk int primary key, +key1 int, +key2 int, +col1 char(255), +key(key1), +key(key2) +) engine=innodb; +set @a=-1; +insert into t1 +select +@a:=@a+1, +@a, +@a, +repeat('abcd', 63) +from t0 A, t0 B, t0 C, t0 D; +# The following must NOT use 'index' on PK. +# It should use index_merge(key1,key2) + filesort +explain +select * +from t1 +where key1<3 or key2<3 +order by pk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL # Using sort_union(key1,key2); Using where; Using filesort +explain +select * +from t1 +where key1<3 or key2<3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL # Using sort_union(key1,key2); Using where +drop table t0, t1; diff --git a/mysql-test/t/order_by_innodb.test b/mysql-test/t/order_by_innodb.test index c20eaceb053..097eddd24f1 100644 --- a/mysql-test/t/order_by_innodb.test +++ b/mysql-test/t/order_by_innodb.test @@ -21,3 +21,43 @@ SELECT * FROM t1 WHERE a = 8 AND (b = 1 OR b IS NULL) ORDER BY c; DROP TABLE t1; +--echo # +--echo # MDEV-9457: Poor query plan chosen for ORDER BY query by a recent 10.1 +--echo # +create table t0 (a int); +insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t1 ( + pk int primary key, + key1 int, + key2 int, + col1 char(255), + key(key1), + key(key2) +) engine=innodb; + +set @a=-1; +insert into t1 +select + @a:=@a+1, + @a, + @a, + repeat('abcd', 63) +from t0 A, t0 B, t0 C, t0 D; + +--echo # The following must NOT use 'index' on PK. +--echo # It should use index_merge(key1,key2) + filesort +--replace_column 9 # +explain +select * +from t1 +where key1<3 or key2<3 +order by pk; + +--replace_column 9 # +explain +select * +from t1 +where key1<3 or key2<3; + +drop table t0, t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 28a2c8df606..ea30bf96f06 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -20777,7 +20777,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) - ref_key= -1; + { + /* + we set ref_key=MAX_KEY instead of -1, because test_if_cheaper ordering + assumes that "ref_key==-1" means doing full index scan. + (This is not very straightforward and we got into this situation for + historical reasons. Should be fixed at some point). + */ + ref_key= MAX_KEY; + } else { ref_key= select->quick->index; @@ -25442,8 +25450,12 @@ static bool get_range_limit_read_cost(const JOIN_TAB *tab, @param table Table if tab == NULL or tab->table @param usable_keys Key map to find a cheaper key in @param ref_key - * 0 <= key < MAX_KEY - key number (hint) to start the search - * -1 - no key number provided + 0 <= key < MAX_KEY - Key that is currently used for finding + row + MAX_KEY - means index_merge is used + -1 - means we're currently not using an + index to find rows. + @param select_limit LIMIT value @param [out] new_key Key number if success, otherwise undefined @param [out] new_key_direction Return -1 (reverse) or +1 if success, @@ -25472,7 +25484,6 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, uint *saved_best_key_parts) { DBUG_ENTER("test_if_cheaper_ordering"); - DBUG_ASSERT(ref_key < int(MAX_KEY)); /* Check whether there is an index compatible with the given order usage of which is cheaper than usage of the ref_key index (ref_key>=0) @@ -25537,7 +25548,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, Calculate the selectivity of the ref_key for REF_ACCESS. For RANGE_ACCESS we use table->quick_condition_rows. */ - if (ref_key >= 0 && !is_hash_join_key_no(ref_key) && tab->type == JT_REF) + if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF) { if (table->quick_keys.is_set(ref_key)) refkey_rows_estimate= table->quick_rows[ref_key]; From da0991c6daaa650ec224fe4028975eaf6db02ba7 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Thu, 21 Jan 2016 14:03:24 +0400 Subject: [PATCH 31/83] MDEV-7875 Duplicate maria_add_gis_sp script in the sources. Now both are generated by the cmake from the scripts/maria_add_gis_sp.sql.in --- .gitignore | 2 + scripts/CMakeLists.txt | 30 +++++++++++- scripts/maria_add_gis_sp.sql | 48 ------------------- ..._bootstrap.sql => maria_add_gis_sp.sql.in} | 7 ++- 4 files changed, 35 insertions(+), 52 deletions(-) delete mode 100644 scripts/maria_add_gis_sp.sql rename scripts/{maria_add_gis_sp_bootstrap.sql => maria_add_gis_sp.sql.in} (90%) diff --git a/.gitignore b/.gitignore index 92c58cc781e..733bbb45dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -112,6 +112,8 @@ scripts/wsrep_sst_mysqldump scripts/wsrep_sst_rsync scripts/wsrep_sst_xtrabackup scripts/wsrep_sst_xtrabackup-v2 +scripts/maria_add_gis_sp.sql +scripts/maria_add_gis_sp_bootstrap.sql sql-bench/bench-count-distinct sql-bench/bench-init.pl sql-bench/compare-results diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 01fd1c4f78f..6af76816c60 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -79,8 +79,6 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mysql_performance_tables.sql ${CMAKE_CURRENT_SOURCE_DIR}/fill_help_tables.sql ${CMAKE_CURRENT_SOURCE_DIR}/mysql_test_data_timezone.sql - ${CMAKE_CURRENT_SOURCE_DIR}/maria_add_gis_sp.sql - ${CMAKE_CURRENT_SOURCE_DIR}/maria_add_gis_sp_bootstrap.sql ${FIX_PRIVILEGES_SQL} DESTINATION ${INSTALL_MYSQLSHAREDIR} COMPONENT Server ) @@ -324,6 +322,34 @@ ELSE() ENDFOREACH() ENDIF() +# Configure two scripts from one 'in' file. +# The maria_add_gis_sp.sql - to be sent to 'mysql' tool +# and the maria_add_gis_sp_bootstrap.sql, that can be sent to +# the server as a bootstrap command. + +SET(ADD_GIS_SP_SET_DELIMITER "delimiter |") +SET(ADD_GIS_SP_RESET_DELIMITER "delimiter ;") +SET(ADD_GIS_SP_EOL "|") +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/maria_add_gis_sp.sql.in + ${CMAKE_CURRENT_BINARY_DIR}/maria_add_gis_sp.sql ESCAPE_QUOTES @ONLY) + +INSTALL_SCRIPT(${CMAKE_CURRENT_BINARY_DIR}/maria_add_gis_sp.sql + DESTINATION ${INSTALL_BINDIR} + COMPONENT Server + ) + +SET(ADD_GIS_SP_SET_DELIMITER "") +SET(ADD_GIS_SP_RESET_DELIMITER "") +SET(ADD_GIS_SP_EOL ";") +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/maria_add_gis_sp.sql.in + ${CMAKE_CURRENT_BINARY_DIR}/maria_add_gis_sp_bootstrap.sql ESCAPE_QUOTES @ONLY) + +INSTALL_SCRIPT(${CMAKE_CURRENT_BINARY_DIR}/maria_add_gis_sp.sql + ${CMAKE_CURRENT_BINARY_DIR}/maria_add_gis_sp_bootstrap.sql + DESTINATION ${INSTALL_MYSQLSHAREDIR} + COMPONENT Server + ) + # Install libgcc as mylibgcc.a IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_FLAGS MATCHES "-static") EXECUTE_PROCESS ( diff --git a/scripts/maria_add_gis_sp.sql b/scripts/maria_add_gis_sp.sql deleted file mode 100644 index 8ddc7abd17f..00000000000 --- a/scripts/maria_add_gis_sp.sql +++ /dev/null @@ -1,48 +0,0 @@ --- Copyright (C) 2014 MariaDB Ab. --- --- This program is free software; you can redistribute it and/or modify --- it under the terms of the GNU 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 part creates stored procedures required by the OpenGIS standards. --- Proc privilege is needed to run it. --- To use this file, load its contents into the mysql database like that: --- mysql -u root -p mysql < scripts/maria_add_gis_sp.sql - -SET sql_mode=''; - -DROP PROCEDURE IF EXISTS AddGeometryColumn; -DROP PROCEDURE IF EXISTS DropGeometryColumn; - -delimiter | - -CREATE PROCEDURE AddGeometryColumn(catalog varchar(64), t_schema varchar(64), - t_name varchar(64), geometry_column varchar(64), t_srid int) -begin - set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); - PREPARE ls from @qwe; - execute ls; - deallocate prepare ls; -end| - -CREATE PROCEDURE DropGeometryColumn(catalog varchar(64), t_schema varchar(64), - t_name varchar(64), geometry_column varchar(64)) -begin - set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); - PREPARE ls from @qwe; - execute ls; - deallocate prepare ls; -end| - -delimiter ; - diff --git a/scripts/maria_add_gis_sp_bootstrap.sql b/scripts/maria_add_gis_sp.sql.in similarity index 90% rename from scripts/maria_add_gis_sp_bootstrap.sql rename to scripts/maria_add_gis_sp.sql.in index 063b6650efd..1e2e513cd0a 100644 --- a/scripts/maria_add_gis_sp_bootstrap.sql +++ b/scripts/maria_add_gis_sp.sql.in @@ -18,17 +18,20 @@ SET sql_mode=''; +@ADD_GIS_SP_SET_DELIMITER@ + DROP PROCEDURE IF EXISTS AddGeometryColumn; DROP PROCEDURE IF EXISTS DropGeometryColumn; CREATE PROCEDURE AddGeometryColumn(catalog varchar(64), t_schema varchar(64), t_name varchar(64), geometry_column varchar(64), t_srid int) begin - set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end; + set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' ADD ', geometry_column,' GEOMETRY REF_SYSTEM_ID=', t_srid); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end @ADD_GIS_SP_EOL@ CREATE PROCEDURE DropGeometryColumn(catalog varchar(64), t_schema varchar(64), t_name varchar(64), geometry_column varchar(64)) begin - set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end; + set @qwe= concat('ALTER TABLE ', t_schema, '.', t_name, ' DROP ', geometry_column); PREPARE ls from @qwe; execute ls; deallocate prepare ls; end @ADD_GIS_SP_EOL@ +@ADD_GIS_SP_RESET_DELIMITER@ From 0fab28ceddb61ce9caf15f6449d3d500577e3892 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 22 Dec 2015 20:25:29 +0100 Subject: [PATCH 32/83] cmake: better auto *.i targets in Makefiles --- cmake/maintainer.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake index 24547103b63..367b78afd0d 100644 --- a/cmake/maintainer.cmake +++ b/cmake/maintainer.cmake @@ -56,3 +56,10 @@ ELSEIF(MYSQL_MAINTAINER_MODE MATCHES "AUTO") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MY_MAINTAINER_C_WARNINGS}") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MY_MAINTAINER_CXX_WARNINGS}") ENDIF() + +IF(CMAKE_C_COMPILER_ID MATCHES "GNU") + STRING(REPLACE " -E " " -E -dDI " CMAKE_C_CREATE_PREPROCESSED_SOURCE ${CMAKE_C_CREATE_PREPROCESSED_SOURCE}) +ENDIF() +IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + STRING(REPLACE " -E " " -E -dDI " CMAKE_CXX_CREATE_PREPROCESSED_SOURCE ${CMAKE_CXX_CREATE_PREPROCESSED_SOURCE}) +ENDIF() From 744e605de05354bdb18f9241daa8331687492f6b Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 26 Dec 2015 09:40:49 +0100 Subject: [PATCH 33/83] cleanup: wsrep helper to create a thread --- sql/wsrep_thd.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index ab09a9e3a99..cb9822d1599 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -369,6 +369,14 @@ static void wsrep_replication_process(THD *thd) DBUG_VOID_RETURN; } +static bool create_wsrep_THD(wsrep_thd_processor_fun processor) +{ + pthread_t unused; + bool res= pthread_create(&unused, &connection_attrib, start_wsrep_THD, + (void*)processor); + return res; +} + void wsrep_create_appliers(long threads) { if (!wsrep_connected) @@ -385,11 +393,8 @@ void wsrep_create_appliers(long threads) } long wsrep_threads=0; - pthread_t hThread; while (wsrep_threads++ < threads) { - if (pthread_create( - &hThread, &connection_attrib, - start_wsrep_THD, (void*)wsrep_replication_process)) + if (create_wsrep_THD(wsrep_replication_process)) WSREP_WARN("Can't create thread to manage wsrep replication"); } } @@ -476,10 +481,8 @@ void wsrep_create_rollbacker() { if (wsrep_provider && strcasecmp(wsrep_provider, "none")) { - pthread_t hThread; /* create rollbacker */ - if (pthread_create( &hThread, &connection_attrib, - start_wsrep_THD, (void*)wsrep_rollback_process)) + if (create_wsrep_THD(wsrep_rollback_process)) WSREP_WARN("Can't create thread to manage wsrep rollback"); } } From a2330c820af56d69e19f07f9cc9deb655fc67174 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 25 Jan 2016 17:04:09 +0100 Subject: [PATCH 34/83] MDEV-8208 Sporadic SEGFAULT on startup different fix. remove old ones, wait for THD to be fully initialized before continuing with the server startup process. This reverts commits db2e21b, 13615c5, 3f515a0, 70113ee. --- sql/mysqld.cc | 49 +++------------------------------------------ sql/wsrep_mysqld.cc | 21 ++----------------- sql/wsrep_mysqld.h | 2 -- sql/wsrep_thd.cc | 11 ++++++++++ 4 files changed, 16 insertions(+), 67 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8f2feed2eb9..fa8fb3f0a9a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5207,49 +5207,6 @@ static int init_server_components() } plugins_are_initialized= TRUE; /* Don't separate from init function */ -#ifdef WITH_WSREP - /* Wait for wsrep threads to get created. */ - if (wsrep_creating_startup_threads == 1) { - mysql_mutex_lock(&LOCK_thread_count); - while (wsrep_running_threads < 2) - { - mysql_cond_wait(&COND_thread_count, &LOCK_thread_count); - } - - /* Now is the time to initialize threads for queries. */ - THD *tmp; - I_List_iterator it(threads); - while ((tmp= it++)) - { - if (tmp->wsrep_applier == true) - { - /* - Save/restore server_status and variables.option_bits and they get - altered during init_for_queries(). - */ - unsigned int server_status_saved= tmp->server_status; - ulonglong option_bits_saved= tmp->variables.option_bits; - - /* - Set THR_THD to temporarily point to this THD to register all the - variables that allocates memory for this THD. - */ - THD *current_thd_saved= current_thd; - set_current_thd(tmp); - - tmp->init_for_queries(); - - /* Restore current_thd. */ - set_current_thd(current_thd_saved); - - tmp->server_status= server_status_saved; - tmp->variables.option_bits= option_bits_saved; - } - } - mysql_mutex_unlock(&LOCK_thread_count); - } -#endif - /* we do want to exit if there are any other unknown options */ if (remaining_argc > 1) { @@ -5897,6 +5854,9 @@ int mysqld_main(int argc, char **argv) if (Events::init((THD*) 0, opt_noacl || opt_bootstrap)) unireg_abort(1); + /* It's now safe to use thread specific memory */ + mysqld_server_initialized= 1; + if (WSREP_ON) { if (opt_bootstrap) @@ -5937,9 +5897,6 @@ int mysqld_main(int argc, char **argv) } } - /* It's now safe to use thread specific memory */ - mysqld_server_initialized= 1; - create_shutdown_thread(); start_handle_manager(); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index b922d2b2857..03524c8ad5c 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -91,12 +91,6 @@ my_bool wsrep_slave_UK_checks = 0; // slave thread does UK checks my_bool wsrep_slave_FK_checks = 0; // slave thread does FK checks bool wsrep_new_cluster = false; // Bootstrap the cluster ? -/* - Set during the creation of first wsrep applier and rollback threads. - Since these threads are critical, abort if the thread creation fails. -*/ -my_bool wsrep_creating_startup_threads = 0; - // Use wsrep_gtid_domain_id for galera transactions? bool wsrep_gtid_mode = 0; // gtid_domain_id for galera transactions. @@ -798,7 +792,6 @@ void wsrep_init_startup (bool first) if (!wsrep_start_replication()) unireg_abort(1); - wsrep_creating_startup_threads= 1; wsrep_create_rollbacker(); wsrep_create_appliers(1); @@ -1820,21 +1813,11 @@ pthread_handler_t start_wsrep_THD(void *arg) //thd->version= refresh_version; thd->proc_info= 0; thd->set_command(COM_SLEEP); - - if (wsrep_creating_startup_threads == 0) - { - thd->init_for_queries(); - } + thd->init_for_queries(); mysql_mutex_lock(&LOCK_thread_count); wsrep_running_threads++; mysql_cond_broadcast(&COND_thread_count); - - if (wsrep_running_threads > 2) - { - wsrep_creating_startup_threads= 0; - } - mysql_mutex_unlock(&LOCK_thread_count); processor(thd); @@ -1877,7 +1860,7 @@ error: WSREP_ERROR("Failed to create/initialize system thread"); /* Abort if its the first applier/rollbacker thread. */ - if (wsrep_creating_startup_threads == 1) + if (!mysqld_server_initialized) unireg_abort(1); else return NULL; diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 26d3484b3b4..a22eb1a0b64 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -86,7 +86,6 @@ extern my_bool wsrep_slave_FK_checks; extern my_bool wsrep_slave_UK_checks; extern ulong wsrep_running_threads; extern bool wsrep_new_cluster; -extern my_bool wsrep_creating_startup_threads; extern bool wsrep_gtid_mode; extern uint32 wsrep_gtid_domain_id; @@ -341,7 +340,6 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len); #define wsrep_thr_init() do {} while(0) #define wsrep_thr_deinit() do {} while(0) #define wsrep_running_threads (0) -#define wsrep_creating_startup_threads (0) #endif /* WITH_WSREP */ #endif /* WSREP_MYSQLD_H */ diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index cb9822d1599..fb48c1ad60e 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -371,9 +371,20 @@ static void wsrep_replication_process(THD *thd) static bool create_wsrep_THD(wsrep_thd_processor_fun processor) { + ulong old_wsrep_running_threads= wsrep_running_threads; pthread_t unused; + mysql_mutex_lock(&LOCK_thread_count); bool res= pthread_create(&unused, &connection_attrib, start_wsrep_THD, (void*)processor); + /* + if starting a thread on server startup, wait until the this thread's THD + is fully initialized (otherwise a THD initialization code might + try to access a partially initialized server data structure - MDEV-8208). + */ + if (!mysqld_server_initialized) + while (old_wsrep_running_threads == wsrep_running_threads) + mysql_cond_wait(&COND_thread_count, &LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thread_count); return res; } From 5a5f18f3f7f5c98800c6370836bc407deb0e5c02 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 21 Jan 2016 21:58:30 +0100 Subject: [PATCH 35/83] MDEV-9205 PAM user map plugin does not work with LDAP groups allow more characters in a valid user/group name: * POSIX allows dashes '-' and dots '.' * also the name may end with a dollar sign '$' for our purposes it's enough to allow [-.$] anywhere in the name --- plugin/auth_pam/mapper/pam_user_map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/auth_pam/mapper/pam_user_map.c b/plugin/auth_pam/mapper/pam_user_map.c index 1c4bccc7f27..fb149c5cc05 100644 --- a/plugin/auth_pam/mapper/pam_user_map.c +++ b/plugin/auth_pam/mapper/pam_user_map.c @@ -127,13 +127,13 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, s++; } from= s; - skip(isalnum(*s) || (*s == '_')); + skip(isalnum(*s) || (*s == '_') || (*s == '.') || (*s == '-') || (*s == '$')); end_from= s; skip(isspace(*s)); if (end_from == from || *s++ != ':') goto syntax_error; skip(isspace(*s)); to= s; - skip(isalnum(*s) || (*s == '_')); + skip(isalnum(*s) || (*s == '_') || (*s == '.') || (*s == '-') || (*s == '$')); end_to= s; if (end_to == to) goto syntax_error; From b01e2ff1cd5885eddbfcb2792222ed39548dbdd0 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 21 Jan 2016 22:10:44 +0100 Subject: [PATCH 36/83] MDEV-9385 Devel package wants ownership of /usr/share/pkgconfig on CentOS/RHEL add /usr/share/pkgconfig to the rpm ignored list --- cmake/cpack_rpm.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/cpack_rpm.cmake b/cmake/cpack_rpm.cmake index f40a38ec775..4ec16369354 100644 --- a/cmake/cpack_rpm.cmake +++ b/cmake/cpack_rpm.cmake @@ -86,6 +86,7 @@ SET(ignored "%ignore ${CMAKE_INSTALL_PREFIX}/share/man" "%ignore ${CMAKE_INSTALL_PREFIX}/share/man/man1*" "%ignore ${CMAKE_INSTALL_PREFIX}/share/man/man8*" + "%ignore ${CMAKE_INSTALL_PREFIX}/share/pkgconfig" ) SET(CPACK_RPM_server_USER_FILELIST ${ignored} "%config(noreplace) ${INSTALL_SYSCONF2DIR}/*") From 1fea7e785f2de734fb3e278c9c0df2776f565c06 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 Jan 2016 16:08:24 +0100 Subject: [PATCH 37/83] cleanup: create LEX_USER::reset_auth() as this is used quite often --- sql/sql_acl.cc | 6 ++---- sql/sql_class.cc | 4 +--- sql/sql_parse.cc | 6 ++---- sql/sql_yacc.yy | 16 ++++------------ sql/structs.h | 6 ++++++ 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index cdfd731ffcb..71dedd30aaf 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -984,7 +984,7 @@ static bool fix_user_plugin_ptr(ACL_USER *user) - if user->plugin is specified, user->auth is the plugin auth data. - if user->plugin is mysql_native_password or mysql_old_password, - user->auth if the password hash, and LEX_USER is transformed + user->auth is the password hash, and LEX_USER is transformed to match the next case (that is, user->plugin is cleared). - if user->plugin is NOT specified, built-in auth is assumed, that is mysql_native_password or mysql_old_password. In that case, @@ -10156,9 +10156,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str)); thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str)); - combo->password= null_lex_str; - combo->plugin= empty_lex_str; - combo->auth= empty_lex_str; + combo->reset_auth(); if(au) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 729b23cf993..421ad627190 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5160,9 +5160,7 @@ void THD::get_definer(LEX_USER *definer, bool role) { definer->user = invoker_user; definer->host= invoker_host; - definer->password= null_lex_str; - definer->plugin= empty_lex_str; - definer->auth= empty_lex_str; + definer->reset_auth(); } else #endif diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ba9dc7eb854..663864a95bd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8949,9 +8949,7 @@ void get_default_definer(THD *thd, LEX_USER *definer, bool role) } definer->user.length= strlen(definer->user.str); - definer->password= null_lex_str; - definer->plugin= empty_lex_str; - definer->auth= empty_lex_str; + definer->reset_auth(); } @@ -9009,7 +9007,7 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) definer->user= *user_name; definer->host= *host_name; - definer->password= null_lex_str; + definer->reset_auth(); return definer; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 302c2fda7f0..241016672d8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -14139,9 +14139,7 @@ user_maybe_role: MYSQL_YYABORT; $$->user = $1; $$->host= null_lex_str; // User or Role, see get_current_user() - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; + $$->reset_auth(); if (check_string_char_length(&$$->user, ER_USERNAME, username_char_length, @@ -14153,9 +14151,7 @@ user_maybe_role: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; $$->user = $1; $$->host=$3; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; + $$->reset_auth(); if (check_string_char_length(&$$->user, ER_USERNAME, username_char_length, @@ -15417,9 +15413,7 @@ current_role: if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))) MYSQL_YYABORT; $$->user= current_role; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; + $$->reset_auth(); } ; @@ -15438,9 +15432,7 @@ grant_role: MYSQL_YYABORT; $$->user = $1; $$->host= empty_lex_str; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; + $$->reset_auth(); if (check_string_char_length(&$$->user, ER_USERNAME, username_char_length, diff --git a/sql/structs.h b/sql/structs.h index 191463af344..986b0d64bb0 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -211,6 +211,12 @@ typedef struct st_lex_user { else l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf; } + void reset_auth() + { + password.length= plugin.length= auth.length= 0; + password.str= 0; + plugin.str= auth.str= const_cast(""); + } } LEX_USER; /* From c37107380abf8f4c04c270ee7afdf8e16042c943 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 Jan 2016 16:24:32 +0100 Subject: [PATCH 38/83] cleanup: LEX_USER::pwtext and LEX_USER::pwhash Was: * LEX_USER::password was storing sometimes plaintext password and sometimes password hash * LEX_USER::auth was storing sometimes password hash and sometimes plugin authentication string Now: * LEX_USER::pwtext stores the password in plain-text * LEX_USER::pwhash stores the password hash * LEX_USER::auth stores the plugin authentication string --- sql/sql_acl.cc | 86 +++++++++++++++++++++++++++---------------------- sql/sql_yacc.yy | 15 +++++---- sql/structs.h | 7 ++-- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 71dedd30aaf..de228a21095 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -988,12 +988,13 @@ static bool fix_user_plugin_ptr(ACL_USER *user) to match the next case (that is, user->plugin is cleared). - if user->plugin is NOT specified, built-in auth is assumed, that is mysql_native_password or mysql_old_password. In that case, - user->auth is the password hash. And user->password is the original - plain-text password. Either one can be set or even both. + user->pwhash is the password hash. And user->pwtext is the original + plain-text password. Either one can be set or both. Upon exiting this function: - - user->password is the password hash, as the mysql.user.password column + - user->pwtext is left untouched + - user->pwhash is the password hash, as the mysql.user.password column - user->plugin is the plugin name, as the mysql.user.plugin column - user->auth is the plugin auth data, as the mysql.user.authentication_string column */ @@ -1001,6 +1002,9 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) { size_t check_length; + DBUG_ASSERT(user->plugin.length || !user->auth.length); + DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); + if (my_strcasecmp(system_charset_info, user->plugin.str, native_password_plugin_name.str) == 0) check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; @@ -1011,21 +1015,28 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) else if (user->plugin.length) return false; // nothing else to do - else - if (user->auth.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) - check_length= 0; // length is valid, no need to re-check + else if (thd->variables.old_passwords == 1 || + user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; else check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - if (check_length && user->auth.length && user->auth.length != check_length) + if (user->plugin.length) + { + user->pwhash= user->auth; + user->plugin= empty_lex_str; + user->auth= empty_lex_str; + } + + if (user->pwhash.length && user->pwhash.length != check_length) { my_error(ER_PASSWD_LENGTH, MYF(0), check_length); return true; } - if (user->password.length || !user->auth.length) + if (user->pwtext.length || !user->pwhash.length) { - if (validate_password(&user->user, &user->password)) + if (validate_password(&user->user, &user->pwtext)) { my_error(ER_NOT_VALID_PASSWORD, MYF(0)); return true; @@ -1040,7 +1051,7 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) } } - if (user->password.length && !user->auth.length) + if (user->pwtext.length && !user->pwhash.length) { size_t scramble_length; void (*make_scramble)(char *, const char *, size_t); @@ -1059,14 +1070,11 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) char *buff= (char *) thd->alloc(scramble_length + 1); if (buff == NULL) return true; - make_scramble(buff, user->password.str, user->password.length); - user->auth.str= buff; - user->auth.length= scramble_length; + make_scramble(buff, user->pwtext.str, user->pwtext.length); + user->pwhash.str= buff; + user->pwhash.length= scramble_length; } - user->password= user->auth.length ? user->auth : null_lex_str; - user->plugin= empty_lex_str; - user->auth= empty_lex_str; return false; } @@ -2767,7 +2775,7 @@ bool change_password(THD *thd, LEX_USER *user) const CSET_STRING query_save __attribute__((unused)) = thd->query_string; DBUG_ENTER("change_password"); DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", - user->host.str, user->user.str, user->password.str)); + user->host.str, user->user.str, user->pwhash.str)); DBUG_ASSERT(user->host.str != 0); // Ensured by parent /* @@ -2784,7 +2792,7 @@ bool change_password(THD *thd, LEX_USER *user) { query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", safe_str(user->user.str), safe_str(user->host.str), - safe_str(user->password.str)); + safe_str(user->pwhash.str)); } if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) @@ -2812,10 +2820,10 @@ bool change_password(THD *thd, LEX_USER *user) if (acl_user->plugin.str == native_password_plugin_name.str || acl_user->plugin.str == old_password_plugin_name.str) { - acl_user->auth_string.str= strmake_root(&acl_memroot, user->password.str, user->password.length); - acl_user->auth_string.length= user->password.length; - set_user_salt(acl_user, user->password.str, user->password.length); - set_user_plugin(acl_user, user->password.length); + acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length); + acl_user->auth_string.length= user->pwhash.length; + set_user_salt(acl_user, user->pwhash.str, user->pwhash.length); + set_user_plugin(acl_user, user->pwhash.length); } else push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, @@ -2825,7 +2833,7 @@ bool change_password(THD *thd, LEX_USER *user) if (update_user_table(thd, tables[USER_TABLE].table, safe_str(acl_user->host.hostname), safe_str(acl_user->user.str), - user->password.str, user->password.length)) + user->pwhash.str, user->pwhash.length)) { mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ goto end; @@ -3366,17 +3374,18 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, mysql_mutex_assert_owner(&acl_cache->lock); - if (combo.password.str && combo.password.str[0]) + if (combo.pwhash.str && combo.pwhash.str[0]) { - if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && - combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && + combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { + DBUG_ASSERT(0); my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); DBUG_RETURN(-1); } } else - combo.password= empty_lex_str; + combo.pwhash= empty_lex_str; /* if the user table is not up to date, we can't handle role updates */ if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role) @@ -3418,7 +3427,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, see also test_if_create_new_users() */ - else if (!combo.password.length && !combo.plugin.length && no_auto_create) + else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create) { my_error(ER_PASSWORD_NO_MATCH, MYF(0)); goto end; @@ -3465,8 +3474,8 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, } rights= get_access(table, 3, &next_field); DBUG_PRINT("info",("table fields: %d",table->s->fields)); - if (combo.password.str[0]) - table->field[2]->store(combo.password.str, combo.password.length, system_charset_info); + if (combo.pwhash.str[0]) + table->field[2]->store(combo.pwhash.str, combo.pwhash.length, system_charset_info); if (table->s->fields >= 31) /* From 4.0.0 we have more fields */ { /* We write down SSL related ACL stuff */ @@ -3529,14 +3538,14 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, table->field[next_field + 1]->set_notnull(); if (combo.plugin.str[0]) { - DBUG_ASSERT(combo.password.str[0] == 0); + DBUG_ASSERT(combo.pwhash.str[0] == 0); table->field[2]->reset(); table->field[next_field]->store(combo.plugin.str, combo.plugin.length, system_charset_info); table->field[next_field + 1]->store(combo.auth.str, combo.auth.length, system_charset_info); } - if (combo.password.str[0]) + if (combo.pwhash.str[0]) { DBUG_ASSERT(combo.plugin.str[0] == 0); table->field[next_field]->reset(); @@ -3605,7 +3614,7 @@ end: acl_update_role(combo.user.str, rights); else acl_update_user(combo.user.str, combo.host.str, - combo.password.str, combo.password.length, + combo.pwhash.str, combo.pwhash.length, lex->ssl_type, lex->ssl_cipher, lex->x509_issuer, @@ -3621,7 +3630,7 @@ end: acl_insert_role(combo.user.str, rights); else acl_insert_user(combo.user.str, combo.host.str, - combo.password.str, combo.password.length, + combo.pwhash.str, combo.pwhash.length, lex->ssl_type, lex->ssl_cipher, lex->x509_issuer, @@ -5679,7 +5688,7 @@ static bool merge_one_role_privileges(ACL_ROLE *grantee) static bool has_auth(LEX_USER *user, LEX *lex) { - return user->password.str || user->plugin.length || user->auth.length || + return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length || lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher || lex->x509_issuer || lex->x509_subject || lex->mqh.specified_limits; @@ -5690,7 +5699,8 @@ static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd) if (to != from) { /* preserve authentication information, if LEX_USER was reallocated */ - to->password= from->password; + to->pwtext= from->pwtext; + to->pwhash= from->pwhash; to->plugin= from->plugin; to->auth= from->auth; } @@ -10160,9 +10170,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, if(au) { - if (au->plugin.str != native_password_plugin_name.str && - au->plugin.str != old_password_plugin_name.str) - combo->plugin= au->plugin; + combo->plugin= au->plugin; combo->auth= au->auth_string; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 241016672d8..876cd2bf55a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -15067,14 +15067,14 @@ opt_for_user: ; text_or_password: - TEXT_STRING { Lex->definer->auth= $1;} - | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->password= $3; } + TEXT_STRING { Lex->definer->pwhash= $1;} + | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } | OLD_PASSWORD_SYM '(' TEXT_STRING ')' { - Lex->definer->password= $3; - Lex->definer->auth.str= Item_func_password::alloc(thd, + Lex->definer->pwtext= $3; + Lex->definer->pwhash.str= Item_func_password::alloc(thd, $3.str, $3.length, Item_func_password::OLD); - Lex->definer->auth.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } ; @@ -15648,14 +15648,15 @@ using_or_as: USING | AS ; grant_user: user IDENTIFIED_SYM BY TEXT_STRING { - $$=$1; $1->password=$4; + $$= $1; + $1->pwtext= $4; if (Lex->sql_command == SQLCOM_REVOKE) MYSQL_YYABORT; } | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING { $$= $1; - $1->auth= $5; + $1->pwhash= $5; } | user IDENTIFIED_SYM via_or_with ident_or_text { diff --git a/sql/structs.h b/sql/structs.h index 986b0d64bb0..c1c832d07ec 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -202,7 +202,8 @@ extern const char *show_comp_option_name[]; typedef int *(*update_var)(THD *, struct st_mysql_show_var *); typedef struct st_lex_user { - LEX_STRING user, host, password, plugin, auth; + LEX_STRING user, host, plugin, auth; + LEX_STRING pwtext, pwhash; bool is_role() { return user.str[0] && !host.str[0]; } void set_lex_string(LEX_STRING *l, char *buf) { @@ -213,8 +214,8 @@ typedef struct st_lex_user { } void reset_auth() { - password.length= plugin.length= auth.length= 0; - password.str= 0; + pwtext.length= pwhash.length= plugin.length= auth.length= 0; + pwtext.str= pwhash.str= 0; plugin.str= auth.str= const_cast(""); } } LEX_USER; From 666b9663f94c2a4f39d331b28fabd09f2dc43bf2 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 25 Jan 2016 19:03:33 +0100 Subject: [PATCH 39/83] update test results --- mysql-test/suite/plugins/r/thread_pool_server_audit.result | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/thread_pool_server_audit.result b/mysql-test/suite/plugins/r/thread_pool_server_audit.result index 68af53048c5..2577a36cad4 100644 --- a/mysql-test/suite/plugins/r/thread_pool_server_audit.result +++ b/mysql-test/suite/plugins/r/thread_pool_server_audit.result @@ -232,9 +232,9 @@ Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,no_such_user,localhost,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users=\'odin, root, dva, tri\'',0 TIME,HOSTNAME,root,localhost,ID,ID,CREATE,test,t2, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create table t2 (id int)',0 @@ -292,7 +292,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,READ,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,proc, TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,event, TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop database sa_db',0 -TIME,HOSTNAME,,,ID,0,DISCONNECT,,,0 +TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,,,0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'create database sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'use sa_db',0 TIME,HOSTNAME,root,localhost,ID,ID,WRITE,mysql,user, From d5b1b1ac6022163fb67603db3bd4235aff1162fb Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 25 Jan 2016 22:57:47 +0100 Subject: [PATCH 40/83] stack too small on labrador (again!) increase the thread stack a bit for main.signal_demo3 and sys_vars.max_sp_recursion_depth_func to stop failing --- include/my_pthread.h | 2 +- mysql-test/r/mysqld--help,win.rdiff | 2 +- mysql-test/r/mysqld--help.result | 2 +- .../suite/sys_vars/r/sysvars_server_embedded.result | 4 ++-- .../suite/sys_vars/r/sysvars_server_notembedded.result | 4 ++-- mysql-test/suite/sys_vars/r/thread_stack_basic.result | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/my_pthread.h b/include/my_pthread.h index 374529c573a..37576ac3cb4 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -714,7 +714,7 @@ extern void my_mutex_end(void); We need to have at least 256K stack to handle calls to myisamchk_init() with the current number of keys and key parts. */ -#define DEFAULT_THREAD_STACK (288*1024L) +#define DEFAULT_THREAD_STACK (289*1024L) #endif #define MY_PTHREAD_LOCK_READ 0 diff --git a/mysql-test/r/mysqld--help,win.rdiff b/mysql-test/r/mysqld--help,win.rdiff index 57adfd7a2f6..a09b5603869 100644 --- a/mysql-test/r/mysqld--help,win.rdiff +++ b/mysql-test/r/mysqld--help,win.rdiff @@ -133,6 +133,6 @@ -thread-pool-oversubscribe 3 -thread-pool-stall-limit 500 +thread-pool-min-threads 1 - thread-stack 294912 + thread-stack 295936 time-format %H:%i:%s timed-mutexes FALSE diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 6d0dbe8be42..bef03904a72 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1430,7 +1430,7 @@ thread-pool-idle-timeout 60 thread-pool-max-threads 1000 thread-pool-oversubscribe 3 thread-pool-stall-limit 500 -thread-stack 294912 +thread-stack 295936 time-format %H:%i:%s timed-mutexes FALSE tmp-table-size 16777216 diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 545a90107d2..fd003588668 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3861,9 +3861,9 @@ READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME THREAD_STACK SESSION_VALUE NULL -GLOBAL_VALUE 294912 +GLOBAL_VALUE 295936 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 294912 +DEFAULT_VALUE 295936 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The stack size for each thread diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 741a0c61278..26e67fc2fdb 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -4631,9 +4631,9 @@ READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME THREAD_STACK SESSION_VALUE NULL -GLOBAL_VALUE 294912 +GLOBAL_VALUE 295936 GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE 294912 +DEFAULT_VALUE 295936 VARIABLE_SCOPE GLOBAL VARIABLE_TYPE BIGINT UNSIGNED VARIABLE_COMMENT The stack size for each thread diff --git a/mysql-test/suite/sys_vars/r/thread_stack_basic.result b/mysql-test/suite/sys_vars/r/thread_stack_basic.result index 4d974f66a00..d5a30bbaf6e 100644 --- a/mysql-test/suite/sys_vars/r/thread_stack_basic.result +++ b/mysql-test/suite/sys_vars/r/thread_stack_basic.result @@ -1,20 +1,20 @@ select @@global.thread_stack; @@global.thread_stack -294912 +295936 select @@session.thread_stack; ERROR HY000: Variable 'thread_stack' is a GLOBAL variable show global variables like 'thread_stack'; Variable_name Value -thread_stack 294912 +thread_stack 295936 show session variables like 'thread_stack'; Variable_name Value -thread_stack 294912 +thread_stack 295936 select * from information_schema.global_variables where variable_name='thread_stack'; VARIABLE_NAME VARIABLE_VALUE -THREAD_STACK 294912 +THREAD_STACK 295936 select * from information_schema.session_variables where variable_name='thread_stack'; VARIABLE_NAME VARIABLE_VALUE -THREAD_STACK 294912 +THREAD_STACK 295936 set global thread_stack=1; ERROR HY000: Variable 'thread_stack' is a read only variable set session thread_stack=1; From d14c4c7874b0de1da452499967b8000a4ac83d67 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 Jan 2016 20:00:35 +0100 Subject: [PATCH 41/83] cleanup: move all password validation logic into one function --- sql/sql_acl.cc | 64 ++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index de228a21095..ca1763209b9 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -874,6 +874,17 @@ static void free_acl_role(ACL_ROLE *role) delete_dynamic(&(role->parent_grantee)); } +static my_bool check_if_exists(THD *, plugin_ref, void *) +{ + return TRUE; +} + +static bool has_validation_plugins() +{ + return plugin_foreach(NULL, check_if_exists, + MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); +} + struct validation_data { LEX_STRING *user, *password; }; static my_bool do_validate(THD *, plugin_ref plugin, void *arg) @@ -885,22 +896,27 @@ static my_bool do_validate(THD *, plugin_ref plugin, void *arg) } -static bool validate_password(LEX_STRING *user, LEX_STRING *password) +static bool validate_password(LEX_USER *user) { - struct validation_data data= { user, password }; - return plugin_foreach(NULL, do_validate, - MariaDB_PASSWORD_VALIDATION_PLUGIN, &data); -} - -static my_bool check_if_exists(THD *, plugin_ref, void *) -{ - return TRUE; -} - -static bool has_validation_plugins() -{ - return plugin_foreach(NULL, check_if_exists, - MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); + if (user->pwtext.length || !user->pwhash.length) + { + struct validation_data data= { &user->user, &user->pwtext }; + if (plugin_foreach(NULL, do_validate, + MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) + { + my_error(ER_NOT_VALID_PASSWORD, MYF(0)); + return true; + } + } + else + { + if (strict_password_validation && has_validation_plugins()) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation"); + return true; + } + } + return false; } /** @@ -1034,22 +1050,8 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) return true; } - if (user->pwtext.length || !user->pwhash.length) - { - if (validate_password(&user->user, &user->pwtext)) - { - my_error(ER_NOT_VALID_PASSWORD, MYF(0)); - return true; - } - } - else - { - if (strict_password_validation && has_validation_plugins()) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation"); - return true; - } - } + if (validate_password(user)) + return true; if (user->pwtext.length && !user->pwhash.length) { From 68910e7092a3741fff56632b84f5eb340ef99245 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 Jan 2016 20:43:19 +0100 Subject: [PATCH 42/83] MDEV-9273 ERROR 1819 on grant statment for existing user Cannot do password validation in fix_lex_user(), we don't know there what "GRANT ... TO user" means - creating a new user with an empty password (need validation) or granting privileges to an existing user (no validation needed). Move validation down into replace_user_table(). And copy into check_change_password(). --- .../suite/plugins/r/simple_password_check.result | 3 ++- mysql-test/suite/plugins/t/simple_password_check.test | 3 ++- sql/sql_acl.cc | 10 ++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/plugins/r/simple_password_check.result b/mysql-test/suite/plugins/r/simple_password_check.result index c74471a651a..c74b4e99992 100644 --- a/mysql-test/suite/plugins/r/simple_password_check.result +++ b/mysql-test/suite/plugins/r/simple_password_check.result @@ -79,6 +79,7 @@ ERROR HY000: Your password does not satisfy the current policy requirements grant select on *.* to `BarFoo1!` identified by 'FooBar1!'; drop user `BarFoo1!`; create user foo1 identified by 'aA.12345'; +grant select on *.* to foo1; drop user foo1; set global simple_password_check_digits=3; set global simple_password_check_letters_same_case=3; @@ -129,7 +130,7 @@ ERROR HY000: The MariaDB server is running with the --strict-password-validation create user foo2 identified with mysql_native_password using ''; ERROR HY000: Your password does not satisfy the current policy requirements grant select on *.* to foo2 identified with mysql_old_password; -ERROR HY000: Your password does not satisfy the current policy requirements +ERROR 28000: Can't find any matching row in the user table update mysql.user set password='xxx' where user='foo1'; set global strict_password_validation=0; set password for foo1 = ''; diff --git a/mysql-test/suite/plugins/t/simple_password_check.test b/mysql-test/suite/plugins/t/simple_password_check.test index c31e31154e3..9854f93efe8 100644 --- a/mysql-test/suite/plugins/t/simple_password_check.test +++ b/mysql-test/suite/plugins/t/simple_password_check.test @@ -26,6 +26,7 @@ grant select on *.* to `BarFoo1!` identified by 'FooBar1!'; drop user `BarFoo1!`; create user foo1 identified by 'aA.12345'; +grant select on *.* to foo1; drop user foo1; set global simple_password_check_digits=3; @@ -78,7 +79,7 @@ create user foo2 identified with mysql_native_password using '111111111111111111 grant select on *.* to foo2 identified with mysql_old_password using '2222222222222222'; --error ER_NOT_VALID_PASSWORD create user foo2 identified with mysql_native_password using ''; ---error ER_NOT_VALID_PASSWORD +--error ER_PASSWORD_NO_MATCH grant select on *.* to foo2 identified with mysql_old_password; # direct updates are not protected diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ca1763209b9..c6793241f70 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1050,9 +1050,6 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) return true; } - if (validate_password(user)) - return true; - if (user->pwtext.length && !user->pwhash.length) { size_t scramble_length; @@ -2747,7 +2744,8 @@ bool check_change_password(THD *thd, LEX_USER *user) { LEX_USER *real_user= get_current_user(thd, user); - if (fix_and_copy_user(real_user, user, thd)) + if (fix_and_copy_user(real_user, user, thd) || + validate_password(real_user)) return true; *user= *real_user; @@ -3461,6 +3459,10 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, store_record(table,record[1]); // Save copy for update } + if (!old_row_exists || combo.pwtext.length || combo.pwhash.length) + if (validate_password(&combo)) + goto end; + /* Update table columns with new privileges */ Field **tmp_field; From 5da7c346c7a81c1cb7823e50f32f33f266cd26dd Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 23 Jan 2016 20:40:01 +0100 Subject: [PATCH 43/83] MDEV-9428 NO_AUTO_VALUE_ON_ZERO is ignored when a trigger before insert is defined Don't compare "field == table->next_number_field" because the field can be special nullable field copy created by the trigger. Compare field_index values instead. --- mysql-test/r/trigger_null-8605.result | 13 +++++++++++++ mysql-test/t/trigger_null-8605.test | 14 ++++++++++++++ sql/sql_base.cc | 5 ++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/trigger_null-8605.result b/mysql-test/r/trigger_null-8605.result index 79627eac455..3b630166663 100644 --- a/mysql-test/r/trigger_null-8605.result +++ b/mysql-test/r/trigger_null-8605.result @@ -309,3 +309,16 @@ a b c 1 1 1 2 5 3 drop table t1; +set session sql_mode ='no_auto_value_on_zero'; +create table t1 (id int unsigned auto_increment primary key); +insert t1 values (0); +select * from t1; +id +0 +delete from t1; +create trigger t1_bi before insert on t1 for each row begin end; +insert t1 values (0); +select * from t1; +id +0 +drop table t1; diff --git a/mysql-test/t/trigger_null-8605.test b/mysql-test/t/trigger_null-8605.test index 6861676145f..f4935b9c975 100644 --- a/mysql-test/t/trigger_null-8605.test +++ b/mysql-test/t/trigger_null-8605.test @@ -325,3 +325,17 @@ insert t1 values (9, 9, 2); insert t1 (a,c) values (9, 3); select * from t1; drop table t1; + +# +# MDEV-9428 NO_AUTO_VALUE_ON_ZERO is ignored when a trigger before insert is defined +# +set session sql_mode ='no_auto_value_on_zero'; +create table t1 (id int unsigned auto_increment primary key); +insert t1 values (0); +select * from t1; +delete from t1; +create trigger t1_bi before insert on t1 for each row begin end; +insert t1 values (0); +select * from t1; +drop table t1; + diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b05bbfaead6..6dfd2622805 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8962,6 +8962,9 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, Item *value; Field *field; bool abort_on_warning_saved= thd->abort_on_warning; + uint autoinc_index= table->next_number_field + ? table->next_number_field->field_index + : ~0U; DBUG_ENTER("fill_record"); if (!*ptr) @@ -8987,7 +8990,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, DBUG_ASSERT(field->table == table); value=v++; - if (field == table->next_number_field) + if (field->field_index == autoinc_index) table->auto_increment_field_not_null= TRUE; if (field->vcol_info && value->type() != Item::DEFAULT_VALUE_ITEM && From ed4fb9b248d769d71bc3d8b20c0e2eaa3ac1e444 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 Jan 2016 19:53:32 +0100 Subject: [PATCH 44/83] MDEV-9259 Add missing mroonga files to Debian packaging in 10.1 --- debian/mariadb-server-10.1.files.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/debian/mariadb-server-10.1.files.in b/debian/mariadb-server-10.1.files.in index dbe5fb58ae0..3b143c5c9cf 100644 --- a/debian/mariadb-server-10.1.files.in +++ b/debian/mariadb-server-10.1.files.in @@ -6,6 +6,7 @@ usr/lib/mysql/plugin/ha_blackhole.so usr/lib/mysql/plugin/ha_federated.so usr/lib/mysql/plugin/ha_federatedx.so usr/lib/mysql/plugin/ha_innodb.so +usr/lib/mysql/plugin/ha_mroonga.so usr/lib/mysql/plugin/ha_sphinx.so usr/lib/mysql/plugin/handlersocket.so usr/lib/mysql/plugin/locales.so @@ -85,6 +86,8 @@ usr/share/mysql/echo_stderr usr/share/mysql/errmsg-utf8.txt usr/share/mysql/fill_help_tables.sql usr/share/mysql/maria_add_gis_sp_bootstrap.sql +usr/share/mysql/mroonga/install.sql +usr/share/mysql/mroonga/uninstall.sql usr/share/mysql/mysql_system_tables_data.sql usr/share/mysql/mysql_system_tables.sql usr/share/mysql/mysql_performance_tables.sql From 2ff65ba53190ad897fc41f546fc4a7bdc80d9054 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 24 Jan 2016 17:41:11 +0100 Subject: [PATCH 45/83] MDEV-9299 Test main.events_2 incompatible with Debian reproducibility testing framework Debian tests are run in 2017, update the test to use 2027 as a "future". --- mysql-test/r/events_2.result | 4 ++-- mysql-test/t/events_2.test | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/events_2.result b/mysql-test/r/events_2.result index 9a1f9d954e8..c57893ccf6f 100644 --- a/mysql-test/r/events_2.result +++ b/mysql-test/r/events_2.result @@ -2,10 +2,10 @@ set sql_mode=""; drop database if exists events_test; create database events_test; use events_test; -create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; +create event e_26 on schedule at '2027-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; db name body definer convert_tz(execute_at, 'UTC', 'SYSTEM') on_completion -events_test e_26 set @a = 5 root@localhost 2017-01-01 00:00:00 DROP +events_test e_26 set @a = 5 root@localhost 2027-01-01 00:00:00 DROP drop event e_26; create event e_26 on schedule at NULL disable do set @a = 5; ERROR HY000: Incorrect AT value: 'NULL' diff --git a/mysql-test/t/events_2.test b/mysql-test/t/events_2.test index ec69a2a5591..7d9ac7c5858 100644 --- a/mysql-test/t/events_2.test +++ b/mysql-test/t/events_2.test @@ -15,7 +15,7 @@ use events_test; # mysql.event intact checking end # -create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; +create event e_26 on schedule at '2027-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; drop event e_26; --error ER_WRONG_VALUE From 71b39066d4320e74e5ad73f4f5a6d436e32f14f3 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 25 Jan 2016 20:17:55 +0100 Subject: [PATCH 46/83] after merge fixes fix compiler warnings --- storage/connect/catalog.h | 6 +++--- storage/connect/tabmysql.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/connect/catalog.h b/storage/connect/catalog.h index 6488b513ba9..70304c410cc 100644 --- a/storage/connect/catalog.h +++ b/storage/connect/catalog.h @@ -77,8 +77,8 @@ class DllExport CATALOG { virtual bool ClearName(PGLOBAL, PSZ) {return true;} virtual PRELDEF MakeOneTableDesc(PGLOBAL, LPCSTR, LPCSTR) {return NULL;} virtual PRELDEF GetTableDescEx(PGLOBAL, PTABLE) {return NULL;} - virtual PRELDEF GetTableDesc(PGLOBAL, LPCSTR, LPCSTR, - PRELDEF* = NULL) {return NULL;} + //virtual PRELDEF GetTableDesc(PGLOBAL, LPCSTR, LPCSTR, + // PRELDEF* = NULL) {return NULL;} virtual PRELDEF GetFirstTable(PGLOBAL) {return NULL;} virtual PRELDEF GetNextTable(PGLOBAL) {return NULL;} virtual bool TestCond(PGLOBAL, const char*, const char*) {return true;} @@ -95,7 +95,7 @@ class DllExport CATALOG { protected: virtual bool ClearSection(PGLOBAL, const char*, const char*) {return true;} - virtual PRELDEF MakeTableDesc(PGLOBAL, LPCSTR, LPCSTR) {return NULL;} + //virtual PRELDEF MakeTableDesc(PGLOBAL, LPCSTR, LPCSTR) {return NULL;} // Members char *Cbuf; /* Buffer used for col section */ diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index b9cede52a21..5a8bb17bd50 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -334,7 +334,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) Delayed = !!GetIntCatInfo("Delayed", 0); } else { // MYSQL access from a PROXY table - Database = GetStringCatInfo(g, "Database", Schema ? Schema : "*"); + Database = GetStringCatInfo(g, "Database", Schema ? Schema : (char*)"*"); Isview = GetBoolCatInfo("View", false); // We must get other connection parms from the calling table From c76ab94fa9344d84df74ae3ab54dc876b900e471 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2016 14:36:13 +0100 Subject: [PATCH 47/83] Fix invalid format warnings --- storage/innobase/include/fil0fil.ic | 2 +- storage/xtradb/include/fil0fil.ic | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/include/fil0fil.ic b/storage/innobase/include/fil0fil.ic index a0bfb271d39..0f8c8ff3b00 100644 --- a/storage/innobase/include/fil0fil.ic +++ b/storage/innobase/include/fil0fil.ic @@ -146,7 +146,7 @@ fil_page_type_validate( /* Dump out the page info */ fprintf(stderr, "InnoDB: Space %lu offset %lu name %s page_type %lu page_type_name %s\n" - "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %lu compressed_len %lu\n", + "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %llu compressed_len %lu\n", space, offset, rspace->name, page_type, fil_get_page_type_name(page_type), key_version, page_compressed, page_compressed_encrypted, lsn, compressed_len); fflush(stderr); diff --git a/storage/xtradb/include/fil0fil.ic b/storage/xtradb/include/fil0fil.ic index ec3b7e85e91..2aa00d86b02 100644 --- a/storage/xtradb/include/fil0fil.ic +++ b/storage/xtradb/include/fil0fil.ic @@ -147,7 +147,7 @@ fil_page_type_validate( /* Dump out the page info */ fprintf(stderr, "InnoDB: Space %lu offset %lu name %s page_type %lu page_type_name %s\n" - "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %lu compressed_len %lu\n", + "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %llu compressed_len %lu\n", space, offset, rspace->name, page_type, fil_get_page_type_name(page_type), key_version, page_compressed, page_compressed_encrypted, lsn, compressed_len); fflush(stderr); From 77c75a46cfe9ffa914c73fe129dec743b80ed57b Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 26 Jan 2016 17:52:24 +0400 Subject: [PATCH 48/83] A clean-up patch for Item_func_conv_charset (needed for MDEV-9181) Removing the "conv_charset" member and using collation.collation instead, as they duplicated each other. --- sql/item_strfunc.cc | 6 +++--- sql/item_strfunc.h | 10 ++++++---- storage/spider/spd_db_mysql.cc | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 6c49c02f9aa..9880db7745a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3404,7 +3404,7 @@ String *Item_func_conv_charset::val_str(String *str) String *arg= args[0]->val_str(str); String_copier_for_item copier(current_thd); return ((null_value= args[0]->null_value || - copier.copy_with_warn(conv_charset, &tmp_value, + copier.copy_with_warn(collation.collation, &tmp_value, arg->charset(), arg->ptr(), arg->length(), arg->length()))) ? 0 : &tmp_value; @@ -3412,7 +3412,7 @@ String *Item_func_conv_charset::val_str(String *str) void Item_func_conv_charset::fix_length_and_dec() { - collation.set(conv_charset, DERIVATION_IMPLICIT); + DBUG_ASSERT(collation.derivation == DERIVATION_IMPLICIT); fix_char_length(args[0]->max_char_length()); } @@ -3421,7 +3421,7 @@ void Item_func_conv_charset::print(String *str, enum_query_type query_type) str->append(STRING_WITH_LEN("convert(")); args[0]->print(str, query_type); str->append(STRING_WITH_LEN(" using ")); - str->append(conv_charset->csname); + str->append(collation.collation->csname); str->append(')'); } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 1a9f855dec5..65c286819d3 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -959,20 +959,22 @@ class Item_func_conv_charset :public Item_str_func String tmp_value; public: bool safe; - CHARSET_INFO *conv_charset; // keep it public Item_func_conv_charset(THD *thd, Item *a, CHARSET_INFO *cs): Item_str_func(thd, a) - { conv_charset= cs; use_cached_value= 0; safe= 0; } + { + collation.set(cs, DERIVATION_IMPLICIT); + use_cached_value= 0; safe= 0; + } Item_func_conv_charset(THD *thd, Item *a, CHARSET_INFO *cs, bool cache_if_const): Item_str_func(thd, a) { - conv_charset= cs; + collation.set(cs, DERIVATION_IMPLICIT); if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive()) { uint errors= 0; String tmp, *str= args[0]->val_str(&tmp); if (!str || str_value.copy(str->ptr(), str->length(), - str->charset(), conv_charset, &errors)) + str->charset(), cs, &errors)) null_value= 1; use_cached_value= 1; str_value.mark_as_const(); diff --git a/storage/spider/spd_db_mysql.cc b/storage/spider/spd_db_mysql.cc index 22aef83c7d4..3f56d9a9d89 100644 --- a/storage/spider/spd_db_mysql.cc +++ b/storage/spider/spd_db_mysql.cc @@ -4041,7 +4041,7 @@ int spider_db_mysql_util::open_item_func( { Item_func_conv_charset *item_func_conv_charset = (Item_func_conv_charset *)item_func; - CHARSET_INFO *conv_charset = item_func_conv_charset->conv_charset; + CHARSET_INFO *conv_charset = item_func_conv_charset->collation.collation; uint cset_length = strlen(conv_charset->csname); if (str->reserve(SPIDER_SQL_USING_LEN + cset_length)) DBUG_RETURN(HA_ERR_OUT_OF_MEM); From a095c99301a0acac9b2db9b294f24a8753ebed48 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 26 Jan 2016 17:56:41 +0100 Subject: [PATCH 49/83] Fix packaging for client RPM plugins - provide 'ignored' list --- cmake/plugin.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index cd4e4faaaf0..cd02bcfc4be 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -216,13 +216,13 @@ MACRO(MYSQL_ADD_PLUGIN) # workarounds for cmake issues #13248 and #12864: SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_PROVIDES "cmake_bug_13248" PARENT_SCOPE) SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_OBSOLETES "cmake_bug_13248" PARENT_SCOPE) - - IF(NOT ARG_CLIENT AND NOT ARG_CONFIG AND UNIX) + SET(CPACK_RPM_${ARG_COMPONENT}_USER_FILELIST ${ignored} PARENT_SCOPE) + IF(NOT ARG_CLIENT AND NOT ARG_CONFIG AND UNIX) SET(ARG_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/${target}.cnf") FILE(WRITE ${ARG_CONFIG} "[mariadb]\nplugin-load-add=${ARG_MODULE_OUTPUT_NAME}.so\n") - INSTALL(FILES ${ARG_CONFIG} COMPONENT ${ARG_COMPONENT} DESTINATION ${INSTALL_SYSCONF2DIR}) - SET(CPACK_RPM_${ARG_COMPONENT}_USER_FILELIST ${ignored} "%config(noreplace) ${INSTALL_SYSCONF2DIR}/*" PARENT_SCOPE) - ENDIF() + INSTALL(FILES ${ARG_CONFIG} COMPONENT ${ARG_COMPONENT} DESTINATION ${INSTALL_SYSCONF2DIR}) + SET(CPACK_RPM_${ARG_COMPONENT}_USER_FILELIST ${ignored} "%config(noreplace) ${INSTALL_SYSCONF2DIR}/*" PARENT_SCOPE) + ENDIF() ENDIF() ELSE() SET(ARG_COMPONENT Server) From a4ff37e912ee50d2ccee7fa50a361f9990ac78b4 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 26 Jan 2016 22:33:25 +0400 Subject: [PATCH 50/83] MDEV-6421 SQL_ERROR_LOG doesn't log comments in Events. Change parser so it saves all the query line to the ';' in the sp_instr::m_query. --- mysql-test/r/events_bugs.result | 10 +++++----- .../suite/plugins/r/sql_error_log.result | 10 ++++++++++ mysql-test/suite/plugins/t/sql_error_log.test | 18 ++++++++++++++++++ mysql-test/t/events_bugs.test | 10 +++++----- sql/sql_yacc.yy | 2 +- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index e3599218aac..f1ff0faaa47 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -90,7 +90,7 @@ create table events_smode_test(ev_name char(10), a date); "This should never insert something" create event ee_16407_2 on schedule every 60 second do begin -select get_lock('ee_16407_2', 60) /*ee_16407_2*/; +select get_lock('ee_16407_2', 60); /*ee_16407_2*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values('ee_16407_2','1980-19-02'); end| @@ -99,7 +99,7 @@ ERROR 22007: Incorrect date value: '1980-19-02' for column 'a' at row 1 "This is ok" create event ee_16407_3 on schedule every 60 second do begin -select get_lock('ee_16407_2', 60) /*ee_16407_3*/; +select get_lock('ee_16407_2', 60); /*ee_16407_3*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29'); @@ -108,7 +108,7 @@ set sql_mode=""| "This will insert rows but they will be truncated" create event ee_16407_4 on schedule every 60 second do begin -select get_lock('ee_16407_2', 60) /*ee_16407_4*/; +select get_lock('ee_16407_2', 60); /*ee_16407_4*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956'); end| @@ -156,13 +156,13 @@ create procedure ee_16407_5_pendant() begin insert into events_test.events_smode create procedure ee_16407_6_pendant() begin insert into events_test.events_smode_test values('ee_16407_6','2004-02-29'); end| create event ee_16407_5 on schedule every 60 second do begin -select get_lock('ee_16407_5', 60) /*ee_16407_5*/; +select get_lock('ee_16407_5', 60); /*ee_16407_5*/ select release_lock('ee_16407_5'); call events_test.ee_16407_5_pendant(); end| create event ee_16407_6 on schedule every 60 second do begin -select get_lock('ee_16407_5', 60) /*ee_16407_6*/; +select get_lock('ee_16407_5', 60); /*ee_16407_6*/ select release_lock('ee_16407_5'); call events_test.ee_16407_6_pendant(); end| diff --git a/mysql-test/suite/plugins/r/sql_error_log.result b/mysql-test/suite/plugins/r/sql_error_log.result index 3086b1f2d1f..a583cf91a83 100644 --- a/mysql-test/suite/plugins/r/sql_error_log.result +++ b/mysql-test/suite/plugins/r/sql_error_log.result @@ -35,6 +35,15 @@ SET SQL_MODE = ''; drop table t1; SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'not_exists' AND TABLE_NAME = 'not_exists'; TABLE_NAME +CREATE procedure e1() +BEGIN +START TRANSACTION; +INSERT INTO test.non_exists VALUES (0,0,0) /* e1 */; +COMMIT; +END| +CALL e1(); +ERROR 42S02: Table 'test.non_exists' doesn't exist +DROP PROCEDURE e1; uninstall plugin SQL_ERROR_LOG; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown @@ -45,3 +54,4 @@ TIME HOSTNAME ERROR 1000: new message : RESIGNAL SQLSTATE '40000' SET MYSQL_ERRNO = 1000, MESSAGE_TEXT = 'new message' TIME HOSTNAME ERROR 1366: Incorrect integer value: 'aa' for column 'id' at row 1 : insert into t1 values ('aa') +TIME HOSTNAME ERROR 1146: Table 'test.non_exists' doesn't exist : INSERT INTO test.non_exists VALUES (0,0,0) /* e1 */ diff --git a/mysql-test/suite/plugins/t/sql_error_log.test b/mysql-test/suite/plugins/t/sql_error_log.test index a28e2d2e259..6c83e9655ce 100644 --- a/mysql-test/suite/plugins/t/sql_error_log.test +++ b/mysql-test/suite/plugins/t/sql_error_log.test @@ -48,6 +48,24 @@ drop table t1; SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'not_exists' AND TABLE_NAME = 'not_exists'; +# +# MDEV-6421 SQL_ERROR_LOG doesn't log comments in Events +# actually testing SP call is enough for that. + +DELIMITER |; + +CREATE procedure e1() +BEGIN + START TRANSACTION; + INSERT INTO test.non_exists VALUES (0,0,0) /* e1 */; + COMMIT; +END| + +DELIMITER ;| +--error ER_NO_SUCH_TABLE +CALL e1(); +DROP PROCEDURE e1; + uninstall plugin SQL_ERROR_LOG; let $MYSQLD_DATADIR= `SELECT @@datadir`; diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index dc31556998a..5b5123ad295 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -305,7 +305,7 @@ create table events_smode_test(ev_name char(10), a date); delimiter |; create event ee_16407_2 on schedule every 60 second do begin - select get_lock('ee_16407_2', 60) /*ee_16407_2*/; + select get_lock('ee_16407_2', 60); /*ee_16407_2*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values('ee_16407_2','1980-19-02'); end| @@ -314,7 +314,7 @@ insert into events_test.events_smode_test values ('test','1980-19-02')| --echo "This is ok" create event ee_16407_3 on schedule every 60 second do begin - select get_lock('ee_16407_2', 60) /*ee_16407_3*/; + select get_lock('ee_16407_2', 60); /*ee_16407_3*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-19'); insert into events_test.events_smode_test values ('ee_16407_3','1980-02-29'); @@ -323,7 +323,7 @@ set sql_mode=""| --echo "This will insert rows but they will be truncated" create event ee_16407_4 on schedule every 60 second do begin - select get_lock('ee_16407_2', 60) /*ee_16407_4*/; + select get_lock('ee_16407_2', 60); /*ee_16407_4*/ select release_lock('ee_16407_2'); insert into events_test.events_smode_test values ('ee_16407_4','10-11-1956'); end| @@ -411,13 +411,13 @@ create procedure ee_16407_5_pendant() begin insert into events_test.events_smode create procedure ee_16407_6_pendant() begin insert into events_test.events_smode_test values('ee_16407_6','2004-02-29'); end| create event ee_16407_5 on schedule every 60 second do begin - select get_lock('ee_16407_5', 60) /*ee_16407_5*/; + select get_lock('ee_16407_5', 60); /*ee_16407_5*/ select release_lock('ee_16407_5'); call events_test.ee_16407_5_pendant(); end| create event ee_16407_6 on schedule every 60 second do begin - select get_lock('ee_16407_5', 60) /*ee_16407_6*/; + select get_lock('ee_16407_5', 60); /*ee_16407_6*/ select release_lock('ee_16407_5'); call events_test.ee_16407_6_pendant(); end| diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8e849d76070..d5a73abc5c6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3696,7 +3696,7 @@ sp_proc_stmt_statement: if (yychar == YYEMPTY) i->m_query.length= lip->get_ptr() - sp->m_tmp_query; else - i->m_query.length= lip->get_tok_end() - sp->m_tmp_query; + i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;; if (!(i->m_query.str= strmake_root(thd->mem_root, sp->m_tmp_query, i->m_query.length)) || From df2695495188b0c3459eabb40c8e390f56cddb27 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 26 Jan 2016 16:00:59 +0400 Subject: [PATCH 51/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. The metadata creation part of the mysqld_shww_create separated to be used on the mysqld_stmt_prepare stage. --- sql/sql_prepare.cc | 46 ++++++++++- sql/sql_show.cc | 168 +++++++++++++++++++++++--------------- sql/sql_show.h | 2 + tests/mysql_client_test.c | 9 ++ 4 files changed, 157 insertions(+), 68 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 55f05c3762c..e9ffac39352 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -103,6 +103,7 @@ When one supplies long data for a placeholder: #include "sql_derived.h" // mysql_derived_prepare, // mysql_handle_derived #include "sql_cursor.h" +#include "sql_show.h" #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -1811,6 +1812,42 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } +/** + Validate and prepare for execution CREATE TABLE statement. + + @param stmt prepared statement + @param tables list of tables used in this query + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static int mysql_test_show_create_table(Prepared_statement *stmt, + TABLE_LIST *tables) +{ + DBUG_ENTER("mysql_test_show_create_table"); + THD *thd= stmt->thd; + List fields; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + + if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) + goto err_exit; + + if (send_prep_stmt(stmt, fields.elements) || + thd->protocol->send_result_set_metadata(&fields, Protocol::SEND_EOF) || + thd->protocol->flush()) + goto err_exit; + + DBUG_RETURN(2); + +err_exit: + DBUG_RETURN(1); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2144,7 +2181,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_CREATE_TABLE: res= mysql_test_create_table(stmt); break; - + case SQLCOM_SHOW_CREATE: + res= mysql_test_show_create_table(stmt, tables); + if (res == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index dd7a71f15fa..b2bf5a9ffb0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1091,6 +1091,106 @@ public: }; +/* + Return metadata for CREATE command for table or view + + @param thd Thread handler + @param table_list Table / view + @param field_list resulting list of fields + @param buffer resulting CREATE statement + + @return + @retval 0 OK + @retval 1 Error + +*/ + +bool +mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, + List *field_list, String *buffer) +{ + bool error= TRUE; + MEM_ROOT *mem_root= thd->mem_root; + DBUG_ENTER("mysqld_show_create_get_fields"); + DBUG_PRINT("enter",("db: %s table: %s",table_list->db, + table_list->table_name)); + + /* We want to preserve the tree for views. */ + thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; + + { + /* + Use open_tables() directly rather than + open_normal_and_derived_tables(). This ensures that + close_thread_tables() is not called if open tables fails and the + error is ignored. This allows us to handle broken views nicely. + */ + uint counter; + Show_create_error_handler view_error_suppressor(thd, table_list); + thd->push_internal_handler(&view_error_suppressor); + bool open_error= + open_tables(thd, &table_list, &counter, + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) || + mysql_handle_derived(thd->lex, DT_PREPARE); + thd->pop_internal_handler(); + if (open_error && (thd->killed || thd->is_error())) + goto exit; + } + + /* TODO: add environment variables show when it become possible */ + if (thd->lex->only_view && !table_list->view) + { + my_error(ER_WRONG_OBJECT, MYF(0), + table_list->db, table_list->table_name, "VIEW"); + goto exit; + } + + buffer->length(0); + + if (table_list->view) + buffer->set_charset(table_list->view_creation_ctx->get_client_cs()); + + if ((table_list->view ? + show_create_view(thd, table_list, buffer) : + show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) + goto exit; + + if (table_list->view) + { + field_list->push_back(new (mem_root) + Item_empty_string(thd, "View", NAME_CHAR_LEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Create View", + MY_MAX(buffer->length(),1024)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "character_set_client", + MY_CS_NAME_SIZE), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "collation_connection", + MY_CS_NAME_SIZE), + mem_root); + } + else + { + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Table", NAME_CHAR_LEN), + mem_root); + // 1024 is for not to confuse old clients + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Create Table", + MY_MAX(buffer->length(),1024)), + mem_root); + } + error= FALSE; + +exit: + DBUG_RETURN(error); +} + + /* Return CREATE command for table or view @@ -1125,75 +1225,9 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) */ MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); - /* We want to preserve the tree for views. */ - thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; - { - /* - Use open_tables() directly rather than - open_normal_and_derived_tables(). This ensures that - close_thread_tables() is not called if open tables fails and the - error is ignored. This allows us to handle broken views nicely. - */ - uint counter; - Show_create_error_handler view_error_suppressor(thd, table_list); - thd->push_internal_handler(&view_error_suppressor); - bool open_error= - open_tables(thd, &table_list, &counter, - MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) || - mysql_handle_derived(thd->lex, DT_PREPARE); - thd->pop_internal_handler(); - if (open_error && (thd->killed || thd->is_error())) - goto exit; - } - - /* TODO: add environment variables show when it become possible */ - if (thd->lex->only_view && !table_list->view) - { - my_error(ER_WRONG_OBJECT, MYF(0), - table_list->db, table_list->table_name, "VIEW"); + if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) goto exit; - } - - buffer.length(0); - - if (table_list->view) - buffer.set_charset(table_list->view_creation_ctx->get_client_cs()); - - if ((table_list->view ? - show_create_view(thd, table_list, &buffer) : - show_create_table(thd, table_list, &buffer, NULL, WITHOUT_DB_NAME))) - goto exit; - - if (table_list->view) - { - field_list.push_back(new (mem_root) - Item_empty_string(thd, "View", NAME_CHAR_LEN), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Create View", - MY_MAX(buffer.length(),1024)), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "character_set_client", - MY_CS_NAME_SIZE), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "collation_connection", - MY_CS_NAME_SIZE), - mem_root); - } - else - { - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Table", NAME_CHAR_LEN), - mem_root); - // 1024 is for not to confuse old clients - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Create Table", - MY_MAX(buffer.length(),1024)), - mem_root); - } if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | diff --git a/sql/sql_show.h b/sql/sql_show.h index 029249f4129..06ff909733a 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -85,6 +85,8 @@ bool append_identifier(THD *thd, String *packet, const char *name, uint length); void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild); int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); +bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, + List *field_list, String *buffer); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name, LEX_STRING *orig_db_name, diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index a1a52e832dd..b14b4f4dab9 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -422,6 +422,15 @@ static void test_prepare_simple() mysql_stmt_close(stmt); + /* show create */ + strmov(query, "SHOW CREATE TABLE test_prepare_simple"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + + DIE_UNLESS(mysql_stmt_field_count(stmt) == 2); + + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc); From d227399318fc9cb43afa0fae17ac4d8a7cfdade3 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 26 Jan 2016 23:16:56 +0400 Subject: [PATCH 52/83] Comment fixed. --- sql/sql_prepare.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index e9ffac39352..84173e003fa 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1813,7 +1813,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) /** - Validate and prepare for execution CREATE TABLE statement. + Validate and prepare for execution SHOW CREATE TABLE statement. @param stmt prepared statement @param tables list of tables used in this query From 7d39b28093b06ea67318c257057e93cf891ce896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Tue, 26 Jan 2016 16:33:06 +0200 Subject: [PATCH 53/83] [MDEV-9468]: Client hangs in my_addr_resolve Account for timeout of select returning a 0 error code. --- mysys/my_addr_resolve.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 0b1c60c4131..72b04119855 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -175,6 +175,10 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) sizeof(output) - total_bytes_read); if (extra_bytes_read < 0) return 1; + /* Timeout or max bytes read. */ + if (extra_bytes_read == 0) + break; + total_bytes_read += extra_bytes_read; } @@ -183,7 +187,7 @@ int my_addr_resolve(void *ptr, my_addr_loc *loc) return 1; /* Go through the addr2line response and get the required data. - The response is structured in 2 lnes. The first line contains the function + The response is structured in 2 lines. The first line contains the function name, while the second one contains : */ for (i = 0; i < total_bytes_read; i++) { if (output[i] == '\n') { From b404b236a2093e9bd259ed0d6c2add62dc3005d4 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 27 Jan 2016 11:42:31 +0400 Subject: [PATCH 54/83] MDEV-9332 Bug after upgrade to 10.1.10 --- mysql-test/r/ctype_utf8.result | 7 ++- mysql-test/r/func_str.result | 12 ++++ mysql-test/t/ctype_utf8.test | 4 +- mysql-test/t/func_str.test | 10 +++ sql/sql_string.cc | 110 +++++++++++++++++++++++++++++++-- 5 files changed, 135 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index d95b506f247..90bc6b51177 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -5365,14 +5365,17 @@ DROP TABLE t1; SET sql_mode=default; # # Bug#57687 crash when reporting duplicate group_key error and utf8 -# Make sure to modify this when Bug#58081 is fixed. +# Bug#58081 Duplicate entry error when doing GROUP BY +# MDEV-9332 Bug after upgrade to 10.1.10 # SET NAMES utf8; CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (0), (0), (1), (0), (0); SELECT COUNT(*) FROM t1, t1 t2 GROUP BY INSERT('', t2.a, t1.a, (@@global.max_binlog_size)); -ERROR 23000: Duplicate entry '107374182410737418241' for key 'group_key' +COUNT(*) +20 +5 DROP TABLE t1; # # Bug#11764503 (Bug#57341) Query in EXPLAIN EXTENDED shows wrong characters diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 9bc80613710..678b6d22028 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -4571,5 +4571,17 @@ Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 18446744073709551615) and (format(`test`.`t1`.`a`,0) = '18,446,744,073,709,551,615')) DROP TABLE t1; # +# Bug#58081 Duplicate entry error when doing GROUP BY +# MDEV-9332 Bug after upgrade to 10.1.10 +# +SET NAMES latin1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(0),(1),(0),(0); +SELECT COUNT(*) FROM t1, t1 t2 GROUP BY INSERT('', t2.a, t1.a, @@global.max_binlog_size); +COUNT(*) +20 +5 +DROP TABLE t1; +# # End of 10.1 tests # diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 426985a6d42..014194d78e7 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -1577,12 +1577,12 @@ SET NAMES utf8; --echo # --echo # Bug#57687 crash when reporting duplicate group_key error and utf8 ---echo # Make sure to modify this when Bug#58081 is fixed. +--echo # Bug#58081 Duplicate entry error when doing GROUP BY +--echo # MDEV-9332 Bug after upgrade to 10.1.10 --echo # SET NAMES utf8; CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (0), (0), (1), (0), (0); ---error ER_DUP_ENTRY SELECT COUNT(*) FROM t1, t1 t2 GROUP BY INSERT('', t2.a, t1.a, (@@global.max_binlog_size)); DROP TABLE t1; diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 511f1f31736..2645417f3e5 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -1773,6 +1773,16 @@ EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=18446744073709551615 AND FORMAT(a,0)='18,446,744,073,709,551,615'; DROP TABLE t1; +--echo # +--echo # Bug#58081 Duplicate entry error when doing GROUP BY +--echo # MDEV-9332 Bug after upgrade to 10.1.10 +--echo # +SET NAMES latin1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (0),(0),(1),(0),(0); +SELECT COUNT(*) FROM t1, t1 t2 GROUP BY INSERT('', t2.a, t1.a, @@global.max_binlog_size); +DROP TABLE t1; + --echo # --echo # End of 10.1 tests --echo # diff --git a/sql/sql_string.cc b/sql/sql_string.cc index b14c3afca4b..8cf20e71f55 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -789,12 +789,114 @@ int stringcmp(const String *s,const String *t) } +/** + Return a string which has the same value with "from" and + which is safe to modify, trying to avoid unnecessary allocation + and copying when possible. + + @param to Buffer. Must not be a constant string. + @param from Some existing value. We'll try to reuse it. + Can be a constant or a variable string. + @param from_length The total size that will be possibly needed. + Note, can be 0. + + Note, in some cases "from" and "to" can point to the same object. + + If "from" is a variable string and its allocated memory is enough + to store "from_length" bytes, then "from" is returned as is. + + If "from" is a variable string and its allocated memory is not enough + to store "from_length" bytes, then "from" is reallocated and returned. + + Otherwise (if "from" is a constant string, or looks like a constant string), + then "to" is reallocated to fit "from_length" bytes, the value is copied + from "from" to "to", then "to" is returned. +*/ String *copy_if_not_alloced(String *to,String *from,uint32 from_length) { - if (from->Alloced_length >= from_length) - return from; - if ((from->alloced && (from->Alloced_length != 0)) || !to || from == to) + DBUG_ASSERT(to); + /* + If "from" is a constant string, e.g.: + SELECT INSERT('', , , ); + we should not return it. See MDEV-9332. + + The code below detects different string types: + + a. All constant strings have Alloced_length==0 and alloced==false. + They point to a static memory array, or a mem_root memory, + and should stay untouched until the end of their life cycle. + Not safe to reuse. + + b. Some variable string have Alloced_length==0 and alloced==false initially, + they are not bound to any char array and allocate space on the first use + (and become #d). A typical example of such String is Item::str_value. + This type of string could be reused, but there is no a way to distinguish + them from the true constant strings (#a). + Not safe to reuse. + + c. Some variable strings have Alloced_length>0 and alloced==false. + They point to a fixed size writtable char array (typically on stack) + initially but can later allocate more space on the heap when the + fixed size array is too small (these strings become #d after allocation). + Safe to reuse. + + d. Some variable strings have Alloced_length>0 and alloced==true. + They already store data on the heap. + Safe to reuse. + + e. Some strings can have Alloced_length==0 and alloced==true. + This type of strings allocate space on the heap, but then are marked + as constant strings using String::mark_as_const(). + A typical example - the result of a character set conversion + of a constant string. + Not safe to reuse. + */ + if (from->Alloced_length > 0) // "from" is #c or #d (not a constant) { + if (from->Alloced_length >= from_length) + return from; // #c or #d (large enough to store from_length bytes) + + if (from->alloced) + { + (void) from->realloc(from_length); + return from; // #d (reallocated to fit from_length bytes) + } + /* + "from" is of type #c. It currently points to a writtable char array + (typically on stack), but is too small for "from_length" bytes. + We need to reallocate either "from" or "to". + + "from" typically points to a temporary buffer inside Item_xxx::val_str(), + or to Item::str_value, and thus is "less permanent" than "to". + + Reallocating "to" may give more benifits: + - "to" can point to a "more permanent" storage and can be reused + for multiple rows, e.g. str_buffer in Protocol::send_result_set_row(), + which is passed to val_str() for all string type rows. + - "from" can stay pointing to its original fixed size stack char array, + and thus reduce the total amount of my_alloc/my_free. + */ + } + + if (from == to) + { + /* + Possible string types: + #a not possible (constants should not be passed as "to") + #b possible (a fresh variable with no associated char buffer) + #c possible (a variable with a char buffer, + in case it's smaller than fixed_length) + #d not possible (handled earlier) + #e not possible (constants should not be passed as "to") + + If a string of types #a or #e appears here, that means the caller made + something wrong. Otherwise, it's safe to reallocate and return "to". + + Note, as we can't distinguish between #a and #b for sure, + so we can't assert "not #a", but we can at least assert "not #e". + */ + DBUG_ASSERT(!from->alloced || from->Alloced_length > 0); // Not #e + (void) from->realloc(from_length); return from; } @@ -803,7 +905,7 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length) if ((to->str_length=MY_MIN(from->str_length,from_length))) memcpy(to->Ptr,from->Ptr,to->str_length); to->str_charset=from->str_charset; - return to; + return to; // "from" was of types #a, #b, #e, or small #c. } From 13b79f488b874847d77a7e6d6abe4bd7b5d0aa5a Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 27 Jan 2016 16:33:41 +0200 Subject: [PATCH 55/83] Fixed MDEV-9347 Not all rows returned by the C API Problem was that insert-order (enforced by the optimizer) did not handle the case where the bitmap changed to a new one. Fixed by remembering the last bitmap page used and to force usage of this when inserting new rows --- storage/maria/ma_bitmap.c | 17 ++++++++++++++++- storage/maria/maria_def.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/storage/maria/ma_bitmap.c b/storage/maria/ma_bitmap.c index 7c144ac52a4..60d57b95d86 100644 --- a/storage/maria/ma_bitmap.c +++ b/storage/maria/ma_bitmap.c @@ -1249,7 +1249,7 @@ static my_bool allocate_head(MARIA_FILE_BITMAP *bitmap, uint size, DBUG_ASSERT(size <= FULL_PAGE_SIZE(share)); - if (insert_order) + if (insert_order && bitmap->page == share->last_insert_bitmap) { uint last_insert_page= share->last_insert_page; uint byte= 6 * (last_insert_page / 16); @@ -1315,6 +1315,7 @@ found: { share->last_insert_page= ((uint) (best_data - bitmap->map)) / 6 * 16 + best_pos; + share->last_insert_bitmap= bitmap->page; } fill_block(bitmap, block, best_data, best_pos, best_bits, FULL_HEAD_PAGE); DBUG_RETURN(0); @@ -1614,6 +1615,16 @@ static my_bool find_head(MARIA_HA *info, uint length, uint position) */ block= dynamic_element(&info->bitmap_blocks, position, MARIA_BITMAP_BLOCK *); + if (info->s->base.extra_options & MA_EXTRA_OPTIONS_INSERT_ORDER) + { + if (bitmap->page != info->s->last_insert_bitmap && + _ma_change_bitmap_page(info, bitmap, + info->s->last_insert_bitmap)) + return 1; + /* Don't allocate any blocks from earlier pages */ + info->s->state.first_bitmap_with_space= info->s->last_insert_bitmap; + } + /* We need to have DIRENTRY_SIZE here to take into account that we may need an extra directory entry for the row @@ -3115,6 +3126,10 @@ static my_bool _ma_bitmap_create_missing(MARIA_HA *info, bzero(bitmap->map, bitmap->block_size); bitmap->used_size= 0; #ifndef DBUG_OFF + /* + Make a copy of the page to be able to print out bitmap changes during + debugging + */ memcpy(bitmap->map + bitmap->block_size, bitmap->map, bitmap->block_size); #endif diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index 676927b119a..7337b01a981 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -523,6 +523,7 @@ typedef struct st_maria_share Keep of track of last insert page, used to implement insert order */ uint last_insert_page; + pgcache_page_no_t last_insert_bitmap; } MARIA_SHARE; From ef3ca5c3ba9a03a213634e51ce27e8b95cce3a99 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 13 Oct 2015 10:13:53 +0200 Subject: [PATCH 56/83] New authentication plugin for authentication via named pipe on Windows operating systems. The plugin gets the sid of the client process and considers the user authenticated if the given username matches the username of this sid. --- plugin/auth_pipe/CMakeLists.txt | 19 +++++ plugin/auth_pipe/auth_pipe.c | 126 ++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 plugin/auth_pipe/CMakeLists.txt create mode 100644 plugin/auth_pipe/auth_pipe.c diff --git a/plugin/auth_pipe/CMakeLists.txt b/plugin/auth_pipe/CMakeLists.txt new file mode 100644 index 00000000000..0a2eacad264 --- /dev/null +++ b/plugin/auth_pipe/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU 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 + +IF(WIN32) + MYSQL_ADD_PLUGIN(auth_pipe auth_pipe.c MODULE_ONLY) +ENDIF() diff --git a/plugin/auth_pipe/auth_pipe.c b/plugin/auth_pipe/auth_pipe.c new file mode 100644 index 00000000000..43ae25a9415 --- /dev/null +++ b/plugin/auth_pipe/auth_pipe.c @@ -0,0 +1,126 @@ +/* Copyright (C) 2015 Vladislav Vaintroub, Georg Richter and Monty Program Ab + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 */ + +/** + @file + + auth_pipd authentication plugin. + + Authentication is successful if the connection is done via a named pip and + the owner of the client process matches the user name that was used when + connecting to mysqld. +*/ + + +#include +#include +#include + + + + + +/** + perform the named pipe´based authentication + + This authentication callback performs a named pipe based authentication - + it gets the uid of the client process and considers the user authenticated + if it uses username of this uid. That is - if the user is already + authenticated to the OS (if she is logged in) - she can use MySQL as herself +*/ + +static int pipe_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) +{ + unsigned char *pkt; + PTOKEN_USER pTokenUser= NULL; + HANDLE hToken; + MYSQL_PLUGIN_VIO_INFO vio_info; + DWORD dLength= 0; + int Ret= CR_ERROR; + TCHAR username[UNLEN + 1]; + DWORD username_length= UNLEN + 1; + char domainname[DNLEN + 1]; + DWORD domainsize=DNLEN + 1; + SID_NAME_USE sidnameuse; + + /* no user name yet ? read the client handshake packet with the user name */ + if (info->user_name == 0) + { + if (vio->read_packet(vio, &pkt) < 0) + return CR_ERROR; + } + + info->password_used= PASSWORD_USED_NO_MENTION; + + vio->info(vio, &vio_info); + if (vio_info.protocol != MYSQL_VIO_PIPE) + return CR_ERROR; + + /* get the UID of the client process */ + if (!ImpersonateNamedPipeClient(vio_info.handle)) + return CR_ERROR; + + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken)) + goto end; + + /* determine length of TokenUser */ + GetTokenInformation(hToken, TokenUser, NULL, 0, &dLength); + if (!dLength) + goto end; + + if (!(pTokenUser= (PTOKEN_USER)LocalAlloc(0, dLength))) + goto end; + + if (!GetTokenInformation(hToken, TokenUser, (PVOID)pTokenUser, dLength, &dLength)) + goto end; + + if (!LookupAccountSid(NULL, pTokenUser->User.Sid, username, &username_length, domainname, &domainsize, &sidnameuse)) + goto end; + + Ret= strcmp(username, info->user_name) ? CR_ERROR : CR_OK; +end: + if (pTokenUser) + LocalFree(pTokenUser); + RevertToSelf(); + /* now it's simple as that */ + return Ret; +} + +static struct st_mysql_auth pipe_auth_handler= +{ + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + 0, + pipe_auth +}; + +maria_declare_plugin(socket_auth) +{ + MYSQL_AUTHENTICATION_PLUGIN, + &pipe_auth_handler, + "windows_pipe", + "Vladislav Vaintroub, Georg Richter", + "Windows named pipe based authentication", + PLUGIN_LICENSE_GPL, + NULL, + NULL, + 0x0100, + NULL, + NULL, + "1.0", + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; + From 33e5a8aba2a9c7a4ccf62c7504db36a5a51702f7 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 13 Oct 2015 16:35:53 +0200 Subject: [PATCH 57/83] On Windows SSL works with sockets only, so we shouldn't tell the client that we support SSL when using named pipes or shared memory. --- sql/sql_acl.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c6793241f70..638460df683 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -11260,13 +11260,16 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio, *end++= protocol_version; thd->client_capabilities= CLIENT_BASIC_FLAGS; - + if (opt_using_transactions) thd->client_capabilities|= CLIENT_TRANSACTIONS; thd->client_capabilities|= CAN_CLIENT_COMPRESS; - if (ssl_acceptor_fd) + /* Currently we support SSL with sockets only */ + if (thd->active_vio->type != VIO_TYPE_NAMEDPIPE && + thd->active_vio->type != VIO_TYPE_SHARED_MEMORY && + ssl_acceptor_fd) { thd->client_capabilities |= CLIENT_SSL; thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT; From c1bf5ba27ef2ec189fe890283e68dbd9bd2efca9 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 13 Oct 2015 17:10:16 +0200 Subject: [PATCH 58/83] Revert "On Windows SSL works with sockets only, so we shouldn't tell the client" This reverts commit 2ee149be4e9319208dfcfb77712aa1ef805f6ba8. --- sql/sql_acl.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 638460df683..c6793241f70 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -11260,16 +11260,13 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio, *end++= protocol_version; thd->client_capabilities= CLIENT_BASIC_FLAGS; - + if (opt_using_transactions) thd->client_capabilities|= CLIENT_TRANSACTIONS; thd->client_capabilities|= CAN_CLIENT_COMPRESS; - /* Currently we support SSL with sockets only */ - if (thd->active_vio->type != VIO_TYPE_NAMEDPIPE && - thd->active_vio->type != VIO_TYPE_SHARED_MEMORY && - ssl_acceptor_fd) + if (ssl_acceptor_fd) { thd->client_capabilities |= CLIENT_SSL; thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT; From 4b31e6dc95c7c008cd318822eb9d5a1e845b288c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 27 Jan 2016 15:23:42 +0100 Subject: [PATCH 59/83] Address review comments, add unit test --- mysql-test/r/auth_named_pipe.result | 10 ++++ mysql-test/t/auth_named_pipe-master.opt | 1 + mysql-test/t/auth_named_pipe.test | 23 ++++++++ plugin/auth_pipe/CMakeLists.txt | 18 +------ plugin/auth_pipe/auth_pipe.c | 72 +++++++------------------ 5 files changed, 55 insertions(+), 69 deletions(-) create mode 100644 mysql-test/r/auth_named_pipe.result create mode 100644 mysql-test/t/auth_named_pipe-master.opt create mode 100644 mysql-test/t/auth_named_pipe.test diff --git a/mysql-test/r/auth_named_pipe.result b/mysql-test/r/auth_named_pipe.result new file mode 100644 index 00000000000..8de507e1744 --- /dev/null +++ b/mysql-test/r/auth_named_pipe.result @@ -0,0 +1,10 @@ +INSTALL SONAME 'auth_named_pipe'; +CREATE USER USERNAME IDENTIFIED WITH named_pipe; +SELECT USER(),CURRENT_USER(); +USER() CURRENT_USER() +USERNAME@localhost USERNAME@% +DROP USER USERNAME; +CREATE USER nosuchuser IDENTIFIED WITH named_pipe; +ERROR 28000: Access denied for user 'nosuchuser'@'localhost' +DROP USER nosuchuser; +UNINSTALL SONAME 'auth_named_pipe'; diff --git a/mysql-test/t/auth_named_pipe-master.opt b/mysql-test/t/auth_named_pipe-master.opt new file mode 100644 index 00000000000..e534ae1eae5 --- /dev/null +++ b/mysql-test/t/auth_named_pipe-master.opt @@ -0,0 +1 @@ +--loose-enable-named-pipe diff --git a/mysql-test/t/auth_named_pipe.test b/mysql-test/t/auth_named_pipe.test new file mode 100644 index 00000000000..5473d628246 --- /dev/null +++ b/mysql-test/t/auth_named_pipe.test @@ -0,0 +1,23 @@ +--source include/windows.inc + +INSTALL SONAME 'auth_named_pipe'; + +--replace_result $USERNAME USERNAME +eval CREATE USER $USERNAME IDENTIFIED WITH named_pipe; +# Connect using named pipe, correct username +connect(pipe_con,localhost,$USERNAME,,,,,PIPE); +--replace_result $USERNAME USERNAME +SELECT USER(),CURRENT_USER(); +disconnect pipe_con; +connection default; +--replace_result $USERNAME USERNAME +eval DROP USER $USERNAME; + +# test invalid user name +CREATE USER nosuchuser IDENTIFIED WITH named_pipe; +--disable_query_log +--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR +connect(pipe_con,localhost,nosuchuser,,,,,PIPE); +--enable_query_log +DROP USER nosuchuser; +UNINSTALL SONAME 'auth_named_pipe'; \ No newline at end of file diff --git a/plugin/auth_pipe/CMakeLists.txt b/plugin/auth_pipe/CMakeLists.txt index 0a2eacad264..bbc44d0f5e2 100644 --- a/plugin/auth_pipe/CMakeLists.txt +++ b/plugin/auth_pipe/CMakeLists.txt @@ -1,19 +1,3 @@ -# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU 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 - IF(WIN32) - MYSQL_ADD_PLUGIN(auth_pipe auth_pipe.c MODULE_ONLY) + MYSQL_ADD_PLUGIN(auth_named_pipe auth_pipe.c) ENDIF() diff --git a/plugin/auth_pipe/auth_pipe.c b/plugin/auth_pipe/auth_pipe.c index 43ae25a9415..20c33c07e84 100644 --- a/plugin/auth_pipe/auth_pipe.c +++ b/plugin/auth_pipe/auth_pipe.c @@ -17,44 +17,27 @@ /** @file - auth_pipd authentication plugin. + auth_pipe authentication plugin. - Authentication is successful if the connection is done via a named pip and - the owner of the client process matches the user name that was used when - connecting to mysqld. + Authentication is successful if the connection is done via a named pipe + pipe peer name matches mysql user name */ - #include #include #include - - - /** - perform the named pipe´based authentication - - This authentication callback performs a named pipe based authentication - - it gets the uid of the client process and considers the user authenticated - if it uses username of this uid. That is - if the user is already - authenticated to the OS (if she is logged in) - she can use MySQL as herself + This authentication callback obtains user name using named pipe impersonation */ - static int pipe_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { unsigned char *pkt; - PTOKEN_USER pTokenUser= NULL; - HANDLE hToken; MYSQL_PLUGIN_VIO_INFO vio_info; - DWORD dLength= 0; - int Ret= CR_ERROR; - TCHAR username[UNLEN + 1]; - DWORD username_length= UNLEN + 1; - char domainname[DNLEN + 1]; - DWORD domainsize=DNLEN + 1; - SID_NAME_USE sidnameuse; + char username[UNLEN + 1]; + size_t username_length; + int ret; /* no user name yet ? read the client handshake packet with the user name */ if (info->user_name == 0) @@ -62,41 +45,26 @@ static int pipe_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) if (vio->read_packet(vio, &pkt) < 0) return CR_ERROR; } - info->password_used= PASSWORD_USED_NO_MENTION; - vio->info(vio, &vio_info); if (vio_info.protocol != MYSQL_VIO_PIPE) return CR_ERROR; - /* get the UID of the client process */ + /* Impersonate the named pipe peer, and retrieve the user name */ if (!ImpersonateNamedPipeClient(vio_info.handle)) return CR_ERROR; - - if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken)) - goto end; - - /* determine length of TokenUser */ - GetTokenInformation(hToken, TokenUser, NULL, 0, &dLength); - if (!dLength) - goto end; - if (!(pTokenUser= (PTOKEN_USER)LocalAlloc(0, dLength))) - goto end; - - if (!GetTokenInformation(hToken, TokenUser, (PVOID)pTokenUser, dLength, &dLength)) - goto end; - - if (!LookupAccountSid(NULL, pTokenUser->User.Sid, username, &username_length, domainname, &domainsize, &sidnameuse)) - goto end; - - Ret= strcmp(username, info->user_name) ? CR_ERROR : CR_OK; -end: - if (pTokenUser) - LocalFree(pTokenUser); + username_length= sizeof(username) - 1; + ret= CR_ERROR; + if (GetUserName(username, &username_length)) + { + /* Always compare names case-insensitive on Windows.*/ + if (_stricmp(username, info->user_name) == 0) + ret= CR_OK; + } RevertToSelf(); - /* now it's simple as that */ - return Ret; + + return ret; } static struct st_mysql_auth pipe_auth_handler= @@ -106,11 +74,11 @@ static struct st_mysql_auth pipe_auth_handler= pipe_auth }; -maria_declare_plugin(socket_auth) +maria_declare_plugin(auth_named_pipe) { MYSQL_AUTHENTICATION_PLUGIN, &pipe_auth_handler, - "windows_pipe", + "named_pipe", "Vladislav Vaintroub, Georg Richter", "Windows named pipe based authentication", PLUGIN_LICENSE_GPL, From 02cc921a449d847a27a1a942ff5a5b2b9885b1bb Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 27 Jan 2016 15:14:57 +0100 Subject: [PATCH 60/83] compiler warnings --- sql/sql_show.cc | 1 - storage/innobase/include/fil0fil.ic | 2 +- storage/xtradb/include/fil0fil.ic | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index b2bf5a9ffb0..6cdef2ee301 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1214,7 +1214,6 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) String buffer(buff, sizeof(buff), system_charset_info); List field_list; bool error= TRUE; - MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("mysqld_show_create"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->table_name)); diff --git a/storage/innobase/include/fil0fil.ic b/storage/innobase/include/fil0fil.ic index 0f8c8ff3b00..5654d8f6178 100644 --- a/storage/innobase/include/fil0fil.ic +++ b/storage/innobase/include/fil0fil.ic @@ -148,7 +148,7 @@ fil_page_type_validate( fprintf(stderr, "InnoDB: Space %lu offset %lu name %s page_type %lu page_type_name %s\n" "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %llu compressed_len %lu\n", space, offset, rspace->name, page_type, fil_get_page_type_name(page_type), - key_version, page_compressed, page_compressed_encrypted, lsn, compressed_len); + key_version, page_compressed, page_compressed_encrypted, (ulonglong)lsn, compressed_len); fflush(stderr); ut_ad(page_type == FIL_PAGE_PAGE_COMPRESSED || diff --git a/storage/xtradb/include/fil0fil.ic b/storage/xtradb/include/fil0fil.ic index 2aa00d86b02..7ccc69b9561 100644 --- a/storage/xtradb/include/fil0fil.ic +++ b/storage/xtradb/include/fil0fil.ic @@ -149,7 +149,7 @@ fil_page_type_validate( fprintf(stderr, "InnoDB: Space %lu offset %lu name %s page_type %lu page_type_name %s\n" "InnoDB: key_version %u page_compressed %d page_compressed_encrypted %d lsn %llu compressed_len %lu\n", space, offset, rspace->name, page_type, fil_get_page_type_name(page_type), - key_version, page_compressed, page_compressed_encrypted, lsn, compressed_len); + key_version, page_compressed, page_compressed_encrypted, (ulonglong)lsn, compressed_len); fflush(stderr); ut_ad(page_type == FIL_PAGE_PAGE_COMPRESSED || From eb155661383bcf215d38baf5ff2c5b4413a5552d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 27 Jan 2016 15:26:12 +0100 Subject: [PATCH 61/83] fix failures of ps and ps_1general in --ps-protocol --- sql/sql_prepare.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 84173e003fa..ee0aa2d73fd 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1836,12 +1836,16 @@ static int mysql_test_show_create_table(Prepared_statement *stmt, if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) goto err_exit; - if (send_prep_stmt(stmt, fields.elements) || - thd->protocol->send_result_set_metadata(&fields, Protocol::SEND_EOF) || - thd->protocol->flush()) - goto err_exit; + if (!stmt->is_sql_prepare()) + { + if (send_prep_stmt(stmt, fields.elements) || + thd->protocol->send_result_set_metadata(&fields, Protocol::SEND_EOF) || + thd->protocol->flush()) + goto err_exit; - DBUG_RETURN(2); + DBUG_RETURN(2); + } + DBUG_RETURN(0); err_exit: DBUG_RETURN(1); From 07e976294089cbc0c8cf46d984565e6136ffa9e8 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 22 Jan 2016 19:29:26 +0100 Subject: [PATCH 62/83] MDEV-8615: Assertion `m_cpp_buf <= begin_ptr && begin_ptr <= m_cpp_buf + m_buf_length' failed in Lex_input_stream::body_utf8_start Nothing should be done before any keyword recognized. --- mysql-test/r/compound.result | 7 ++ mysql-test/t/compound.test | 7 ++ sql/sql_yacc.yy | 176 +++++++++++++++++++++++------------ 3 files changed, 133 insertions(+), 57 deletions(-) diff --git a/mysql-test/r/compound.result b/mysql-test/r/compound.result index 92d3226ec00..1d412e671a4 100644 --- a/mysql-test/r/compound.result +++ b/mysql-test/r/compound.result @@ -162,3 +162,10 @@ a begin not atomic select a from t1 having a > 1; end| a drop table t1| +# +# MDEV-8615: Assertion `m_cpp_buf <= begin_ptr && +# begin_ptr <= m_cpp_buf + m_buf_length' failed in +# Lex_input_stream::body_utf8_start +# +b'| +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'b'' at line 1 diff --git a/mysql-test/t/compound.test b/mysql-test/t/compound.test index 1c5d12a1665..cabdf96e00e 100644 --- a/mysql-test/t/compound.test +++ b/mysql-test/t/compound.test @@ -150,3 +150,10 @@ select a from t1 having a > 1| begin not atomic select a from t1 having a > 1; end| drop table t1| +--echo # +--echo # MDEV-8615: Assertion `m_cpp_buf <= begin_ptr && +--echo # begin_ptr <= m_cpp_buf + m_buf_length' failed in +--echo # Lex_input_stream::body_utf8_start +--echo # +--error ER_PARSE_ERROR +--query b' diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 876cd2bf55a..18a981c58c6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -248,6 +248,35 @@ static bool maybe_start_compound_statement(THD *thd) return 0; } +static bool push_sp_label(THD *thd, LEX_STRING label) +{ + sp_pcontext *ctx= thd->lex->spcont; + sp_label *lab= ctx->find_label(label); + + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), label.str); + return 1; + } + else + { + lab= thd->lex->spcont->push_label(thd, label, + thd->lex->sphead->instructions()); + lab->type= sp_label::ITERATION; + } + return 0; +} + +static bool push_sp_empty_label(THD *thd) +{ + if (maybe_start_compound_statement(thd)) + return 1; + /* Unlabeled controls get an empty label. */ + thd->lex->spcont->push_label(thd, empty_lex_str, + thd->lex->sphead->instructions()); + return 0; +} + /** Helper action for a case expression statement (the expr in 'CASE expr'). This helper is used for 'searched' cases only. @@ -997,7 +1026,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); Currently there are 160 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 160 +%expect 162 /* Comments for TOKENS. @@ -1934,6 +1963,7 @@ END_OF_INPUT %type sp_proc_stmt_iterate %type sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close %type case_stmt_specification +%type loop_body while_body repeat_body %type sp_decl_idents sp_handler_type sp_hcond_list %type sp_cond sp_hcond sqlstate signal_value opt_signal_value @@ -3768,20 +3798,6 @@ sp_proc_stmt_return: } ; -sp_unlabeled_control: - { - if (maybe_start_compound_statement(thd)) - MYSQL_YYABORT; - /* Unlabeled controls get an empty label. */ - Lex->spcont->push_label(thd, empty_lex_str, - Lex->sphead->instructions()); - } - sp_control_content - { - Lex->sphead->backpatch(Lex->spcont->pop_label()); - } - ; - sp_proc_stmt_leave: LEAVE_SYM label_ident { @@ -4200,41 +4216,6 @@ else_clause_opt: | ELSE sp_proc_stmts1 ; -sp_labeled_control: - label_ident ':' - { - LEX *lex= Lex; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($1); - - if (lab) - { - my_error(ER_SP_LABEL_REDEFINE, MYF(0), $1.str); - MYSQL_YYABORT; - } - else - { - lab= lex->spcont->push_label(thd, $1, lex->sphead->instructions()); - lab->type= sp_label::ITERATION; - } - } - sp_control_content sp_opt_label - { - LEX *lex= Lex; - sp_label *lab= lex->spcont->pop_label(); - - if ($5.str) - { - if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0) - { - my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str); - MYSQL_YYABORT; - } - } - lex->sphead->backpatch(lab); - } - ; - sp_opt_label: /* Empty */ { $$= null_lex_str; } | label_ident { $$= $1; } @@ -4327,8 +4308,7 @@ sp_block_content: } ; -sp_control_content: - LOOP_SYM +loop_body: sp_proc_stmts1 END LOOP_SYM { LEX *lex= Lex; @@ -4340,15 +4320,16 @@ sp_control_content: lex->sphead->add_instr(i)) MYSQL_YYABORT; } - | WHILE_SYM - { Lex->sphead->reset_lex(thd); } + ; + +while_body: expr DO_SYM { LEX *lex= Lex; sp_head *sp= lex->sphead; uint ip= sp->instructions(); sp_instr_jump_if_not *i= new (lex->thd->mem_root) - sp_instr_jump_if_not(ip, lex->spcont, $3, lex); + sp_instr_jump_if_not(ip, lex->spcont, $1, lex); if (i == NULL || /* Jumping forward */ sp->push_backpatch(i, lex->spcont->last_label()) || @@ -4370,7 +4351,10 @@ sp_control_content: MYSQL_YYABORT; lex->sphead->do_cont_backpatch(); } - | REPEAT_SYM sp_proc_stmts1 UNTIL_SYM + ; + +repeat_body: + sp_proc_stmts1 UNTIL_SYM { Lex->sphead->reset_lex(thd); } expr END REPEAT_SYM { @@ -4378,7 +4362,7 @@ sp_control_content: uint ip= lex->sphead->instructions(); sp_label *lab= lex->spcont->last_label(); /* Jumping back */ sp_instr_jump_if_not *i= new (lex->thd->mem_root) - sp_instr_jump_if_not(ip, lex->spcont, $5, lab->ip, lex); + sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex); if (i == NULL || lex->sphead->add_instr(i)) MYSQL_YYABORT; @@ -4389,6 +4373,84 @@ sp_control_content: } ; +pop_sp_label: + sp_opt_label + { + sp_label *lab; + Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); + if ($1.str) + { + if (my_strcasecmp(system_charset_info, $1.str, + lab->name.str) != 0) + { + my_error(ER_SP_LABEL_MISMATCH, MYF(0), $1.str); + MYSQL_YYABORT; + } + } + } + ; + +pop_sp_empty_label: + { + sp_label *lab; + Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); + DBUG_ASSERT(lab->name.length == 0); + } + ; + +sp_labeled_control: + label_ident ':' LOOP_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + } + loop_body pop_sp_label + { } + | label_ident ':' WHILE_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body pop_sp_label + { } + | label_ident ':' REPEAT_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + } + repeat_body pop_sp_label + { } + ; + +sp_unlabeled_control: + LOOP_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + } + loop_body + pop_sp_empty_label + { } + | WHILE_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body + pop_sp_empty_label + { } + | REPEAT_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + } + repeat_body + pop_sp_empty_label + { } + ; + trg_action_time: BEFORE_SYM { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; } From f3926cd18e2ba64f2643c6c4f6a981ed99868895 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 12:01:55 +0400 Subject: [PATCH 63/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. Fix for SHOW CREATE DATABASE. --- sql/sql_prepare.cc | 59 +++++++++++++++++++++++++++------------ sql/sql_show.cc | 24 ++++++++++------ sql/sql_show.h | 1 + tests/mysql_client_test.c | 7 ++++- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ee0aa2d73fd..9b455433acf 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1812,6 +1812,14 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } +static bool send_stmt_metadata(THD *thd, Prepared_statement *stmt, List *fields) +{ + return send_prep_stmt(stmt, fields->elements) || + thd->protocol->send_result_set_metadata(fields, Protocol::SEND_EOF) || + thd->protocol->flush(); +} + + /** Validate and prepare for execution SHOW CREATE TABLE statement. @@ -1824,7 +1832,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static int mysql_test_show_create_table(Prepared_statement *stmt, +static bool mysql_test_show_create_table(Prepared_statement *stmt, TABLE_LIST *tables) { DBUG_ENTER("mysql_test_show_create_table"); @@ -1833,22 +1841,31 @@ static int mysql_test_show_create_table(Prepared_statement *stmt, char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); - if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) - goto err_exit; + DBUG_RETURN(mysqld_show_create_get_fields(thd, tables, &fields, &buffer) || + send_stmt_metadata(thd, stmt, &fields)); +} + + +/** + Validate and prepare for execution SHOW CREATE DATABASE statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_create_db(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_create_db"); + THD *thd= stmt->thd; + List fields; + + mysqld_show_create_db_get_fields(thd, &fields); - if (!stmt->is_sql_prepare()) - { - if (send_prep_stmt(stmt, fields.elements) || - thd->protocol->send_result_set_metadata(&fields, Protocol::SEND_EOF) || - thd->protocol->flush()) - goto err_exit; - - DBUG_RETURN(2); - } - DBUG_RETURN(0); - -err_exit: - DBUG_RETURN(1); + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } @@ -2186,8 +2203,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_create_table(stmt); break; case SQLCOM_SHOW_CREATE: - res= mysql_test_show_create_table(stmt, tables); - if (res == 2) + if (!(res= mysql_test_show_create_table(stmt, tables))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; + case SQLCOM_SHOW_CREATE_DB: + if (!(res= mysql_test_show_create_db(stmt))) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 6cdef2ee301..d931aed475a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1272,6 +1272,19 @@ exit: DBUG_RETURN(error); } + +void mysqld_show_create_db_get_fields(THD *thd, List *field_list) +{ + MEM_ROOT *mem_root= thd->mem_root; + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Database", NAME_CHAR_LEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Create Database", 1024), + mem_root); +} + + bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname, LEX_STRING *orig_dbname, const DDL_options_st &options) @@ -1284,7 +1297,7 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname, #endif Schema_specification_st create; Protocol *protocol=thd->protocol; - MEM_ROOT *mem_root= thd->mem_root; + List field_list; DBUG_ENTER("mysql_show_create_db"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1318,13 +1331,8 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname, load_db_opt_by_name(thd, dbname->str, &create); } - List field_list; - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Database", NAME_CHAR_LEN), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Create Database", 1024), - mem_root); + + mysqld_show_create_db_get_fields(thd, &field_list); if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | diff --git a/sql/sql_show.h b/sql/sql_show.h index 06ff909733a..ecb7e9468ea 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -88,6 +88,7 @@ int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd); bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, List *field_list, String *buffer); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); +void mysqld_show_create_db_get_fields(THD *thd, List *field_list); bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name, LEX_STRING *orig_db_name, const DDL_options_st &options); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index b14b4f4dab9..3d46a720cf7 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -426,9 +426,14 @@ static void test_prepare_simple() strmov(query, "SHOW CREATE TABLE test_prepare_simple"); stmt= mysql_simple_prepare(mysql, query); check_stmt(stmt); - DIE_UNLESS(mysql_stmt_field_count(stmt) == 2); + mysql_stmt_close(stmt); + /* show create database */ + strmov(query, "SHOW CREATE DATABASE test"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 2); mysql_stmt_close(stmt); /* now fetch the results ..*/ From 552d33095a25f6e1f9af802e71713b0bec0f6acb Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 12:39:27 +0400 Subject: [PATCH 64/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. Fix for SHOW GRANTS statement. --- sql/sql_acl.cc | 17 +++++++++++++---- sql/sql_acl.h | 2 ++ sql/sql_prepare.cc | 30 ++++++++++++++++++++++++++++++ tests/mysql_client_test.c | 7 +++++++ 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c6793241f70..e5a80420470 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -7740,6 +7740,16 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data) } +void mysql_show_grants_get_fields(THD *thd, List *fields, + const char *name) +{ + Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0); + field->name= (char *) name; + field->max_length=1024; + fields->push_back(field, thd->mem_root); +} + + /* SHOW GRANTS; Send grants for a user to the client @@ -7805,15 +7815,14 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) } DBUG_ASSERT(rolename || username); - Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0); List field_list; - field->name=buff; - field->max_length=1024; if (!username) strxmov(buff,"Grants for ",rolename, NullS); else strxmov(buff,"Grants for ",username,"@",hostname, NullS); - field_list.push_back(field, thd->mem_root); + + mysql_show_grants_get_fields(thd, &field_list, buff); + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 335a558cddb..0893504b72d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -241,6 +241,8 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *field_name); +void mysql_show_grants_get_fields(THD *thd, List *fields, + const char *name); bool mysql_show_grants(THD *thd, LEX_USER *user); int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9b455433acf..ba63ac4430e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1869,6 +1869,29 @@ static bool mysql_test_show_create_db(Prepared_statement *stmt) } +/** + Validate and prepare for execution SHOW GRANTS statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_grants(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_grants"); + THD *thd= stmt->thd; + List fields; + + mysql_show_grants_get_fields(thd, &fields, "Grants for"); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2216,6 +2239,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_GRANTS: + if (!(res= mysql_test_show_grants(stmt))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 3d46a720cf7..84353f63d3b 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -436,6 +436,13 @@ static void test_prepare_simple() DIE_UNLESS(mysql_stmt_field_count(stmt) == 2); mysql_stmt_close(stmt); + /* show grants */ + strmov(query, "SHOW GRANTS"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 1); + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc); From 75a1d866dd4c00b91ca9b29593ad41543f084544 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 13:31:53 +0400 Subject: [PATCH 65/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. SHOW SLAVE STATUS fixed. --- sql/slave.cc | 419 +++++++++++++++++++------------------- sql/slave.h | 2 + sql/sql_prepare.cc | 31 +++ tests/mysql_client_test.c | 7 + 4 files changed, 249 insertions(+), 210 deletions(-) diff --git a/sql/slave.cc b/sql/slave.cc index 244b2f14643..72bed45d245 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -163,7 +163,6 @@ static int queue_event(Master_info* mi,const char* buf,ulong event_len); static int terminate_slave_thread(THD *, mysql_mutex_t *, mysql_cond_t *, volatile uint *, bool); static bool check_io_slave_killed(Master_info *mi, const char *info); -static bool send_show_master_info_header(THD *, bool, size_t); static bool send_show_master_info_data(THD *, Master_info *, bool, String *); /* Function to set the slave's max_allowed_packet based on the value @@ -2497,10 +2496,13 @@ bool show_master_info(THD *thd, Master_info *mi, bool full) { DBUG_ENTER("show_master_info"); String gtid_pos; + List field_list; if (full && rpl_global_gtid_slave_state->tostring(>id_pos, NULL, 0)) DBUG_RETURN(TRUE); - if (send_show_master_info_header(thd, full, gtid_pos.length())) + show_master_info_get_fields(thd, &field_list, full, gtid_pos.length()); + if (thd->protocol->send_result_set_metadata(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); if (send_show_master_info_data(thd, mi, full, >id_pos)) DBUG_RETURN(TRUE); @@ -2508,224 +2510,218 @@ bool show_master_info(THD *thd, Master_info *mi, bool full) DBUG_RETURN(FALSE); } -static bool send_show_master_info_header(THD *thd, bool full, - size_t gtid_pos_length) +void show_master_info_get_fields(THD *thd, List *field_list, + bool full, size_t gtid_pos_length) { - List field_list; - Protocol *protocol= thd->protocol; Master_info *mi; MEM_ROOT *mem_root= thd->mem_root; - DBUG_ENTER("show_master_info_header"); + DBUG_ENTER("show_master_info_get_fields"); if (full) { - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Connection_name", - MAX_CONNECTION_NAME), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Slave_SQL_State", 30), - thd->mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Connection_name", + MAX_CONNECTION_NAME), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Slave_SQL_State", 30), + mem_root); } - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Slave_IO_State", 30), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_Host", sizeof(mi->host)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_User", sizeof(mi->user)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Master_Port", 7, MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Connect_Retry", 10, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_Log_File", FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Read_Master_Log_Pos", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Relay_Log_File", FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Relay_Log_Pos", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Relay_Master_Log_File", - FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Slave_IO_Running", 3), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Slave_SQL_Running", 3), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Do_DB", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Ignore_DB", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Do_Table", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Ignore_Table", 23), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Wild_Do_Table", 24), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Wild_Ignore_Table", - 28), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Last_Errno", 4, MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Last_Error", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Skip_Counter", 10, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Exec_Master_Log_Pos", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Relay_Log_Space", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Until_Condition", 6), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Until_Log_File", FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Until_Log_Pos", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Allowed", 7), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_CA_File", - sizeof(mi->ssl_ca)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_CA_Path", - sizeof(mi->ssl_capath)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Cert", - sizeof(mi->ssl_cert)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Cipher", - sizeof(mi->ssl_cipher)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Key", - sizeof(mi->ssl_key)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Seconds_Behind_Master", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Verify_Server_Cert", - 3), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Last_IO_Errno", 4, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Last_IO_Error", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Last_SQL_Errno", 4, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Last_SQL_Error", 20), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Ignore_Server_Ids", - FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Master_Server_Id", sizeof(ulong), - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Crl", - sizeof(mi->ssl_crl)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Master_SSL_Crlpath", - sizeof(mi->ssl_crlpath)), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Using_Gtid", - sizeof("Current_Pos")-1), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Gtid_IO_Pos", 30), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Do_Domain_Ids", - FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Replicate_Ignore_Domain_Ids", - FN_REFLEN), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Parallel_Mode", - sizeof("conservative")-1), - thd->mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Slave_IO_State", 30), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_Host", sizeof(mi->host)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_User", sizeof(mi->user)), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Master_Port", 7, MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Connect_Retry", 10, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_Log_File", FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Read_Master_Log_Pos", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Relay_Log_File", FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Relay_Log_Pos", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Relay_Master_Log_File", + FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Slave_IO_Running", 3), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Slave_SQL_Running", 3), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Do_DB", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Ignore_DB", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Do_Table", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Ignore_Table", 23), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Wild_Do_Table", 24), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Wild_Ignore_Table", + 28), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Last_Errno", 4, MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Last_Error", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Skip_Counter", 10, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Exec_Master_Log_Pos", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Relay_Log_Space", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Until_Condition", 6), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Until_Log_File", FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Until_Log_Pos", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Allowed", 7), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_CA_File", + sizeof(mi->ssl_ca)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_CA_Path", + sizeof(mi->ssl_capath)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Cert", + sizeof(mi->ssl_cert)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Cipher", + sizeof(mi->ssl_cipher)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Key", + sizeof(mi->ssl_key)), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Seconds_Behind_Master", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Verify_Server_Cert", + 3), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Last_IO_Errno", 4, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Last_IO_Error", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Last_SQL_Errno", 4, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Last_SQL_Error", 20), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Ignore_Server_Ids", + FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Master_Server_Id", sizeof(ulong), + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Crl", + sizeof(mi->ssl_crl)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Master_SSL_Crlpath", + sizeof(mi->ssl_crlpath)), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Using_Gtid", + sizeof("Current_Pos")-1), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Gtid_IO_Pos", 30), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Do_Domain_Ids", + FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Replicate_Ignore_Domain_Ids", + FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Parallel_Mode", + sizeof("conservative")-1), + mem_root); if (full) { - field_list.push_back(new (mem_root) - Item_return_int(thd, "Retried_transactions", 10, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Max_relay_log_size", 10, - MYSQL_TYPE_LONGLONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Executed_log_entries", 10, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Slave_received_heartbeats", 10, - MYSQL_TYPE_LONG), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_float(thd, "Slave_heartbeat_period", 0.0, 3, 10), - thd->mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Gtid_Slave_Pos", - gtid_pos_length), - thd->mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Retried_transactions", 10, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Max_relay_log_size", 10, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Executed_log_entries", 10, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Slave_received_heartbeats", 10, + MYSQL_TYPE_LONG), + mem_root); + field_list->push_back(new (mem_root) + Item_float(thd, "Slave_heartbeat_period", 0.0, 3, 10), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Gtid_Slave_Pos", + gtid_pos_length), + mem_root); } - - if (protocol->send_result_set_metadata(&field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); - DBUG_RETURN(FALSE); + DBUG_VOID_RETURN; } /* Text for Slave_IO_Running */ @@ -2962,6 +2958,7 @@ bool show_all_master_info(THD* thd) uint i, elements; String gtid_pos; Master_info **tmp; + List field_list; DBUG_ENTER("show_master_info"); mysql_mutex_assert_owner(&LOCK_active_mi); @@ -2972,7 +2969,9 @@ bool show_all_master_info(THD* thd) DBUG_RETURN(TRUE); } - if (send_show_master_info_header(thd, 1, gtid_pos.length())) + show_master_info_get_fields(thd, &field_list, 1, gtid_pos.length()); + if (thd->protocol->send_result_set_metadata(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); if (!master_info_index || diff --git a/sql/slave.h b/sql/slave.h index 361c19557b1..d88d08b2b0d 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -207,6 +207,8 @@ int mysql_table_dump(THD* thd, const char* db, int fetch_master_table(THD* thd, const char* db_name, const char* table_name, Master_info* mi, MYSQL* mysql, bool overwrite); +void show_master_info_get_fields(THD *thd, List *field_list, + bool full, size_t gtid_pos_length); bool show_master_info(THD* thd, Master_info* mi, bool full); bool show_all_master_info(THD* thd); bool show_binlog_info(THD* thd); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ba63ac4430e..fee364912c0 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -104,6 +104,7 @@ When one supplies long data for a placeholder: // mysql_handle_derived #include "sql_cursor.h" #include "sql_show.h" +#include "slave.h" #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -1892,6 +1893,29 @@ static bool mysql_test_show_grants(Prepared_statement *stmt) } +/** + Validate and prepare for execution SHOW SLAVE STATUS statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_slave_status(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_slave_status"); + THD *thd= stmt->thd; + List fields; + + show_master_info_get_fields(thd, &fields, 0, 0); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2246,6 +2270,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_SLAVE_STAT: + if (!(res= mysql_test_show_slave_status(stmt))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 84353f63d3b..d871ea178ea 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -443,6 +443,13 @@ static void test_prepare_simple() DIE_UNLESS(mysql_stmt_field_count(stmt) == 1); mysql_stmt_close(stmt); + /* show slave status */ + strmov(query, "SHOW SLAVE STATUS"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 47); + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc); From efb36ac5d5a5e2b7937545e2d3fddb1b7c8b7f9a Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 13:42:53 +0400 Subject: [PATCH 66/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. SHOW MASTER STATUS fixed. --- sql/slave.h | 1 + sql/sql_prepare.cc | 30 ++++++++++++++++++++++++++++++ sql/sql_repl.cc | 34 ++++++++++++++++++++-------------- tests/mysql_client_test.c | 7 +++++++ 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/sql/slave.h b/sql/slave.h index d88d08b2b0d..ca89064d773 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -211,6 +211,7 @@ void show_master_info_get_fields(THD *thd, List *field_list, bool full, size_t gtid_pos_length); bool show_master_info(THD* thd, Master_info* mi, bool full); bool show_all_master_info(THD* thd); +void show_binlog_info_get_fields(THD *thd, List *field_list); bool show_binlog_info(THD* thd); bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report, bool (*pred)(const void *), const void *param); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index fee364912c0..7ba269ed131 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1916,6 +1916,29 @@ static bool mysql_test_show_slave_status(Prepared_statement *stmt) } +/** + Validate and prepare for execution SHOW MASTER STATUS statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_master_status(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_master_status"); + THD *thd= stmt->thd; + List fields; + + show_binlog_info_get_fields(thd, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2277,6 +2300,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_MASTER_STAT: + if (!(res= mysql_test_show_master_status(stmt))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index e524153ad15..3a73237ad4f 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -4088,6 +4088,25 @@ err: } +void show_binlog_info_get_fields(THD *thd, List *field_list) +{ + MEM_ROOT *mem_root= thd->mem_root; + field_list->push_back(new (mem_root) + Item_empty_string(thd, "File", FN_REFLEN), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "Position", 20, + MYSQL_TYPE_LONGLONG), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Binlog_Do_DB", 255), + mem_root); + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Binlog_Ignore_DB", 255), + mem_root); +} + + /** Execute a SHOW MASTER STATUS statement. @@ -4100,23 +4119,10 @@ err: bool show_binlog_info(THD* thd) { Protocol *protocol= thd->protocol; - MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("show_binlog_info"); List field_list; - field_list.push_back(new (mem_root) - Item_empty_string(thd, "File", FN_REFLEN), - mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "Position", 20, - MYSQL_TYPE_LONGLONG), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Binlog_Do_DB", 255), - mem_root); - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Binlog_Ignore_DB", 255), - mem_root); + show_binlog_info_get_fields(thd, &field_list); if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index d871ea178ea..ccc39d5f9ce 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -450,6 +450,13 @@ static void test_prepare_simple() DIE_UNLESS(mysql_stmt_field_count(stmt) == 47); mysql_stmt_close(stmt); + /* show master status */ + strmov(query, "SHOW MASTER STATUS"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 4); + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc); From 34df3140f2e5fe3a8531c5f6eb430e2192bfa737 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 13:57:25 +0400 Subject: [PATCH 67/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. SHOW BINARY LOGS fixed. --- sql/sql_prepare.cc | 31 +++++++++++++++++++++++++++++++ sql/sql_repl.cc | 23 +++++++++++++++-------- sql/sql_repl.h | 1 + 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 7ba269ed131..c67acff32f6 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -104,6 +104,7 @@ When one supplies long data for a placeholder: // mysql_handle_derived #include "sql_cursor.h" #include "sql_show.h" +#include "sql_repl.h" #include "slave.h" #include "sp_head.h" #include "sp.h" @@ -1939,6 +1940,29 @@ static bool mysql_test_show_master_status(Prepared_statement *stmt) } +/** + Validate and prepare for execution SHOW BINLOGS statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_binlogs(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_binlogs"); + THD *thd= stmt->thd; + List fields; + + show_binlogs_get_fields(thd, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2307,6 +2331,13 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_BINLOGS: + if (!(res= mysql_test_show_binlogs(stmt))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 3a73237ad4f..19f7e5ab4e6 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -4146,6 +4146,19 @@ bool show_binlog_info(THD* thd) } +void show_binlogs_get_fields(THD *thd, List *field_list) +{ + MEM_ROOT *mem_root= thd->mem_root; + field_list->push_back(new (mem_root) + Item_empty_string(thd, "Log_name", 255), + mem_root); + field_list->push_back(new (mem_root) + Item_return_int(thd, "File_size", 20, + MYSQL_TYPE_LONGLONG), + mem_root); +} + + /** Execute a SHOW BINARY LOGS statement. @@ -4165,7 +4178,6 @@ bool show_binlogs(THD* thd) uint length; int cur_dir_len; Protocol *protocol= thd->protocol; - MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("show_binlogs"); if (!mysql_bin_log.is_open()) @@ -4174,13 +4186,8 @@ bool show_binlogs(THD* thd) DBUG_RETURN(TRUE); } - field_list.push_back(new (mem_root) - Item_empty_string(thd, "Log_name", 255), - mem_root); - field_list.push_back(new (mem_root) - Item_return_int(thd, "File_size", 20, - MYSQL_TYPE_LONGLONG), - mem_root); + show_binlogs_get_fields(thd, &field_list); + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 774e43c0a87..e2000bbca73 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -52,6 +52,7 @@ bool purge_master_logs(THD* thd, const char* to_log); bool purge_master_logs_before_date(THD* thd, time_t purge_time); bool log_in_use(const char* log_name); void adjust_linfo_offsets(my_off_t purge_offset); +void show_binlogs_get_fields(THD *thd, List *field_list); bool show_binlogs(THD* thd); extern int init_master_info(Master_info* mi); void kill_zombie_dump_threads(uint32 slave_server_id); From d16d40be2c47d8be5360ae7604f0199635dc0063 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 14:58:52 +0400 Subject: [PATCH 68/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. SHOW CREATE PROCEDURE/FUNCTION fixed. --- sql/sp_head.cc | 63 +++++++++++++++++++++++++++++++++++++++ sql/sp_head.h | 3 ++ sql/sql_prepare.cc | 37 +++++++++++++++++++++++ tests/mysql_client_test.c | 14 +++++++++ 4 files changed, 117 insertions(+) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 01e649bb746..ea112bc8296 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2531,6 +2531,69 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) } +/** + Collect metadata for SHOW CREATE statement for stored routines. + + @param thd Thread context. + @param type Stored routine type + @param type Stored routine type + (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) + + @return Error status. + @retval FALSE on success + @retval TRUE on error +*/ + +void +sp_head::show_create_routine_get_fields(THD *thd, int type, List *fields) +{ + const char *col1_caption= type == TYPE_ENUM_PROCEDURE ? + "Procedure" : "Function"; + + const char *col3_caption= type == TYPE_ENUM_PROCEDURE ? + "Create Procedure" : "Create Function"; + + MEM_ROOT *mem_root= thd->mem_root; + + /* Send header. */ + + fields->push_back(new (mem_root) + Item_empty_string(thd, col1_caption, NAME_CHAR_LEN), + mem_root); + fields->push_back(new (mem_root) + Item_empty_string(thd, "sql_mode", 256), + mem_root); + + { + /* + NOTE: SQL statement field must be not less than 1024 in order not to + confuse old clients. + */ + + Item_empty_string *stmt_fld= + new (mem_root) Item_empty_string(thd, col3_caption, 1024); + stmt_fld->maybe_null= TRUE; + + fields->push_back(stmt_fld, mem_root); + } + + fields->push_back(new (mem_root) + Item_empty_string(thd, "character_set_client", + MY_CS_NAME_SIZE), + mem_root); + + fields->push_back(new (mem_root) + Item_empty_string(thd, "collation_connection", + MY_CS_NAME_SIZE), + mem_root); + + fields->push_back(new (mem_root) + Item_empty_string(thd, "Database Collation", + MY_CS_NAME_SIZE), + mem_root); +} + + /** Implement SHOW CREATE statement for stored routines. diff --git a/sql/sp_head.h b/sql/sp_head.h index dbdb957aa79..5a91423739c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -340,6 +340,9 @@ public: bool execute_procedure(THD *thd, List *args); + static void + show_create_routine_get_fields(THD *thd, int type, List *fields); + bool show_create_routine(THD *thd, int type); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c67acff32f6..8497d712b80 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1963,6 +1963,29 @@ static bool mysql_test_show_binlogs(Prepared_statement *stmt) } +/** + Validate and prepare for execution SHOW CREATE PROC/FUNC statement. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static bool mysql_test_show_create_routine(Prepared_statement *stmt, int type) +{ + DBUG_ENTER("mysql_test_show_binlogs"); + THD *thd= stmt->thd; + List fields; + + sp_head::show_create_routine_get_fields(thd, type, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + /** @brief Validate and prepare for execution CREATE VIEW statement @@ -2338,6 +2361,20 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; + case SQLCOM_SHOW_CREATE_PROC: + if (!(res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; + case SQLCOM_SHOW_CREATE_FUNC: + if (!(res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION))) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_VIEW: if (lex->create_view_mode == VIEW_ALTER) { diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index ccc39d5f9ce..dd892b45449 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -457,6 +457,20 @@ static void test_prepare_simple() DIE_UNLESS(mysql_stmt_field_count(stmt) == 4); mysql_stmt_close(stmt); + /* show create procedure */ + strmov(query, "SHOW CREATE PROCEDURE e1;"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 6); + mysql_stmt_close(stmt); + + /* show create function */ + strmov(query, "SHOW CREATE FUNCTION e1;"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + DIE_UNLESS(mysql_stmt_field_count(stmt) == 6); + mysql_stmt_close(stmt); + /* now fetch the results ..*/ rc= mysql_commit(mysql); myquery(rc); From 418518c0b1b0eaabaa184ab514bf1594eaa682c2 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 16:42:42 +0400 Subject: [PATCH 69/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. Keep the embedded-server version valid. --- sql/sql_prepare.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 8497d712b80..7757b8da159 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1871,6 +1871,7 @@ static bool mysql_test_show_create_db(Prepared_statement *stmt) } +#ifndef NO_EMBEDDED_ACCESS_CHECKS /** Validate and prepare for execution SHOW GRANTS statement. @@ -1892,8 +1893,10 @@ static bool mysql_test_show_grants(Prepared_statement *stmt) DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } +#endif /*NO_EMBEDDED_ACCESS_CHECKS*/ +#ifndef EMBEDDED_LIBRARY /** Validate and prepare for execution SHOW SLAVE STATUS statement. @@ -1962,6 +1965,8 @@ static bool mysql_test_show_binlogs(Prepared_statement *stmt) DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } +#endif /* EMBEDDED_LIBRARY */ + /** Validate and prepare for execution SHOW CREATE PROC/FUNC statement. @@ -2333,6 +2338,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; +#ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: if (!(res= mysql_test_show_grants(stmt))) { @@ -2340,6 +2346,8 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ +#ifndef EMBEDDED_LIBRARY case SQLCOM_SHOW_SLAVE_STAT: if (!(res= mysql_test_show_slave_status(stmt))) { @@ -2361,6 +2369,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) DBUG_RETURN(FALSE); } break; +#endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: if (!(res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE))) { From 11c85e1d9fab12ed6da00d562084598b633664a3 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Wed, 27 Jan 2016 23:58:01 +0400 Subject: [PATCH 70/83] MDEV-5273 Prepared statement doesn't return metadata after prepare. The SQL command 'PREPARE' was broken - should be take into account. --- sql/sql_prepare.cc | 50 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 7757b8da159..6568910da44 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1814,11 +1814,17 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } -static bool send_stmt_metadata(THD *thd, Prepared_statement *stmt, List *fields) +static int send_stmt_metadata(THD *thd, Prepared_statement *stmt, List *fields) { - return send_prep_stmt(stmt, fields->elements) || - thd->protocol->send_result_set_metadata(fields, Protocol::SEND_EOF) || - thd->protocol->flush(); + if (stmt->is_sql_prepare()) + return 0; + + if (send_prep_stmt(stmt, fields->elements) || + thd->protocol->send_result_set_metadata(fields, Protocol::SEND_EOF) || + thd->protocol->flush()) + return 1; + + return 2; } @@ -1834,7 +1840,7 @@ static bool send_stmt_metadata(THD *thd, Prepared_statement *stmt, List *f TRUE error, error message is set in THD */ -static bool mysql_test_show_create_table(Prepared_statement *stmt, +static int mysql_test_show_create_table(Prepared_statement *stmt, TABLE_LIST *tables) { DBUG_ENTER("mysql_test_show_create_table"); @@ -1843,8 +1849,10 @@ static bool mysql_test_show_create_table(Prepared_statement *stmt, char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); - DBUG_RETURN(mysqld_show_create_get_fields(thd, tables, &fields, &buffer) || - send_stmt_metadata(thd, stmt, &fields)); + if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) + DBUG_RETURN(1); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } @@ -1859,7 +1867,7 @@ static bool mysql_test_show_create_table(Prepared_statement *stmt, TRUE error, error message is set in THD */ -static bool mysql_test_show_create_db(Prepared_statement *stmt) +static int mysql_test_show_create_db(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_show_create_db"); THD *thd= stmt->thd; @@ -1883,7 +1891,7 @@ static bool mysql_test_show_create_db(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static bool mysql_test_show_grants(Prepared_statement *stmt) +static int mysql_test_show_grants(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_show_grants"); THD *thd= stmt->thd; @@ -1908,7 +1916,7 @@ static bool mysql_test_show_grants(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static bool mysql_test_show_slave_status(Prepared_statement *stmt) +static int mysql_test_show_slave_status(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_show_slave_status"); THD *thd= stmt->thd; @@ -1931,7 +1939,7 @@ static bool mysql_test_show_slave_status(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static bool mysql_test_show_master_status(Prepared_statement *stmt) +static int mysql_test_show_master_status(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_show_master_status"); THD *thd= stmt->thd; @@ -1954,7 +1962,7 @@ static bool mysql_test_show_master_status(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static bool mysql_test_show_binlogs(Prepared_statement *stmt) +static int mysql_test_show_binlogs(Prepared_statement *stmt) { DBUG_ENTER("mysql_test_show_binlogs"); THD *thd= stmt->thd; @@ -1979,7 +1987,7 @@ static bool mysql_test_show_binlogs(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static bool mysql_test_show_create_routine(Prepared_statement *stmt, int type) +static int mysql_test_show_create_routine(Prepared_statement *stmt, int type) { DBUG_ENTER("mysql_test_show_binlogs"); THD *thd= stmt->thd; @@ -2325,14 +2333,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) res= mysql_test_create_table(stmt); break; case SQLCOM_SHOW_CREATE: - if (!(res= mysql_test_show_create_table(stmt, tables))) + if ((res= mysql_test_show_create_table(stmt, tables)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_CREATE_DB: - if (!(res= mysql_test_show_create_db(stmt))) + if ((res= mysql_test_show_create_db(stmt)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); @@ -2340,7 +2348,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: - if (!(res= mysql_test_show_grants(stmt))) + if ((res= mysql_test_show_grants(stmt)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); @@ -2349,21 +2357,21 @@ static bool check_prepared_statement(Prepared_statement *stmt) #endif /* NO_EMBEDDED_ACCESS_CHECKS */ #ifndef EMBEDDED_LIBRARY case SQLCOM_SHOW_SLAVE_STAT: - if (!(res= mysql_test_show_slave_status(stmt))) + if ((res= mysql_test_show_slave_status(stmt)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_MASTER_STAT: - if (!(res= mysql_test_show_master_status(stmt))) + if ((res= mysql_test_show_master_status(stmt)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_BINLOGS: - if (!(res= mysql_test_show_binlogs(stmt))) + if ((res= mysql_test_show_binlogs(stmt)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); @@ -2371,14 +2379,14 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: - if (!(res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE))) + if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_CREATE_FUNC: - if (!(res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION))) + if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); From 5092ab28ba91646922e16ee6afc8c40ac5235a31 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 28 Jan 2016 08:57:30 +0100 Subject: [PATCH 71/83] bump the version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 9d27f0cabf4..62be0c53187 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=1 -MYSQL_VERSION_PATCH=10 +MYSQL_VERSION_PATCH=11 MYSQL_VERSION_EXTRA= From ce40ccaf24af2fe395f541cb1079256de8727ccd Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 28 Jan 2016 13:58:39 +0400 Subject: [PATCH 72/83] MDEV-9181 (NULLIF(count(table.col)), 0) gives wrong result on 10.1.x Wrapping args[0] and args[2] into an Item_cache for aggregate functions. --- mysql-test/r/null.result | 60 ++++++++++++++++ mysql-test/t/null.test | 39 +++++++++++ sql/item.h | 45 +++++++++--- sql/item_cmpfunc.cc | 144 +++++++++++++++++++++++++++++++++++++-- sql/item_cmpfunc.h | 8 ++- 5 files changed, 281 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/null.result b/mysql-test/r/null.result index b4cebac661f..af24ad428ff 100644 --- a/mysql-test/r/null.result +++ b/mysql-test/r/null.result @@ -1465,5 +1465,65 @@ Warnings: Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where ((`test`.`t1`.`a` = 2020) and ((case when 2020 = 2010 then NULL else `test`.`t1`.`a` end) = concat('2020',rand()))) DROP TABLE t1; # +# MDEV-9181 (NULLIF(count(table.col)), 0) gives wrong result on 10.1.x +# +CREATE TABLE t1 (c1 varchar(50) DEFAULT NULL); +INSERT INTO t1 (c1) VALUES ('hello'), ('hello\r\n'), ('hello'),('hello'); +SELECT NULLIF(COUNT(c1),0) FROM t1; +NULLIF(COUNT(c1),0) +4 +SELECT CASE WHEN COUNT(c1)=0 THEN NULL ELSE COUNT(c1) END FROM t1; +CASE WHEN COUNT(c1)=0 THEN NULL ELSE COUNT(c1) END +4 +SELECT NULLIF(COUNT(c1)+0,0) AS c1,NULLIF(CAST(COUNT(c1) AS SIGNED),0) AS c2,NULLIF(CONCAT(COUNT(c1)),0) AS c3 FROM t1; +c1 c2 c3 +4 4 4 +SELECT NULLIF(COUNT(DISTINCT c1),0) FROM t1; +NULLIF(COUNT(DISTINCT c1),0) +2 +SELECT CASE WHEN COUNT(DISTINCT c1)=0 THEN NULL ELSE COUNT(DISTINCT c1) END FROM t1; +CASE WHEN COUNT(DISTINCT c1)=0 THEN NULL ELSE COUNT(DISTINCT c1) END +2 +DROP TABLE t1; +CREATE TABLE t1 ( +id INT NOT NULL, +c1 INT DEFAULT NULL +); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3),(2,4); +SELECT NULLIF(COUNT(c1),0) AS c1,NULLIF(COUNT(c1)+0,0) AS c1_wrapped,CASE WHEN COUNT(c1) IS NULL THEN 0 ELSE COUNT(c1) END AS c1_case FROM t1 GROUP BY id; +c1 c1_wrapped c1_case +2 2 2 +2 2 2 +DROP TABLE t1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +SET @a=0; +SELECT NULLIF(LAST_VALUE(@a:=@a+1,a),0) FROM t1; +NULLIF(LAST_VALUE(@a:=@a+1,a),0) +1 +2 +3 +SELECT @a; +@a +6 +SET @a=0; +SELECT NULLIF(AVG(a),0), NULLIF(AVG(LAST_VALUE(@a:=@a+1,a)),0) FROM t1; +NULLIF(AVG(a),0) NULLIF(AVG(LAST_VALUE(@a:=@a+1,a)),0) +2.0000 2.0000 +SELECT @a; +@a +3 +EXPLAIN EXTENDED SELECT NULLIF(a,0) FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 select nullif(`test`.`t1`.`a`,0) AS `NULLIF(a,0)` from `test`.`t1` +EXPLAIN EXTENDED SELECT NULLIF(AVG(a),0) FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 select nullif((avg(`test`.`t1`.`a`)),0) AS `NULLIF(AVG(a),0)` from `test`.`t1` +DROP TABLE t1; +# # End of 10.1 tests # diff --git a/mysql-test/t/null.test b/mysql-test/t/null.test index 5347a961c59..b60f11ab534 100644 --- a/mysql-test/t/null.test +++ b/mysql-test/t/null.test @@ -909,6 +909,45 @@ EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=2020 AND NULLIF(a,2010)=CONCAT('2020',RAND()); DROP TABLE t1; +--echo # +--echo # MDEV-9181 (NULLIF(count(table.col)), 0) gives wrong result on 10.1.x +--echo # +CREATE TABLE t1 (c1 varchar(50) DEFAULT NULL); +INSERT INTO t1 (c1) VALUES ('hello'), ('hello\r\n'), ('hello'),('hello'); +SELECT NULLIF(COUNT(c1),0) FROM t1; +SELECT CASE WHEN COUNT(c1)=0 THEN NULL ELSE COUNT(c1) END FROM t1; +SELECT NULLIF(COUNT(c1)+0,0) AS c1,NULLIF(CAST(COUNT(c1) AS SIGNED),0) AS c2,NULLIF(CONCAT(COUNT(c1)),0) AS c3 FROM t1; +SELECT NULLIF(COUNT(DISTINCT c1),0) FROM t1; +SELECT CASE WHEN COUNT(DISTINCT c1)=0 THEN NULL ELSE COUNT(DISTINCT c1) END FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ( + id INT NOT NULL, + c1 INT DEFAULT NULL +); +INSERT INTO t1 VALUES (1,1),(1,2),(2,3),(2,4); +SELECT NULLIF(COUNT(c1),0) AS c1,NULLIF(COUNT(c1)+0,0) AS c1_wrapped,CASE WHEN COUNT(c1) IS NULL THEN 0 ELSE COUNT(c1) END AS c1_case FROM t1 GROUP BY id; +DROP TABLE t1; + +# Testing with side effects + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2),(3); +SET @a=0; +SELECT NULLIF(LAST_VALUE(@a:=@a+1,a),0) FROM t1; +SELECT @a; +SET @a=0; +SELECT NULLIF(AVG(a),0), NULLIF(AVG(LAST_VALUE(@a:=@a+1,a)),0) FROM t1; +SELECT @a; + +# There should not be cache in here: + +EXPLAIN EXTENDED SELECT NULLIF(a,0) FROM t1; + +# But there should be a cache in here: +EXPLAIN EXTENDED SELECT NULLIF(AVG(a),0) FROM t1; + +DROP TABLE t1; --echo # --echo # End of 10.1 tests diff --git a/sql/item.h b/sql/item.h index d5b6463d91a..9c5762879f0 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3629,6 +3629,11 @@ public: used_tables_cache|= item->used_tables(); const_item_cache&= item->const_item(); } + void used_tables_and_const_cache_update_and_join(Item *item) + { + item->update_used_tables(); + used_tables_and_const_cache_join(item); + } /* Call update_used_tables() for all "argc" items in the array "argv" and join with the current cache. @@ -3638,10 +3643,7 @@ public: void used_tables_and_const_cache_update_and_join(uint argc, Item **argv) { for (uint i=0 ; i < argc ; i++) - { - argv[i]->update_used_tables(); - used_tables_and_const_cache_join(argv[i]); - } + used_tables_and_const_cache_update_and_join(argv[i]); } /* Call update_used_tables() for all items in the list @@ -3654,10 +3656,7 @@ public: List_iterator_fast li(list); Item *item; while ((item=li++)) - { - item->update_used_tables(); - used_tables_and_const_cache_join(item); - } + used_tables_and_const_cache_update_and_join(item); } }; @@ -5073,6 +5072,12 @@ public: return (this->*processor)(arg); } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); + void split_sum_func2_example(THD *thd, Item **ref_pointer_array, + List &fields, uint flags) + { + example->split_sum_func2(thd, ref_pointer_array, fields, &example, flags); + } + Item *get_example() const { return example; } }; @@ -5177,6 +5182,30 @@ public: bool cache_value(); }; + +class Item_cache_str_for_nullif: public Item_cache_str +{ +public: + Item_cache_str_for_nullif(THD *thd, const Item *item) + :Item_cache_str(thd, item) + { } + Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs) + { + /** + Item_cache_str::safe_charset_converter() returns a new Item_cache + with Item_func_conv_charset installed on "example". The original + Item_cache is not referenced (neither directly nor recursively) + from the result of Item_cache_str::safe_charset_converter(). + + For NULLIF() purposes we need a different behavior: + we need a new instance of Item_func_conv_charset, + with the original Item_cache referenced in args[0]. See MDEV-9181. + */ + return Item::safe_charset_converter(thd, tocs); + } +}; + + class Item_cache_row: public Item_cache { Item_cache **values; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 87be0f99e44..135c4437016 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2531,12 +2531,137 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate) } +void Item_func_nullif::split_sum_func(THD *thd, Item **ref_pointer_array, + List &fields, uint flags) +{ + if (m_cache) + { + flags|= SPLIT_SUM_SKIP_REGISTERED; // See Item_func::split_sum_func + m_cache->split_sum_func2_example(thd, ref_pointer_array, fields, flags); + args[1]->split_sum_func2(thd, ref_pointer_array, fields, &args[1], flags); + } + else + { + Item_func::split_sum_func(thd, ref_pointer_array, fields, flags); + } +} + + +void Item_func_nullif::update_used_tables() +{ + if (m_cache) + { + used_tables_and_const_cache_init(); + used_tables_and_const_cache_update_and_join(m_cache->get_example()); + used_tables_and_const_cache_update_and_join(arg_count, args); + } + else + { + Item_func::update_used_tables(); + } +} + + + void Item_func_nullif::fix_length_and_dec() { if (!args[2]) // Only false if EOM return; + DBUG_ASSERT(args[0] == args[2]); + THD *thd= current_thd; + if (args[0]->type() == SUM_FUNC_ITEM) + { + /* + NULLIF(l_expr, r_expr) + + is calculated in the way to return a result equal to: + + CASE WHEN l_expr = r_expr THEN NULL ELSE r_expr END. + + There's nothing special with r_expr, because it's referenced + only by args[1] and nothing else. + + l_expr needs a special treatment, as it's referenced by both + args[0] and args[2] initially. + + args[0] and args[2] can be replaced: + + - to Item_func_conv_charset by character set aggregation routines + - to a constant Item by equal field propagation routines + (in case of Item_field) + + For aggregate functions we have to wrap the original args[0]/args[2] + into Item_cache (see MDEV-9181). In this case the Item_cache + instance becomes the subject to character set conversion instead of + the original args[0]/args[2], while the original args[0]/args[2] get + hidden inside the cache. + + Some examples of what NULLIF can end up with after argument + substitution (we don't mention args[1] here for simplicity): + + 1. l_expr is not an aggragate function: + + a. No conversion happened. + args[0] and args[2] were not replaced to something else + (i.e. neither by character set conversion, nor by propagation): + + args[0] \ + l_expr + args[2] / + + b. Conversion of args[0] happened. + args[0] was replaced, e.g. to Item_func_conv_charset: + + args[0] > Item_func_conv_charset \ + l_expr + args[2] >------------------------/ + + c. Conversion of args[2] happened: + + args[0] >------------------------\ + l_expr + args[2] > Item_func_conv_charset / + + d. Conversion of both args[0] and args[2] happened + (e.g. by equal field propagation). + + args[0] > Item_int + args[2] > Item_int + + 2. In case if l_expr is an aggregate function: + + a. No conversion happened: + + args[0] \ + Item_cache > l_expr + args[2] / + + b. Conversion of args[0] happened: + + args[0] > Item_func_conv_charset \ + Item_cache > l_expr + args[2] >------------------------/ + + c. Conversion of args[2] happened: + + args[0] >------------------------\ + Item_cache > l_expr + args[2] > Item_func_conv_charset / + + d. Conversion of both args[0] and args[2] happened. + (e.g. by equal expression propagation) + TODO: check if it's possible (and add an example query if so). + */ + m_cache= args[0]->cmp_type() == STRING_RESULT ? + new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) : + Item_cache::get_cache(thd, args[0]); + m_cache->setup(current_thd, args[0]); + m_cache->store(args[0]); + m_cache->set_used_tables(args[0]->used_tables()); + args[0]= args[2]= m_cache; + } set_handler_by_field_type(args[2]->field_type()); collation.set(args[2]->collation); decimals= args[2]->decimals; @@ -2611,6 +2736,13 @@ void Item_func_nullif::print(String *str, enum_query_type query_type) } +int Item_func_nullif::compare() +{ + if (m_cache) + m_cache->cache_value(); + return cmp.compare(); +} + /** @note Note that we have to evaluate the first argument twice as the compare @@ -2626,7 +2758,7 @@ Item_func_nullif::real_op() { DBUG_ASSERT(fixed == 1); double value; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0.0; @@ -2641,7 +2773,7 @@ Item_func_nullif::int_op() { DBUG_ASSERT(fixed == 1); longlong value; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0; @@ -2656,7 +2788,7 @@ Item_func_nullif::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0; @@ -2672,7 +2804,7 @@ Item_func_nullif::decimal_op(my_decimal * decimal_value) { DBUG_ASSERT(fixed == 1); my_decimal *res; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0; @@ -2687,7 +2819,7 @@ bool Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(fixed == 1); - if (!cmp.compare()) + if (!compare()) return (null_value= true); return (null_value= args[2]->get_date(ltime, fuzzydate)); } @@ -2696,7 +2828,7 @@ Item_func_nullif::date_op(MYSQL_TIME *ltime, uint fuzzydate) bool Item_func_nullif::is_null() { - return (null_value= (!cmp.compare() ? 1 : args[2]->null_value)); + return (null_value= (!compare() ? 1 : args[2]->null_value)); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index b7c51cd5178..16f8a247999 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -995,10 +995,13 @@ class Item_func_nullif :public Item_func_hybrid_field_type - Item_field::propagate_equal_fields(ANY_SUBST) for the left "a" - Item_field::propagate_equal_fields(IDENTITY_SUBST) for the right "a" */ + Item_cache *m_cache; + int compare(); public: // Put "a" to args[0] for comparison and to args[2] for the returned value. Item_func_nullif(THD *thd, Item *a, Item *b): - Item_func_hybrid_field_type(thd, a, b, a) + Item_func_hybrid_field_type(thd, a, b, a), + m_cache(NULL) {} bool date_op(MYSQL_TIME *ltime, uint fuzzydate); double real_op(); @@ -1009,6 +1012,9 @@ public: uint decimal_precision() const { return args[2]->decimal_precision(); } const char *func_name() const { return "nullif"; } void print(String *str, enum_query_type query_type); + void split_sum_func(THD *thd, Item **ref_pointer_array, List &fields, + uint flags); + void update_used_tables(); table_map not_null_tables() const { return 0; } bool is_null(); Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) From 7b14ba63f22e6c98c1982dae8847035ad1b8e155 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 28 Jan 2016 14:06:05 +0200 Subject: [PATCH 73/83] MDEV-8724 Assertion `rc == 0' failed in ma_decrypt on reading an Aria table Don't assert if decrypt or encrypt fails if my_assert_on_error is not set. Added failed file name if encryption/decryption fails. --- .../suite/maria/encrypt-wrong-key.result | 16 +++++ mysql-test/suite/maria/encrypt-wrong-key.test | 52 +++++++++++++++ storage/maria/ma_crypt.c | 66 +++++++++++-------- 3 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 mysql-test/suite/maria/encrypt-wrong-key.result create mode 100644 mysql-test/suite/maria/encrypt-wrong-key.test diff --git a/mysql-test/suite/maria/encrypt-wrong-key.result b/mysql-test/suite/maria/encrypt-wrong-key.result new file mode 100644 index 00000000000..bc22481296d --- /dev/null +++ b/mysql-test/suite/maria/encrypt-wrong-key.result @@ -0,0 +1,16 @@ +call mtr.add_suppression("file_key_management"); +call mtr.add_suppression("System key id 1 is missing"); +call mtr.add_suppression("Unknown key id 1"); +call mtr.add_suppression("Failed to decrypt"); +CREATE TABLE t1 (i INT, KEY(i)) ENGINE=Aria; +INSERT INTO t1 VALUES (1); +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair info Wrong CRC on datapage at 1 +test.t1 repair warning Number of rows changed from 1 to 0 +test.t1 repair status OK +INSERT INTO t1 VALUES (2); +select * from t1; +ERROR HY000: failed to decrypt './test/t1' rc: -1 dstlen: 0 size: 8172 + +drop table t1; diff --git a/mysql-test/suite/maria/encrypt-wrong-key.test b/mysql-test/suite/maria/encrypt-wrong-key.test new file mode 100644 index 00000000000..b744c5fac17 --- /dev/null +++ b/mysql-test/suite/maria/encrypt-wrong-key.test @@ -0,0 +1,52 @@ +# +# Test what happens if one removes a decryption key for Aria +# + +call mtr.add_suppression("file_key_management"); +call mtr.add_suppression("System key id 1 is missing"); +call mtr.add_suppression("Unknown key id 1"); +call mtr.add_suppression("Failed to decrypt"); + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc + +--write_file $MYSQLTEST_VARDIR/keys1.txt +1;770A8A65DA156D24EE2A093277530142 +EOF + +--exec echo "restart:--aria-encrypt-tables=1 --plugin-load-add=file_key_management.so --file-key-management --file-key-management-filename=$MYSQLTEST_VARDIR/keys1.txt" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +CREATE TABLE t1 (i INT, KEY(i)) ENGINE=Aria; +INSERT INTO t1 VALUES (1); + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc + +--write_file $MYSQLTEST_VARDIR/keys2.txt +2;770A8A65DA156D24EE2A093277530143 +EOF + +--exec echo "restart:--aria-encrypt-tables=1 --plugin-load-add=file_key_management.so --file-key-management --file-key-management-filename=$MYSQLTEST_VARDIR/keys2.txt" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +repair table t1; + +INSERT INTO t1 VALUES (2); + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc + +--exec echo "restart:--aria-encrypt-tables=1 --plugin-load-add=file_key_management.so --file-key-management --file-key-management-filename=$MYSQLTEST_VARDIR/keys1.txt" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +--replace_result \\ / +--error 192 +select * from t1; +drop table t1; diff --git a/storage/maria/ma_crypt.c b/storage/maria/ma_crypt.c index 55fa8430d22..da82a1a37c8 100644 --- a/storage/maria/ma_crypt.c +++ b/storage/maria/ma_crypt.c @@ -182,10 +182,10 @@ ma_crypt_read(MARIA_SHARE* share, uchar *buff) return buff + 2 + iv_length; } -static int ma_encrypt(MARIA_CRYPT_DATA *, const uchar *, uchar *, uint, - uint, LSN, uint *); -static int ma_decrypt(MARIA_CRYPT_DATA *, const uchar *, uchar *, uint, - uint, LSN, uint); +static int ma_encrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *, + uchar *, uint, uint, LSN, uint *); +static int ma_decrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *, + uchar *, uint, uint, LSN, uint); static my_bool ma_crypt_pre_read_hook(PAGECACHE_IO_HOOK_ARGS *args) { @@ -227,7 +227,7 @@ static my_bool ma_crypt_data_post_read_hook(int res, /* 1 - copy head */ memcpy(dst, src, head); /* 2 - decrypt page */ - res= ma_decrypt(share->crypt_data, + res= ma_decrypt(share, share->crypt_data, src + head, dst + head, size - (head + tail), pageno, lsn, key_version); /* 3 - copy tail */ @@ -294,9 +294,9 @@ static my_bool ma_crypt_data_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args) /* 1 - copy head */ memcpy(dst, src, head); /* 2 - encrypt page */ - if (ma_encrypt(share->crypt_data, + if (ma_encrypt(share, share->crypt_data, src + head, dst + head, size - (head + tail), pageno, lsn, - &key_version)) + &key_version)) return 1; /* 3 - copy tail */ memcpy(dst + size - tail, src + size - tail, tail); @@ -361,7 +361,7 @@ static my_bool ma_crypt_index_post_read_hook(int res, /* 1 - copy head */ memcpy(dst, src, head); /* 2 - decrypt page */ - res= ma_decrypt(share->crypt_data, + res= ma_decrypt(share, share->crypt_data, src + head, dst + head, size, pageno, lsn, key_version); /* 3 - copy tail */ memcpy(dst + block_size - tail, src + block_size - tail, tail); @@ -414,7 +414,7 @@ static my_bool ma_crypt_index_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args) /* 1 - copy head */ memcpy(dst, src, head); /* 2 - encrypt page */ - if (ma_encrypt(share->crypt_data, + if (ma_encrypt(share, share->crypt_data, src + head, dst + head, size, pageno, lsn, &key_version)) return 1; /* 3 - copy tail */ @@ -444,19 +444,26 @@ void ma_crypt_set_index_pagecache_callbacks(PAGECACHE_FILE *file, file->post_write_hook= ma_crypt_post_write_hook; } -static int ma_encrypt(MARIA_CRYPT_DATA *crypt_data, - const uchar *src, uchar *dst, uint size, - uint pageno, LSN lsn, - uint *key_version) +static int ma_encrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data, + const uchar *src, uchar *dst, uint size, + uint pageno, LSN lsn, + uint *key_version) { int rc; - uint32 dstlen; + uint32 dstlen= 0; /* Must be set because of error message */ *key_version = encryption_key_get_latest_version(crypt_data->scheme.key_id); if (*key_version == ENCRYPTION_KEY_VERSION_INVALID) { - my_printf_error(HA_ERR_GENERIC, "Unknown key id %u. Can't continue!", - MYF(ME_FATALERROR|ME_NOREFRESH), crypt_data->scheme.key_id); + /* + We use this error for both encryption and decryption, as in normal + cases it should be impossible to get an error here. + */ + my_errno= HA_ERR_DECRYPTION_FAILED; + my_printf_error(HA_ERR_DECRYPTION_FAILED, + "Unknown key id %u. Can't continue!", + MYF(ME_FATALERROR|ME_NOREFRESH), + crypt_data->scheme.key_id); return 1; } @@ -464,40 +471,43 @@ static int ma_encrypt(MARIA_CRYPT_DATA *crypt_data, &crypt_data->scheme, *key_version, crypt_data->space, pageno, lsn); - DBUG_ASSERT(rc == MY_AES_OK); - DBUG_ASSERT(dstlen == size); + /* The following can only fail if the encryption key is wrong */ + DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK); + DBUG_ASSERT(!my_assert_on_error || dstlen == size); if (! (rc == MY_AES_OK && dstlen == size)) { - my_printf_error(HA_ERR_GENERIC, - "failed to encrypt! rc: %d, dstlen: %u size: %u\n", + my_errno= HA_ERR_DECRYPTION_FAILED; + my_printf_error(HA_ERR_DECRYPTION_FAILED, + "failed to encrypt '%s' rc: %d dstlen: %u size: %u\n", MYF(ME_FATALERROR|ME_NOREFRESH), - rc, dstlen, size); + share->open_file_name.str, rc, dstlen, size); return 1; } return 0; } -static int ma_decrypt(MARIA_CRYPT_DATA *crypt_data, +static int ma_decrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data, const uchar *src, uchar *dst, uint size, uint pageno, LSN lsn, uint key_version) { int rc; - uint32 dstlen; + uint32 dstlen= 0; /* Must be set because of error message */ rc= encryption_scheme_decrypt(src, size, dst, &dstlen, &crypt_data->scheme, key_version, crypt_data->space, pageno, lsn); - DBUG_ASSERT(rc == MY_AES_OK); - DBUG_ASSERT(dstlen == size); + DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK); + DBUG_ASSERT(!my_assert_on_error || dstlen == size); if (! (rc == MY_AES_OK && dstlen == size)) { - my_printf_error(HA_ERR_GENERIC, - "failed to encrypt! rc: %d, dstlen: %u size: %u\n", + my_errno= HA_ERR_DECRYPTION_FAILED; + my_printf_error(HA_ERR_DECRYPTION_FAILED, + "failed to decrypt '%s' rc: %d dstlen: %u size: %u\n", MYF(ME_FATALERROR|ME_NOREFRESH), - rc, dstlen, size); + share->open_file_name.str, rc, dstlen, size); return 1; } return 0; From 3d794d0ee8e0a5a7dfbd3b7de056c0a3ccb9aa81 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 6 Jan 2016 09:15:19 +0100 Subject: [PATCH 74/83] MDEV9494 Fix build for Heimdal Kerberos --- plugin/auth_gssapi/gssapi_server.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/auth_gssapi/gssapi_server.cc b/plugin/auth_gssapi/gssapi_server.cc index d325b2a6a76..ed042a026b1 100644 --- a/plugin/auth_gssapi/gssapi_server.cc +++ b/plugin/auth_gssapi/gssapi_server.cc @@ -58,7 +58,7 @@ static char* get_default_principal_name() } /* Check for entry in keytab */ - if (krb5_kt_read_service_key(context, NULL, principal, 0, 0, &key)) + if (krb5_kt_read_service_key(context, NULL, principal, 0, (krb5_enctype)0, &key)) { sql_print_warning("GSSAPI plugin : default principal '%s' not found in keytab", unparsed_name); goto cleanup; From 0bee1504a3741d47dcaaba74b0c846dfdfc8ad59 Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Mon, 1 Feb 2016 01:01:29 +0200 Subject: [PATCH 75/83] MDEV-9502 maria.encrypt-wrong-key fails with embedded server The test restarts the server, it should not be executed for embedded --- mysql-test/suite/maria/encrypt-wrong-key.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/suite/maria/encrypt-wrong-key.test b/mysql-test/suite/maria/encrypt-wrong-key.test index b744c5fac17..6f59952c1fa 100644 --- a/mysql-test/suite/maria/encrypt-wrong-key.test +++ b/mysql-test/suite/maria/encrypt-wrong-key.test @@ -1,3 +1,5 @@ +--source include/not_embedded.inc + # # Test what happens if one removes a decryption key for Aria # From dc50a3dd1920d11f630bc1f7d0bf8e3524007e4b Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Mon, 1 Feb 2016 01:02:23 +0200 Subject: [PATCH 76/83] Raise the version number --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 62be0c53187..4cca60abce7 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=1 -MYSQL_VERSION_PATCH=11 +MYSQL_VERSION_PATCH=12 MYSQL_VERSION_EXTRA= From a96fbc3c352dad89c6007672eedf437e0516abe0 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 1 Feb 2016 12:43:19 +0400 Subject: [PATCH 77/83] MDEV-9503 Server crashes in fix_fields, main.null fails with ps-protocol DBUG_ASSERT() added in the patch for MDEV-9181 did not take into account special circumstances for the prepared statement EXECUTE. Fixig the assert. Also, extending and fixing comments made during MDEV-9181. --- sql/item_cmpfunc.cc | 96 +++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 135c4437016..d7e5eb597cc 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2569,8 +2569,21 @@ Item_func_nullif::fix_length_and_dec() if (!args[2]) // Only false if EOM return; - DBUG_ASSERT(args[0] == args[2]); THD *thd= current_thd; + /* + At prepared statement EXECUTE time, args[0] can already + point to a different Item, created during PREPARE time fix_length_and_dec(). + For example, if character set conversion was needed, arguments can look + like this: + + args[0]= > Item_func_conv_charset \ + l_expr + args[2]= >------------------------/ + + Otherwise (during PREPARE or convensional execution), + args[0] and args[2] should still point to the same original l_expr. + */ + DBUG_ASSERT(args[0] == args[2] || thd->stmt_arena->is_stmt_execute()); if (args[0]->type() == SUM_FUNC_ITEM) { /* @@ -2586,12 +2599,25 @@ Item_func_nullif::fix_length_and_dec() l_expr needs a special treatment, as it's referenced by both args[0] and args[2] initially. - args[0] and args[2] can be replaced: + args[2] is used to return the value. Afrer all transformations + (e.g. in fix_length_and_dec(), equal field propagation, etc) + args[2] points to a an Item which preserves the exact data type and + attributes (e.g. collation) of the original l_expr. + It can point: + - to the original l_expr + - to an Item_cache pointing to l_expr + - to a constant of the same data type with l_expr. + + args[0] is used for comparison. It can be replaced: - to Item_func_conv_charset by character set aggregation routines - to a constant Item by equal field propagation routines (in case of Item_field) + The data type and/or the attributes of args[0] can differ from + the data type and the attributes of the original l_expr, to make + it comparable to args[1] (which points to r_expr or its replacement). + For aggregate functions we have to wrap the original args[0]/args[2] into Item_cache (see MDEV-9181). In this case the Item_cache instance becomes the subject to character set conversion instead of @@ -2599,7 +2625,7 @@ Item_func_nullif::fix_length_and_dec() hidden inside the cache. Some examples of what NULLIF can end up with after argument - substitution (we don't mention args[1] here for simplicity): + substitution (we don't mention args[1] in some cases for simplicity): 1. l_expr is not an aggragate function: @@ -2607,28 +2633,56 @@ Item_func_nullif::fix_length_and_dec() args[0] and args[2] were not replaced to something else (i.e. neither by character set conversion, nor by propagation): + args[1] > r_expr args[0] \ - l_expr + l_expr args[2] / - b. Conversion of args[0] happened. - args[0] was replaced, e.g. to Item_func_conv_charset: + b. Conversion of args[0] happened: - args[0] > Item_func_conv_charset \ - l_expr - args[2] >------------------------/ + CREATE OR REPLACE TABLE t1 ( + a CHAR(10) CHARACTER SET latin1, + b CHAR(10) CHARACTER SET utf8); + SELECT * FROM t1 WHERE NULLIF(a,b); - c. Conversion of args[2] happened: + args[1] > r_expr (Item_field for t1.b) + args[0] > Item_func_conv_charset\ + l_expr (Item_field for t1.a) + args[2] > ----------------------/ - args[0] >------------------------\ - l_expr - args[2] > Item_func_conv_charset / + c. Conversion of args[1] happened: - d. Conversion of both args[0] and args[2] happened - (e.g. by equal field propagation). + CREATE OR REPLACE TABLE t1 ( + a CHAR(10) CHARACTER SET utf8, + b CHAR(10) CHARACTER SET latin1); + SELECT * FROM t1 WHERE NULLIF(a,b); - args[0] > Item_int - args[2] > Item_int + args[1] > Item_func_conv_charset -> r_expr (Item_field for t1.b) + args[0] \ + l_expr (Item_field for t1.a) + args[2] / + + d. Conversion of only args[0] happened (by equal field proparation): + + CREATE OR REPLACE TABLE t1 ( + a CHAR(10), + b CHAR(10)); + SELECT * FROM t1 WHERE NULLIF(a,b) AND a='a'; + + args[1] > r_expr (Item_field for t1.b) + args[0] > Item_string('a') (constant replacement for t1.a) + args[2] > l_expr (Item_field for t1.a) + + e. Conversion of both args[0] and args[2] happened + (by equal field propagation): + + CREATE OR REPLACE TABLE t1 (a INT,b INT); + SELECT * FROM t1 WHERE NULLIF(a,b) AND a=5; + + args[1] > r_expr (Item_field for "b") + args[0] \ + Item_int (5) (constant replacement for "a") + args[2] / 2. In case if l_expr is an aggregate function: @@ -2644,13 +2698,7 @@ Item_func_nullif::fix_length_and_dec() Item_cache > l_expr args[2] >------------------------/ - c. Conversion of args[2] happened: - - args[0] >------------------------\ - Item_cache > l_expr - args[2] > Item_func_conv_charset / - - d. Conversion of both args[0] and args[2] happened. + c. Conversion of both args[0] and args[2] happened. (e.g. by equal expression propagation) TODO: check if it's possible (and add an example query if so). */ From 8cf1f50967ff58abb7d9688ae64ac662e66389c0 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 1 Feb 2016 16:10:49 +0100 Subject: [PATCH 78/83] MDEV-9112: Non-blocking client API missing on non-x86 platforms The check for UCONTEXT in cmake was somehow become broken, disabling the fallback to ucontext. This caused the non-blocking client API to not be available for non-x86 platforms, on which no hand-crafted assembler implementation of my_context is available. --- config.h.cmake | 2 +- configure.cmake | 3 +++ include/my_context.h | 2 +- sql-common/client.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config.h.cmake b/config.h.cmake index 02952f6f395..6236173c2d6 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -95,6 +95,7 @@ #cmakedefine HAVE_SYS_TYPES_H 1 #cmakedefine HAVE_SYS_UN_H 1 #cmakedefine HAVE_SYS_VADVISE_H 1 +#cmakedefine HAVE_UCONTEXT_H 1 #cmakedefine HAVE_TERM_H 1 #cmakedefine HAVE_TERMBITS_H 1 #cmakedefine HAVE_TERMIOS_H 1 @@ -291,7 +292,6 @@ #cmakedefine HAVE_THR_YIELD 1 #cmakedefine HAVE_TIME 1 #cmakedefine HAVE_TIMES 1 -#cmakedefine HAVE_UCONTEXT 1 #cmakedefine HAVE_VALLOC 1 #cmakedefine HAVE_VIDATTR 1 #define HAVE_VIO_READ_BUFF 1 diff --git a/configure.cmake b/configure.cmake index 1e64e058670..6d82b1d88de 100644 --- a/configure.cmake +++ b/configure.cmake @@ -1098,3 +1098,6 @@ CHECK_STRUCT_HAS_MEMBER("struct dirent" d_ino "dirent.h" STRUCT_DIRENT_HAS_D_IN CHECK_STRUCT_HAS_MEMBER("struct dirent" d_namlen "dirent.h" STRUCT_DIRENT_HAS_D_NAMLEN) SET(SPRINTF_RETURNS_INT 1) CHECK_INCLUDE_FILE(ucontext.h HAVE_UCONTEXT_H) +IF(NOT HAVE_UCONTEXT_H) + CHECK_INCLUDE_FILE(sys/ucontext.h HAVE_UCONTEXT_H) +ENDIF() diff --git a/include/my_context.h b/include/my_context.h index b5daca25bba..dd44103d3b2 100644 --- a/include/my_context.h +++ b/include/my_context.h @@ -31,7 +31,7 @@ #define MY_CONTEXT_USE_X86_64_GCC_ASM #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__) #define MY_CONTEXT_USE_I386_GCC_ASM -#elif defined(HAVE_UCONTEXT) +#elif defined(HAVE_UCONTEXT_H) #define MY_CONTEXT_USE_UCONTEXT #else #define MY_CONTEXT_DISABLE diff --git a/sql-common/client.c b/sql-common/client.c index 1bb4a250c69..f8bd483e8c2 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -4293,6 +4293,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE; if (my_context_init(&ctxt->async_context, stacksize)) { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); my_free(ctxt); DBUG_RETURN(1); } From e6dee57f1ae78326af9485170d232436fb49404f Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Mon, 1 Feb 2016 19:06:54 +0300 Subject: [PATCH 79/83] MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query - Legacy code would set JOIN_TAB::limit only for EXPLAIN queries (this variable is only used when producing EXPLAIN output) - ANALYZE/SHOW EXPLAIN need to produce EXPLAIN output for non-EXPLAIN queries, too, so we should always set JOIN_TAB::limit. --- mysql-test/r/analyze_stmt_orderby.result | 26 +++++++++++++++++++++++- mysql-test/t/analyze_stmt_orderby.test | 25 ++++++++++++++++++++++- sql/sql_select.cc | 6 ++++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/analyze_stmt_orderby.result b/mysql-test/r/analyze_stmt_orderby.result index be1f01a2a52..643c0318f95 100644 --- a/mysql-test/r/analyze_stmt_orderby.result +++ b/mysql-test/r/analyze_stmt_orderby.result @@ -583,4 +583,28 @@ ANALYZE } } drop table t2; -drop table t0,t1; +drop table t1; +# +# MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query +# +create table t1 ( +a int, +filler1 char(128), +filler2 char(128), +key(a) +); +insert into t1 +select A.a+10*B.a+100*C.a, repeat('abc-',32), repeat('abc-',32) +from t0 A, t0 B, t0 C; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Table is already up to date +explain select a from t1 order by a limit 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 10 Using index +# 'rows' in ANALYZE output must match 'rows' in EXPLAIN: +analyze select a from t1 order by a limit 10; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t1 index NULL a 5 NULL 10 10.00 100.00 100.00 Using index +drop table t1; +drop table t0; diff --git a/mysql-test/t/analyze_stmt_orderby.test b/mysql-test/t/analyze_stmt_orderby.test index a40f34805d1..dc63fcaa285 100644 --- a/mysql-test/t/analyze_stmt_orderby.test +++ b/mysql-test/t/analyze_stmt_orderby.test @@ -174,4 +174,27 @@ select col1 f1, col2 f2, col1 f3 from t2 group by f1; drop table t2; -drop table t0,t1; +drop table t1; + +--echo # +--echo # MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query +--echo # +create table t1 ( + a int, + filler1 char(128), + filler2 char(128), + key(a) +); + +insert into t1 +select A.a+10*B.a+100*C.a, repeat('abc-',32), repeat('abc-',32) +from t0 A, t0 B, t0 C; +analyze table t1; + +explain select a from t1 order by a limit 10; +--echo # 'rows' in ANALYZE output must match 'rows' in EXPLAIN: +analyze select a from t1 order by a limit 10; + +drop table t1; +drop table t0; + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ea30bf96f06..de5f29d9bf4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21031,12 +21031,14 @@ check_reverse_order: } table->file->ha_index_or_rnd_end(); + + if (select_limit < table->stat_records()) + tab->limit= select_limit; + if (tab->join->select_options & SELECT_DESCRIBE) { tab->ref.key= -1; tab->ref.key_parts= 0; - if (select_limit < table->stat_records()) - tab->limit= select_limit; table->disable_keyread(); } } From 74f15e2a7f647648bb19bc7c2583c36ccd0d67fc Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Mon, 1 Feb 2016 19:36:22 +0300 Subject: [PATCH 80/83] MDEV-9505: Valgrind failure in SEL_ARG::store_min,find_used_partitions create_partition_index_description() had wrong logic to calculate length of the key value buffer that is used by the range optimizer. For some reason it used MAX(partitioning_columns_len, subpartitioning_columns_len) while it should use SUM of these values. --- mysql-test/r/partition_pruning.result | 18 ++++++++++++++++++ mysql-test/t/partition_pruning.test | 19 +++++++++++++++++++ sql/opt_range.cc | 16 +++++----------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/mysql-test/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index e52c2c7d886..65b788a7d1b 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -3467,3 +3467,21 @@ id select_type table partitions type possible_keys key key_len ref rows Extra 1 PRIMARY t2 p_1000 ref PRIMARY PRIMARY 8 const 2 Using index 1 PRIMARY t1 p_1000 ALL PRIMARY NULL NULL NULL 6 Using where; Using join buffer (flat, BNL join) drop table t1,t2; +# +# MDEV-9505: Valgrind failure in SEL_ARG::store_min,find_used_partitions,... +# +create table t1 (a int, b char(10), c varchar(5), d int) +partition by range columns(a,b,c) +subpartition by key (c,d) +subpartitions 3 +( partition p0 values less than (1,'abc','abc'), +partition p1 values less than (2,'abc','abc'), +partition p2 values less than (3,'abc','abc'), +partition p3 values less than (4,'abc','abc') +); +insert into t1 values (1,'a','b',1),(2,'a','b',2),(3,'a','b',3); +select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) OR +(a = 1 AND b >= 'a' AND (c = 'c' OR (c = 'd' AND d = 2)))); +a b c d +1 a b 1 +drop table t1; diff --git a/mysql-test/t/partition_pruning.test b/mysql-test/t/partition_pruning.test index 06ef99e1e70..9d72e9c0d01 100644 --- a/mysql-test/t/partition_pruning.test +++ b/mysql-test/t/partition_pruning.test @@ -1517,3 +1517,22 @@ and dept_id in (select dept_id from t2 where COMPANY_ID = 1000); drop table t1,t2; +--echo # +--echo # MDEV-9505: Valgrind failure in SEL_ARG::store_min,find_used_partitions,... +--echo # +create table t1 (a int, b char(10), c varchar(5), d int) +partition by range columns(a,b,c) +subpartition by key (c,d) +subpartitions 3 +( partition p0 values less than (1,'abc','abc'), + partition p1 values less than (2,'abc','abc'), + partition p2 values less than (3,'abc','abc'), + partition p3 values less than (4,'abc','abc') +); + +insert into t1 values (1,'a','b',1),(2,'a','b',2),(3,'a','b',3); +select * from t1 where (a = 1 AND b < 'd' AND (c = 'b' OR (c = 'c' AND d = 1)) OR + (a = 1 AND b >= 'a' AND (c = 'c' OR (c = 'd' AND d = 2)))); +drop table t1; + + diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 6b72f9c336b..f051ed07a7e 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -4931,15 +4931,14 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) Field **field= (ppar->part_fields)? part_info->part_field_array : part_info->subpart_field_array; bool in_subpart_fields= FALSE; - uint max_key_len= 0; - uint cur_key_len= 0; + uint total_key_len= 0; for (uint part= 0; part < total_parts; part++, key_part++) { key_part->key= 0; key_part->part= part; key_part->length= (uint16)(*field)->key_length(); key_part->store_length= (uint16)get_partition_field_store_length(*field); - cur_key_len += key_part->store_length; + total_key_len += key_part->store_length; DBUG_PRINT("info", ("part %u length %u store_length %u", part, key_part->length, key_part->store_length)); @@ -4965,18 +4964,13 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) { field= part_info->subpart_field_array; in_subpart_fields= TRUE; - max_key_len= cur_key_len; - cur_key_len= 0; } } range_par->key_parts_end= key_part; - if (cur_key_len > max_key_len) - max_key_len= cur_key_len; - - max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */ - if (!(range_par->min_key= (uchar*)alloc_root(alloc,max_key_len)) || - !(range_par->max_key= (uchar*)alloc_root(alloc,max_key_len))) + total_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */ + if (!(range_par->min_key= (uchar*)alloc_root(alloc,total_key_len)) || + !(range_par->max_key= (uchar*)alloc_root(alloc,total_key_len))) { return true; } From 52d695fef4f959daf12cc1e4b83907917dddfbe8 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 1 Feb 2016 17:51:57 +0100 Subject: [PATCH 81/83] Fix authentication plugin's tests in case username contains non-alphanumeric character, e.g dash --- mysql-test/r/auth_named_pipe.result | 4 ++-- mysql-test/t/auth_named_pipe.test | 4 ++-- plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result | 4 ++-- plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/auth_named_pipe.result b/mysql-test/r/auth_named_pipe.result index 8de507e1744..3268b760a22 100644 --- a/mysql-test/r/auth_named_pipe.result +++ b/mysql-test/r/auth_named_pipe.result @@ -1,9 +1,9 @@ INSTALL SONAME 'auth_named_pipe'; -CREATE USER USERNAME IDENTIFIED WITH named_pipe; +CREATE USER 'USERNAME' IDENTIFIED WITH named_pipe; SELECT USER(),CURRENT_USER(); USER() CURRENT_USER() USERNAME@localhost USERNAME@% -DROP USER USERNAME; +DROP USER 'USERNAME'; CREATE USER nosuchuser IDENTIFIED WITH named_pipe; ERROR 28000: Access denied for user 'nosuchuser'@'localhost' DROP USER nosuchuser; diff --git a/mysql-test/t/auth_named_pipe.test b/mysql-test/t/auth_named_pipe.test index 5473d628246..00577fbef05 100644 --- a/mysql-test/t/auth_named_pipe.test +++ b/mysql-test/t/auth_named_pipe.test @@ -3,7 +3,7 @@ INSTALL SONAME 'auth_named_pipe'; --replace_result $USERNAME USERNAME -eval CREATE USER $USERNAME IDENTIFIED WITH named_pipe; +eval CREATE USER '$USERNAME' IDENTIFIED WITH named_pipe; # Connect using named pipe, correct username connect(pipe_con,localhost,$USERNAME,,,,,PIPE); --replace_result $USERNAME USERNAME @@ -11,7 +11,7 @@ SELECT USER(),CURRENT_USER(); disconnect pipe_con; connection default; --replace_result $USERNAME USERNAME -eval DROP USER $USERNAME; +eval DROP USER '$USERNAME'; # test invalid user name CREATE USER nosuchuser IDENTIFIED WITH named_pipe; diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result index a859ce563c7..dc5155fac8c 100644 --- a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result @@ -1,9 +1,9 @@ INSTALL SONAME 'auth_gssapi'; -CREATE USER GSSAPI_SHORTNAME IDENTIFIED WITH gssapi; +CREATE USER 'GSSAPI_SHORTNAME' IDENTIFIED WITH gssapi; SELECT USER(),CURRENT_USER(); USER() CURRENT_USER() GSSAPI_SHORTNAME@localhost GSSAPI_SHORTNAME@% -DROP USER GSSAPI_SHORTNAME; +DROP USER 'GSSAPI_SHORTNAME'; CREATE USER nosuchuser IDENTIFIED WITH gssapi; ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser', actual name 'GSSAPI_SHORTNAME' DROP USER nosuchuser; diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test index cb49c2e145b..f47ad8c20e2 100644 --- a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test @@ -4,7 +4,7 @@ INSTALL SONAME 'auth_gssapi'; # CREATE USER without 'AS' clause # --replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME -eval CREATE USER $GSSAPI_SHORTNAME IDENTIFIED WITH gssapi; +eval CREATE USER '$GSSAPI_SHORTNAME' IDENTIFIED WITH gssapi; connect (con1,localhost,$GSSAPI_SHORTNAME,,); --replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME SELECT USER(),CURRENT_USER(); @@ -12,7 +12,7 @@ disconnect con1; connection default; --replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME -eval DROP USER $GSSAPI_SHORTNAME; +eval DROP USER '$GSSAPI_SHORTNAME'; CREATE USER nosuchuser IDENTIFIED WITH gssapi; --disable_query_log From 55ea26541d4c95e765073c70126d9bb490c572c6 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 2 Feb 2016 19:54:18 +0200 Subject: [PATCH 82/83] Fixed warnings and one memory loss found by valgrind The memory loss could happen in Aria with encrypted tables when Aria failed to encrypt a block. In normal usage this should never happen. --- mysql-test/valgrind.supp | 9 +++++++++ storage/maria/ma_check.c | 14 +------------- storage/maria/ma_crypt.c | 3 +++ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index a9e1a9e9d8e..9a3301900ae 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1277,6 +1277,15 @@ fun:dlopen@@GLIBC_2.2.5 } +{ + vasprintf in OpenSuse 12.3 + Memcheck:Leak + fun:malloc + fun:vasprintf + fun:asprintf + fun:dlerror +} + { GitHub codership/galera#308 Memcheck:Leak diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c index b517588f959..5035df26b18 100644 --- a/storage/maria/ma_check.c +++ b/storage/maria/ma_check.c @@ -6635,17 +6635,6 @@ static void copy_data_file_state(MARIA_STATE_INFO *to, } -/* Return 1 if block is full of zero's */ - -static my_bool zero_filled_block(uchar *tmp, uint length) -{ - while (length--) - if (*(tmp++) != 0) - return 0; - return 1; -} - - /* Read 'safely' next record while scanning table. @@ -6753,8 +6742,7 @@ read_next_page: sometimes be found at end of a bitmap when we wrote a big record last that was moved to the next bitmap. */ - if (!zero_filled_block(info->scan.page_buff, share->block_size) || - _ma_check_bitmap_data(info, UNALLOCATED_PAGE, 0, + if (_ma_check_bitmap_data(info, UNALLOCATED_PAGE, 0, _ma_bitmap_get_page_bits(info, &share->bitmap, page))) diff --git a/storage/maria/ma_crypt.c b/storage/maria/ma_crypt.c index da82a1a37c8..cc605d79933 100644 --- a/storage/maria/ma_crypt.c +++ b/storage/maria/ma_crypt.c @@ -416,7 +416,10 @@ static my_bool ma_crypt_index_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args) /* 2 - encrypt page */ if (ma_encrypt(share, share->crypt_data, src + head, dst + head, size, pageno, lsn, &key_version)) + { + my_free(crypt_buf); return 1; + } /* 3 - copy tail */ memcpy(dst + block_size - tail, src + block_size - tail, tail); /* 4 - store key version */ From 07b8aefe90ca830d2de068f2966cd2288b158a88 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 3 Feb 2016 00:15:49 +0300 Subject: [PATCH 83/83] MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query Revert the patch for MDEV-9504. It causes test failures, attempt to fix these causes more failures. The source of all this is that the code in test_if_skip_sort_order() has a peculiar way of treating select_limit parameter: Correct value is computed when the query plan is changed. In other cases, we use an approximation that ignores the presence of GROUP BY clause, or JOINs, or both. A patch that fixes all of the above would be too big to do in 10.1 --- mysql-test/r/analyze_stmt_orderby.result | 26 +----------------------- mysql-test/t/analyze_stmt_orderby.test | 25 +---------------------- sql/sql_select.cc | 6 ++---- 3 files changed, 4 insertions(+), 53 deletions(-) diff --git a/mysql-test/r/analyze_stmt_orderby.result b/mysql-test/r/analyze_stmt_orderby.result index 643c0318f95..be1f01a2a52 100644 --- a/mysql-test/r/analyze_stmt_orderby.result +++ b/mysql-test/r/analyze_stmt_orderby.result @@ -583,28 +583,4 @@ ANALYZE } } drop table t2; -drop table t1; -# -# MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query -# -create table t1 ( -a int, -filler1 char(128), -filler2 char(128), -key(a) -); -insert into t1 -select A.a+10*B.a+100*C.a, repeat('abc-',32), repeat('abc-',32) -from t0 A, t0 B, t0 C; -analyze table t1; -Table Op Msg_type Msg_text -test.t1 analyze status Table is already up to date -explain select a from t1 order by a limit 10; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL a 5 NULL 10 Using index -# 'rows' in ANALYZE output must match 'rows' in EXPLAIN: -analyze select a from t1 order by a limit 10; -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 SIMPLE t1 index NULL a 5 NULL 10 10.00 100.00 100.00 Using index -drop table t1; -drop table t0; +drop table t0,t1; diff --git a/mysql-test/t/analyze_stmt_orderby.test b/mysql-test/t/analyze_stmt_orderby.test index dc63fcaa285..a40f34805d1 100644 --- a/mysql-test/t/analyze_stmt_orderby.test +++ b/mysql-test/t/analyze_stmt_orderby.test @@ -174,27 +174,4 @@ select col1 f1, col2 f2, col1 f3 from t2 group by f1; drop table t2; -drop table t1; - ---echo # ---echo # MDEV-9504: ANALYZE TABLE shows wrong 'rows' value for ORDER BY query ---echo # -create table t1 ( - a int, - filler1 char(128), - filler2 char(128), - key(a) -); - -insert into t1 -select A.a+10*B.a+100*C.a, repeat('abc-',32), repeat('abc-',32) -from t0 A, t0 B, t0 C; -analyze table t1; - -explain select a from t1 order by a limit 10; ---echo # 'rows' in ANALYZE output must match 'rows' in EXPLAIN: -analyze select a from t1 order by a limit 10; - -drop table t1; -drop table t0; - +drop table t0,t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index de5f29d9bf4..ea30bf96f06 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21031,14 +21031,12 @@ check_reverse_order: } table->file->ha_index_or_rnd_end(); - - if (select_limit < table->stat_records()) - tab->limit= select_limit; - if (tab->join->select_options & SELECT_DESCRIBE) { tab->ref.key= -1; tab->ref.key_parts= 0; + if (select_limit < table->stat_records()) + tab->limit= select_limit; table->disable_keyread(); } }