diff options
166 files changed, 5719 insertions, 1144 deletions
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/BUILD/SETUP.sh b/BUILD/SETUP.sh index f443c5da680..36072ebaa7e 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 diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 3bbfc70eaed..860e442a290 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -4977,12 +4977,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/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}/*") diff --git a/cmake/install_macros.cmake b/cmake/install_macros.cmake index 62ccca8deb4..17ee3c61554 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/libutils.cmake b/cmake/libutils.cmake index 1eb782a9078..bcba924dfa8 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() diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake index 82cfda6f6eb..bb7c8862dc7 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() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 7b91f0b5747..8bc9df973c0 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -18,6 +18,7 @@ INCLUDE(CMakeParseArguments) # MYSQL_ADD_PLUGIN(plugin_name source1...sourceN # [STORAGE_ENGINE] +# [CLIENT] # [MANDATORY|DEFAULT] # [STATIC_ONLY|DYNAMIC_ONLY] # [MODULE_OUTPUT_NAME module_name] @@ -28,7 +29,7 @@ INCLUDE(CMakeParseArguments) MACRO(MYSQL_ADD_PLUGIN) CMAKE_PARSE_ARGUMENTS(ARG - "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" "MODULE_OUTPUT_NAME;STATIC_OUTPUT_NAME;COMPONENT;CONFIG" "LINK_LIBRARIES;DEPENDENCIES" ${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,22 @@ 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_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") + SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} ${ARG_COMPONENT}) + SET(CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} PARENT_SCOPE) + IF (NOT ARG_CLIENT) + SET(CPACK_RPM_${ARG_COMPONENT}_PACKAGE_REQUIRES "MariaDB" PARENT_SCOPE) 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) + 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() ENDIF() ELSE() SET(ARG_COMPONENT Server) diff --git a/config.h.cmake b/config.h.cmake index 6b072806019..43c8f735ff5 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -88,6 +88,7 @@ #cmakedefine HAVE_SYS_UN_H 1 #cmakedefine HAVE_SYS_VADVISE_H 1 #cmakedefine HAVE_SYS_STATVFS_H 1 +#cmakedefine HAVE_UCONTEXT_H 1 #cmakedefine HAVE_TERM_H 1 #cmakedefine HAVE_TERMBITS_H 1 #cmakedefine HAVE_TERMIOS_H 1 @@ -237,6 +238,7 @@ #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 #cmakedefine HAVE_VASPRINTF 1 diff --git a/configure.cmake b/configure.cmake index ca9d36eace3..b0c2a07ced1 100644 --- a/configure.cmake +++ b/configure.cmake @@ -977,6 +977,14 @@ CHECK_STRUCT_HAS_MEMBER("struct sockaddr_in6" sin6_len SET(CMAKE_EXTRA_INCLUDE_FILES) +CHECK_STRUCT_HAS_MEMBER("struct dirent" d_ino "dirent.h" STRUCT_DIRENT_HAS_D_INO) +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() + CHECK_STRUCT_HAS_MEMBER("struct timespec" tv_sec "time.h" STRUCT_TIMESPEC_HAS_TV_SEC) CHECK_STRUCT_HAS_MEMBER("struct timespec" tv_nsec "time.h" STRUCT_TIMESPEC_HAS_TV_NSEC) @@ -997,4 +1005,3 @@ IF(NOT MSVC) HAVE_FALLOC_PUNCH_HOLE_AND_KEEP_SIZE ) ENDIF() - diff --git a/debian/mariadb-server-10.2.files.in b/debian/mariadb-server-10.2.files.in index 633722ccb35..eaabbb53dca 100644 --- a/debian/mariadb-server-10.2.files.in +++ b/debian/mariadb-server-10.2.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 @@ -83,6 +84,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 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); } diff --git a/include/my_context.h b/include/my_context.h index 5de9f2882c5..228f0c18f62 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/include/my_pthread.h b/include/my_pthread.h index b18b9d824a2..321d44df60a 100644 --- a/include/my_pthread.h +++ b/include/my_pthread.h @@ -694,7 +694,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/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 /* 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/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/auth_named_pipe.result b/mysql-test/r/auth_named_pipe.result new file mode 100644 index 00000000000..3268b760a22 --- /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/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/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/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/r/events_bugs.result b/mysql-test/r/events_bugs.result index 3de6a6e48fc..8920c89fcab 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -91,7 +91,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| @@ -100,7 +100,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'); @@ -109,7 +109,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| @@ -157,13 +157,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/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/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/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 d2cbc073a46..21c6beac7e3 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -1431,7 +1431,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/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(<cache>(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/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/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index c6d7c383ce7..fe19473f8e4 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -3466,3 +3466,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/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/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/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/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..6f59952c1fa --- /dev/null +++ b/mysql-test/suite/maria/encrypt-wrong-key.test @@ -0,0 +1,54 @@ +--source include/not_embedded.inc + +# +# 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/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/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index bf71e518f97..2577a36cad4 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 @@ -289,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,root,localhost,ID,0,DISCONNECT,sa_db,,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, 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/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/r/thread_pool_server_audit.result b/mysql-test/suite/plugins/r/thread_pool_server_audit.result index bf71e518f97..2577a36cad4 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 @@ -289,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,root,localhost,ID,0,DISCONNECT,sa_db,,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, 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/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/suite/rpl/r/rpl_parallel.result b/mysql-test/suite/rpl/r/rpl_parallel.result index 6c904aefa92..631b071136b 100644 --- a/mysql-test/suite/rpl/r/rpl_parallel.result +++ b/mysql-test/suite/rpl/r/rpl_parallel.result @@ -930,6 +930,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/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_parallel.test b/mysql-test/suite/rpl/t/rpl_parallel.test index 8a512848145..a7e5353a9fc 100644 --- a/mysql-test/suite/rpl/t/rpl_parallel.test +++ b/mysql-test/suite/rpl/t/rpl_parallel.test @@ -1422,6 +1422,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 @@ -1437,6 +1438,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; @@ -1469,7 +1471,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/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/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 91ff7c62d47..22450701bf0 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 46851c8ac95..03fa52b37d3 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -4645,9 +4645,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; 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..00577fbef05 --- /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/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/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/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 diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index c0157a7727e..df40b840a68 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -306,7 +306,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| @@ -315,7 +315,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'); @@ -324,7 +324,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| @@ -412,13 +412,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/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 @@ -1774,5 +1774,15 @@ SELECT * FROM t1 WHERE a=18446744073709551615 AND FORMAT(a,0)='18,446,744,073,70 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/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/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/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/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/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/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index a9e1a9e9d8e..9a3301900ae 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1278,6 +1278,15 @@ } { + vasprintf in OpenSuse 12.3 + Memcheck:Leak + fun:malloc + fun:vasprintf + fun:asprintf + fun:dlerror +} + +{ GitHub codership/galera#308 Memcheck:Leak match-leak-kinds: definite 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 <my_sys.h> #include <m_string.h> #include <mysql/service_logger.h> #include <my_pthread.h> +#endif /*FLOGGER_SKIP_INCLUDES*/ #ifndef flogger_mutex_init #define flogger_mutex_init(A,B,C) mysql_mutex_init(A,B,C) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c index 9c9a7d9b97c..72b04119855 100644 --- a/mysys/my_addr_resolve.c +++ b/mysys/my_addr_resolve.c @@ -145,30 +145,75 @@ 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; + ssize_t total_bytes_read = 0; + ssize_t extra_bytes_read = 0; + + fd_set set; + struct timeval timeout; + + 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; - if (read(out[0], output, sizeof(output)) <= 0) + + /* 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; + /* Timeout or max bytes read. */ + if (extra_bytes_read == 0) + break; + + total_bytes_read += extra_bytes_read; + } + + /* Failed starting addr2line. */ + if (total_bytes_read == 0) return 1; - loc->func= s= output; - while (*s != '\n') - s++; - *s++= 0; - loc->file= s; - while (*s != ':') - s++; - *s++= 0; + /* Go through the addr2line response and get the required data. + The response is structured in 2 lines. The first line contains the function + name, while the second one contains <filename>:<line number> */ + for (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; 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 new file mode 100644 index 00000000000..f85d0deaea1 --- /dev/null +++ b/plugin/auth_gssapi/CMakeLists.txt @@ -0,0 +1,36 @@ +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} + 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 new file mode 100644 index 00000000000..7f678400f0d --- /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 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)** + +- 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 *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 + +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..0ab619a08e6 --- /dev/null +++ b/plugin/auth_gssapi/client_plugin.cc @@ -0,0 +1,112 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <string.h> +#include <stdarg.h> +#include <mysqld_error.h> +#include <mysql/client_plugin.h> +#include <mysql.h> +#include <stdio.h> +#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..78111fc2368 --- /dev/null +++ b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake @@ -0,0 +1,98 @@ +# - 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, <toscano.pino@tiscali.it> +# +# 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) + + # 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..a05ea158e4d --- /dev/null +++ b/plugin/auth_gssapi/gssapi_client.cc @@ -0,0 +1,127 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <gssapi/gssapi.h> +#include <string.h> +#include <stdio.h> +#include <mysql/plugin_auth.h> +#include <mysqld_error.h> +#include <mysql.h> +#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..8ea4cab5b02 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_errmsg.cc @@ -0,0 +1,75 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <gssapi.h> +#include <string.h> +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..26db8439e04 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_errmsg.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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..ed042a026b1 --- /dev/null +++ b/plugin/auth_gssapi/gssapi_server.cc @@ -0,0 +1,247 @@ +#include <my_config.h> +#include <gssapi/gssapi.h> +#include <stdio.h> +#include <mysql/plugin_auth.h> +#include <my_sys.h> +#include <mysqld_error.h> +#include <log.h> +#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 <krb5.h> +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, (krb5_enctype)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..dc5155fac8c --- /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..f47ad8c20e2 --- /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..50137ca0211 --- /dev/null +++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm @@ -0,0 +1,49 @@ +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" +} + +if ($::opt_verbose) +{ + 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..a79074abe79 --- /dev/null +++ b/plugin/auth_gssapi/server_plugin.cc @@ -0,0 +1,183 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, + 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 <my_sys.h> +#include <mysqld_error.h> +#include <mysql/plugin_auth.h> +#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; + + /* 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)) + { + 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 for Kerberos authentication", + 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", + 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_BETA +} +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..1348835e653 --- /dev/null +++ b/plugin/auth_gssapi/server_plugin.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <mysql/plugin_auth.h> +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..34b8a56a32e --- /dev/null +++ b/plugin/auth_gssapi/sspi.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <windows.h> +#include <sspi.h> +#include <SecExt.h> +#include <stdarg.h> +#include <stdio.h> + +#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..34c1ce2e7ee --- /dev/null +++ b/plugin/auth_gssapi/sspi_client.cc @@ -0,0 +1,183 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <windows.h> +#include <sspi.h> +#include <SecExt.h> +#include <stdarg.h> +#include <stdio.h> + +#include <mysql/plugin_auth.h> +#include <mysql.h> +#include <mysqld_error.h> + +#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..961ef51f42e --- /dev/null +++ b/plugin/auth_gssapi/sspi_errmsg.cc @@ -0,0 +1,150 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <windows.h> +#include <stdio.h> + +#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..1dfd2986aaa --- /dev/null +++ b/plugin/auth_gssapi/sspi_server.cc @@ -0,0 +1,312 @@ +/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood, +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 <mysql/plugin_auth.h> +#include <my_sys.h> +#include <mysqld_error.h> +#include <log.h> + + +/* 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; +} 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; diff --git a/plugin/auth_pipe/CMakeLists.txt b/plugin/auth_pipe/CMakeLists.txt new file mode 100644 index 00000000000..bbc44d0f5e2 --- /dev/null +++ b/plugin/auth_pipe/CMakeLists.txt @@ -0,0 +1,3 @@ +IF(WIN32) + 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 new file mode 100644 index 00000000000..20c33c07e84 --- /dev/null +++ b/plugin/auth_pipe/auth_pipe.c @@ -0,0 +1,94 @@ +/* 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_pipe authentication plugin. + + Authentication is successful if the connection is done via a named pipe + pipe peer name matches mysql user name +*/ + +#include <mysql/plugin_auth.h> +#include <string.h> +#include <lmcons.h> + + +/** + 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; + MYSQL_PLUGIN_VIO_INFO vio_info; + 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) + { + 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; + + /* Impersonate the named pipe peer, and retrieve the user name */ + if (!ImpersonateNamedPipeClient(vio_info.handle)) + return CR_ERROR; + + 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(); + + return ret; +} + +static struct st_mysql_auth pipe_auth_handler= +{ + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + 0, + pipe_auth +}; + +maria_declare_plugin(auth_named_pipe) +{ + MYSQL_AUTHENTICATION_PLUGIN, + &pipe_auth_handler, + "named_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; + 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 f7e18c88202..7831896686e 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -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 <my_config.h> #include <stdio.h> #include <time.h> #include <string.h> +#include <fcntl.h> #ifndef _WIN32 #include <syslog.h> @@ -80,8 +83,7 @@ static void closelog() {} #endif /*MARIADB_ONLY*/ #include <my_base.h> -//#include <hash.h> -#include <my_dir.h> +//#include <my_dir.h> #include <typelib.h> #include <mysql/plugin.h> #include <mysql/plugin_audit.h> @@ -89,73 +91,170 @@ 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_init2 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_hash_sort loc_my_hash_sort -#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 -void *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= slen < tlen ? 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 /* How to access the pthread_mutex in mysql_mutex_t */ -#ifdef SAFE_MUTEX -#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) 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)) +#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) 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) 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 + + +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 */ +{ + 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 + 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; + +#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 <mysql/service_logger.h> +#include "../../mysys/file_logger.c" +#define HAVE_PSI_INTERFACE +#else +#include <mysql/service_logger.h> #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 @@ -176,7 +275,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; @@ -201,6 +304,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"; @@ -254,7 +378,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 @@ -295,6 +420,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", @@ -374,6 +506,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 }; @@ -384,6 +517,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}, @@ -423,7 +558,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= ' '; @@ -483,19 +618,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, 0); + c->n_users= 0; while (*users) { @@ -504,11 +718,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) { @@ -518,7 +731,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) { @@ -530,7 +743,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++; @@ -539,15 +752,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; } @@ -676,48 +891,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 -{ - 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))); -} - - -void free_connection(void* pconn) +static struct connection_info *get_loc_info(MYSQL_THD thd) { - (void) free(pconn); + return (struct connection_info *) THDVAR(thd, loc_info); } -static struct connection_info *find_connection(unsigned long id) +static int ci_needs_setup(const struct connection_info *ci) { - return (struct connection_info *) - my_hash_search(&connection_hash, (const uchar *) &id, sizeof(id)); + return ci->header != 0; } @@ -881,15 +1067,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), @@ -898,11 +1093,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; } @@ -925,46 +1116,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; @@ -976,42 +1164,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; } @@ -1103,6 +1289,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) { @@ -1248,11 +1455,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; } @@ -1590,14 +1797,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) { @@ -1610,13 +1817,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; @@ -1625,18 +1836,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), @@ -1662,13 +1874,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); @@ -1683,9 +1895,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), @@ -1711,17 +1923,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:; } @@ -1730,17 +1935,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 */ @@ -1750,8 +1955,35 @@ 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; + 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 + variables in some cases. + The 'select * from notexisting_table;' query produces such case. + So just use the static buffer in this case. + */ + 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); if (!logging) goto exit_func; @@ -1765,8 +1997,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) { @@ -1807,7 +2042,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"); @@ -1822,10 +2060,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 = @@ -1843,28 +2077,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; @@ -1882,8 +2094,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) @@ -1942,6 +2177,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 @@ -2068,8 +2338,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) { @@ -2087,9 +2357,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, 0, free_connection, 0); - error_header(); fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n", PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION); @@ -2113,6 +2380,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(); @@ -2131,13 +2408,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); @@ -2172,6 +2444,7 @@ static struct st_mysql_audit mysql_descriptor = { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } }; + mysql_declare_plugin(server_audit) { MYSQL_AUDIT_PLUGIN, @@ -2222,22 +2495,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; } } @@ -2249,7 +2521,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); @@ -2285,7 +2558,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); } @@ -2329,14 +2603,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_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); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2345,14 +2621,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_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); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2428,7 +2706,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)) { @@ -2445,7 +2724,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); } @@ -2457,14 +2737,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); } @@ -2488,6 +2770,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) { @@ -2505,9 +2794,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'; @@ -2524,7 +2822,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 <stdio.h> + +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; + diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 256797dc9d0..224e34de73b 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 ) @@ -287,6 +285,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 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@ diff --git a/sql-common/client.c b/sql-common/client.c index 41a262ad448..7571e5c0b1d 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -4337,6 +4337,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); } diff --git a/sql/item.h b/sql/item.h index 94107d897d6..d0e4372d53f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3683,6 +3683,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. @@ -3692,10 +3697,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 @@ -3708,10 +3710,7 @@ public: List_iterator_fast<Item> 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); } }; @@ -5135,6 +5134,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<Item> &fields, uint flags) + { + example->split_sum_func2(thd, ref_pointer_array, fields, &example, flags); + } + Item *get_example() const { return example; } }; @@ -5239,6 +5244,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 0c2d241309d..dc457ed6484 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2526,12 +2526,185 @@ 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<Item> &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; + 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) + { + /* + 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[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 + 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] in some cases 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[1] > r_expr + args[0] \ + l_expr + args[2] / + + b. Conversion of args[0] happened: + + CREATE OR REPLACE TABLE t1 ( + a CHAR(10) CHARACTER SET latin1, + b CHAR(10) CHARACTER SET utf8); + SELECT * FROM t1 WHERE NULLIF(a,b); + + args[1] > r_expr (Item_field for t1.b) + args[0] > Item_func_conv_charset\ + l_expr (Item_field for t1.a) + args[2] > ----------------------/ + + c. Conversion of args[1] happened: + + 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[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: + + 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 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; @@ -2606,6 +2779,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 @@ -2621,7 +2801,7 @@ Item_func_nullif::real_op() { DBUG_ASSERT(fixed == 1); double value; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0.0; @@ -2636,7 +2816,7 @@ Item_func_nullif::int_op() { DBUG_ASSERT(fixed == 1); longlong value; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0; @@ -2651,7 +2831,7 @@ Item_func_nullif::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; - if (!cmp.compare()) + if (!compare()) { null_value=1; return 0; @@ -2667,7 +2847,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; @@ -2682,7 +2862,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)); } @@ -2691,7 +2871,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 c14ee42016b..af9e008375a 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -996,10 +996,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(); @@ -1010,6 +1013,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<Item> &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) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index bc39517a914..e22718029e5 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 dfa38d7eed6..0ff38157c25 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -960,20 +960,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/sql/mysqld.cc b/sql/mysqld.cc index 0c883de7821..c2da0a6e542 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5235,49 +5235,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<THD> 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) { @@ -5925,6 +5882,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) @@ -5965,9 +5925,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(); @@ -7773,8 +7730,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) @@ -7785,6 +7742,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, enum enum_var_type scope) { @@ -8493,6 +8482,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 b6da7933bb1..d81c89fe534 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 <my_global.h> #include <mysql.h> #include <mysql_com.h> @@ -107,13 +108,12 @@ extern void query_cache_insert(void *thd, 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 @@ -876,6 +876,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/opt_range.cc b/sql/opt_range.cc index 7e6753e9bf6..ed7e9a56ae5 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -4336,15 +4336,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)); @@ -4370,18 +4369,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; } diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 216fbde0177..df721342d1d 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), @@ -1396,23 +1397,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); } @@ -1442,7 +1444,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/rpl_mi.h b/sql/rpl_mi.h index 69d602c1dcb..9365c065ea9 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -345,7 +345,7 @@ public: Master_info *get_master_info(const 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 987e011d5eb..2dd6f7d7afc 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) { @@ -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, @@ -1148,6 +1149,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 0afd13eb19b..376cd7a82ef 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7106,7 +7106,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 @@ -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/slave.cc b/sql/slave.cc index 8512fc229c1..72bed45d245 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -117,7 +117,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, \ @@ -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<Item> 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,226 +2510,222 @@ 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<Item> *field_list, + bool full, size_t gtid_pos_length) { - List<Item> 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, "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, "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), + 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); - } - - if (protocol->send_result_set_metadata(&field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); - DBUG_RETURN(FALSE); + 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); + } + DBUG_VOID_RETURN; } +/* 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) @@ -2780,9 +2778,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()); @@ -2825,7 +2821,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; @@ -2962,6 +2958,7 @@ bool show_all_master_info(THD* thd) uint i, elements; String gtid_pos; Master_info **tmp; + List<Item> 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 || @@ -4128,16 +4127,16 @@ 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; } const char *event_buf; + mi->slave_running= MYSQL_SLAVE_RUN_READING; DBUG_ASSERT(mi->last_error().number == 0); while (!io_slave_killed(mi)) { @@ -4150,8 +4149,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; if (event_len == packet_error) @@ -4607,15 +4605,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, @@ -4886,6 +4875,7 @@ pthread_handler_t handle_slave_sql(void *arg) } 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/slave.h b/sql/slave.h index a519229fac1..ca89064d773 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -135,11 +135,11 @@ extern const char *relay_log_index; extern const char *relay_log_basename; /* - 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 @@ -148,6 +148,7 @@ extern const char *relay_log_basename; #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") @@ -206,8 +207,11 @@ 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<Item> *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<Item> *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/sp_head.cc b/sql/sp_head.cc index ce4ec09bede..d142b2e1f37 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2534,6 +2534,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<Item> *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. @param thd Thread context. diff --git a/sql/sp_head.h b/sql/sp_head.h index 4e761c31d5b..9022f954d11 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -337,6 +337,9 @@ public: bool execute_procedure(THD *thd, List<Item> *args); + static void + show_create_routine_get_fields(THD *thd, int type, List<Item> *fields); + bool show_create_routine(THD *thd, int type); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7d6301cb5b4..6c6c8263449 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; } /** @@ -984,16 +1000,17 @@ 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, - 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 +1018,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,36 +1031,26 @@ 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) { - my_error(ER_PASSWD_LENGTH, MYF(0), check_length); - return true; + user->pwhash= user->auth; + user->plugin= empty_lex_str; + user->auth= empty_lex_str; } - if (user->password.length || !user->auth.length) - { - if (validate_password(&user->user, &user->password)) - { - my_error(ER_NOT_VALID_PASSWORD, MYF(0)); - return true; - } - } - else + if (user->pwhash.length && user->pwhash.length != check_length) { - if (strict_password_validation && has_validation_plugins()) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation"); - return true; - } + 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) { size_t scramble_length; void (*make_scramble)(char *, const char *, size_t); @@ -1059,14 +1069,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; } @@ -2737,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; @@ -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; @@ -3450,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; @@ -3465,8 +3478,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 +3542,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 +3618,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 +3634,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 +5692,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 +5703,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; } @@ -7723,6 +7737,16 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data) } +void mysql_show_grants_get_fields(THD *thd, List<Item> *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 @@ -7788,15 +7812,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<Item> 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)) @@ -10153,15 +10176,11 @@ 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) { - 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_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<Item> *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_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(); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1ff3625e2db..c8b0092d246 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8962,6 +8962,9 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &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<Item> &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 && diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a38822c3f16..345feb6094a 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1817,7 +1817,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); @@ -4024,6 +4025,12 @@ void thd_increment_bytes_sent(void *thd, ulong length) } } +my_bool thd_net_is_killed() +{ + THD *thd= current_thd; + return thd && thd->killed ? 1 : 0; +} + void thd_increment_bytes_received(void *thd, ulong length) { @@ -5158,9 +5165,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_class.h b/sql/sql_class.h index ffa0a3b887e..a15a738b1bf 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -704,6 +704,7 @@ typedef struct system_status_var ulong com_stmt_reset; ulong com_stmt_close; + 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 4548a0bf47c..84f0c6369fd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1353,6 +1353,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; @@ -8942,9 +8943,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(); } @@ -9002,7 +9001,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_prepare.cc b/sql/sql_prepare.cc index 809e6d63338..ca23e11676a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -103,6 +103,9 @@ 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 "sql_repl.h" +#include "slave.h" #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -1799,6 +1802,191 @@ static bool mysql_test_create_table(Prepared_statement *stmt) } +static int send_stmt_metadata(THD *thd, Prepared_statement *stmt, List<Item> *fields) +{ + 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; +} + + +/** + Validate and prepare for execution SHOW 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<Item> fields; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + + if (mysqld_show_create_get_fields(thd, tables, &fields, &buffer)) + DBUG_RETURN(1); + + DBUG_RETURN(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 int mysql_test_show_create_db(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_create_db"); + THD *thd= stmt->thd; + List<Item> fields; + + mysqld_show_create_db_get_fields(thd, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/** + 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 int mysql_test_show_grants(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_grants"); + THD *thd= stmt->thd; + List<Item> fields; + + mysql_show_grants_get_fields(thd, &fields, "Grants for"); + + 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. + + @param stmt prepared statement + + @retval + FALSE success + @retval + TRUE error, error message is set in THD +*/ + +static int mysql_test_show_slave_status(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_slave_status"); + THD *thd= stmt->thd; + List<Item> fields; + + show_master_info_get_fields(thd, &fields, 0, 0); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + +/** + 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 int mysql_test_show_master_status(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_master_status"); + THD *thd= stmt->thd; + List<Item> fields; + + show_binlog_info_get_fields(thd, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + + +/** + 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 int mysql_test_show_binlogs(Prepared_statement *stmt) +{ + DBUG_ENTER("mysql_test_show_binlogs"); + THD *thd= stmt->thd; + List<Item> fields; + + show_binlogs_get_fields(thd, &fields); + + DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); +} + +#endif /* EMBEDDED_LIBRARY */ + + +/** + 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 int mysql_test_show_create_routine(Prepared_statement *stmt, int type) +{ + DBUG_ENTER("mysql_test_show_binlogs"); + THD *thd= stmt->thd; + List<Item> 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 @@ -2132,7 +2320,66 @@ static bool check_prepared_statement(Prepared_statement *stmt) case SQLCOM_CREATE_TABLE: res= mysql_test_create_table(stmt); break; - + case SQLCOM_SHOW_CREATE: + 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)) == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + case SQLCOM_SHOW_GRANTS: + if ((res= mysql_test_show_grants(stmt)) == 2) + { + /* Statement and field info has already been sent */ + 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)) == 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)) == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; + case SQLCOM_SHOW_BINLOGS: + if ((res= mysql_test_show_binlogs(stmt)) == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; +#endif /* EMBEDDED_LIBRARY */ + case SQLCOM_SHOW_CREATE_PROC: + 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)) == 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_repl.cc b/sql/sql_repl.cc index e524153ad15..19f7e5ab4e6 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<Item> *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<Item> 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)) @@ -4140,6 +4146,19 @@ bool show_binlog_info(THD* thd) } +void show_binlogs_get_fields(THD *thd, List<Item> *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. @@ -4159,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()) @@ -4168,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<Item> *field_list); bool show_binlogs(THD* thd); extern int init_master_info(Master_info* mi); void kill_zombie_dump_threads(uint32 slave_server_id); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index efc710509e7..cb0f9e594ff 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -20633,7 +20633,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; @@ -25298,8 +25306,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, @@ -25328,7 +25340,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) @@ -25393,7 +25404,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]; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0d2d07a4503..f4dd24a1d0f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1092,39 +1092,29 @@ public: /* - Return CREATE command for table or view + 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 - @notes - table_list->db and table_list->table_name are kept unchanged to - not cause problems with SP. */ bool -mysqld_show_create(THD *thd, TABLE_LIST *table_list) +mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, + List<Item> *field_list, String *buffer) { - Protocol *protocol= thd->protocol; - char buff[2048]; - String buffer(buff, sizeof(buff), system_charset_info); - List<Item> field_list; bool error= TRUE; MEM_ROOT *mem_root= thd->mem_root; - DBUG_ENTER("mysqld_show_create"); + DBUG_ENTER("mysqld_show_create_get_fields"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->table_name)); - /* - Metadata locks taken during SHOW CREATE should be released when - the statmement completes as it is an information statement. - */ - 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; @@ -1155,45 +1145,88 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) goto exit; } - buffer.length(0); + buffer->length(0); if (table_list->view) - buffer.set_charset(table_list->view_creation_ctx->get_client_cs()); + 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))) + 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) + field_list->push_back(new (mem_root) Item_empty_string(thd, "View", NAME_CHAR_LEN), mem_root); - field_list.push_back(new (mem_root) + field_list->push_back(new (mem_root) Item_empty_string(thd, "Create View", - MY_MAX(buffer.length(),1024)), + MY_MAX(buffer->length(),1024)), mem_root); - field_list.push_back(new (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) + 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) + 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) + field_list->push_back(new (mem_root) Item_empty_string(thd, "Create Table", - MY_MAX(buffer.length(),1024)), + MY_MAX(buffer->length(),1024)), mem_root); } + error= FALSE; + +exit: + DBUG_RETURN(error); +} + + +/* + Return CREATE command for table or view + + @param thd Thread handler + @param table_list Table / view + + @return + @retval 0 OK + @retval 1 Error + + @notes + table_list->db and table_list->table_name are kept unchanged to + not cause problems with SP. +*/ + +bool +mysqld_show_create(THD *thd, TABLE_LIST *table_list) +{ + Protocol *protocol= thd->protocol; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + List<Item> field_list; + bool error= TRUE; + DBUG_ENTER("mysqld_show_create"); + DBUG_PRINT("enter",("db: %s table: %s",table_list->db, + table_list->table_name)); + + /* + Metadata locks taken during SHOW CREATE should be released when + the statmement completes as it is an information statement. + */ + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + + + if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) + goto exit; if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | @@ -1239,6 +1272,19 @@ exit: DBUG_RETURN(error); } + +void mysqld_show_create_db_get_fields(THD *thd, List<Item> *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) @@ -1251,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<Item> field_list; DBUG_ENTER("mysql_show_create_db"); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -1285,13 +1331,8 @@ bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname, load_db_opt_by_name(thd, dbname->str, &create); } - List<Item> 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 029249f4129..ecb7e9468ea 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -85,7 +85,10 @@ 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<Item> *field_list, String *buffer); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); +void mysqld_show_create_db_get_fields(THD *thd, List<Item> *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/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('', <pos>, <length>, <replacement>); + 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. } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 3eb6a99a131..1899dd60f88 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. @@ -1950,6 +1979,7 @@ END_OF_INPUT %type <NONE> sp_proc_stmt_iterate %type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close %type <NONE> case_stmt_specification +%type <NONE> loop_body while_body repeat_body %type <num> sp_decl_idents sp_handler_type sp_hcond_list %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value @@ -3751,7 +3781,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)) || @@ -3793,20 +3823,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 { @@ -4225,41 +4241,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; } @@ -4352,8 +4333,7 @@ sp_block_content: } ; -sp_control_content: - LOOP_SYM +loop_body: sp_proc_stmts1 END LOOP_SYM { LEX *lex= Lex; @@ -4365,15 +4345,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(thd, i, lex->spcont->last_label()) || @@ -4395,7 +4376,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 { @@ -4403,7 +4387,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; @@ -4414,6 +4398,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; } @@ -14080,9 +14142,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, @@ -14094,9 +14154,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, @@ -15012,14 +15070,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; } ; @@ -15358,9 +15416,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(); } ; @@ -15379,9 +15435,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, @@ -15597,14 +15651,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 8dc6323bde0..e51f3e0fe3a 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) { @@ -211,6 +212,12 @@ typedef struct st_lex_user { else l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf; } + void reset_auth() + { + pwtext.length= pwhash.length= plugin.length= auth.length= 0; + pwtext.str= pwhash.str= 0; + plugin.str= auth.str= const_cast<char*>(""); + } } LEX_USER; /* diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 31e0be3cd4d..08f4e3e4ea0 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4483,6 +4483,28 @@ 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 && mi->using_parallel()) + { + 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; 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 ab09a9e3a99..fb48c1ad60e 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -369,6 +369,25 @@ static void wsrep_replication_process(THD *thd) DBUG_VOID_RETURN; } +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; +} + void wsrep_create_appliers(long threads) { if (!wsrep_connected) @@ -385,11 +404,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 +492,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"); } } 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/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'); 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/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..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", "*"); + Database = GetStringCatInfo(g, "Database", Schema ? Schema : (char*)"*"); 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 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); - - uint cnt = srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = 0; - fil_crypt_set_thread_cnt(cnt); - fil_crypt_threads_inited = true; + 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_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/include/fil0fil.ic b/storage/innobase/include/fil0fil.ic index a0bfb271d39..5654d8f6178 100644 --- a/storage/innobase/include/fil0fil.ic +++ b/storage/innobase/include/fil0fil.ic @@ -146,9 +146,9 @@ 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); + 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/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/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/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 55fa8430d22..cc605d79933 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,9 +414,12 @@ 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)) + { + my_free(crypt_buf); return 1; + } /* 3 - copy tail */ memcpy(dst + block_size - tail, src + block_size - tail, tail); /* 4 - store key version */ @@ -444,19 +447,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 +474,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; 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; 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); 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 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); - - uint cnt = srv_n_fil_crypt_threads; - srv_n_fil_crypt_threads = 0; - fil_crypt_set_thread_cnt(cnt); - fil_crypt_threads_inited = true; + 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_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/include/fil0fil.ic b/storage/xtradb/include/fil0fil.ic index ec3b7e85e91..7ccc69b9561 100644 --- a/storage/xtradb/include/fil0fil.ic +++ b/storage/xtradb/include/fil0fil.ic @@ -147,9 +147,9 @@ 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); + 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/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); } diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 91efdc9ef35..9f62666f97a 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -422,6 +422,55 @@ 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); + + /* 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); + + /* 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); + + /* 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); + + /* 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); + + /* 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); 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") |