From d1dc70675c000fc5c6b38ce4456fd2faf56abaf0 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 4 Sep 2024 10:10:04 +1000 Subject: [PATCH 01/16] MDEV-34864 SHOW INDEX FROM - SEQ_IN_INDEX to ULong MySQL-Connector-Net casts SEQ_IN_INDEX to uint and will raise an exception if the type is a System.Int64. As we don't support a huge number of multi-columns in an index reducing to a uint is sufficient to represent all values and maintain compatibility with MySQL-Connector-Net. This matches the type (uint) returned by MySQL-8.3 and 8.0. Reviewer: Alexander Barkov --- mysql-test/main/show_check.result | 6 +++--- mysql-test/suite/funcs_1/r/is_columns_is.result | 4 ++-- mysql-test/suite/funcs_1/r/is_columns_is_embedded.result | 4 ++-- mysql-test/suite/funcs_1/r/is_statistics.result | 6 +++--- sql/sql_show.cc | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mysql-test/main/show_check.result b/mysql-test/main/show_check.result index 7c0a3251e45..ecbaa8af774 100644 --- a/mysql-test/main/show_check.result +++ b/mysql-test/main/show_check.result @@ -66,7 +66,7 @@ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is def information_schema STATISTICS STATISTICS TABLE_NAME Table 253 64 2 N 4097 0 8 def information_schema STATISTICS STATISTICS NON_UNIQUE Non_unique 8 1 1 N 36865 0 63 def information_schema STATISTICS STATISTICS INDEX_NAME Key_name 253 64 7 N 4097 0 8 -def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 8 2 1 N 36865 0 63 +def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 3 2 1 N 36897 0 63 def information_schema STATISTICS STATISTICS COLUMN_NAME Column_name 253 64 1 N 4097 0 8 def information_schema STATISTICS STATISTICS COLLATION Collation 253 1 1 Y 4096 0 8 def information_schema STATISTICS STATISTICS CARDINALITY Cardinality 8 21 1 Y 36864 0 63 @@ -650,7 +650,7 @@ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is def information_schema STATISTICS STATISTICS TABLE_NAME Table 253 64 2 N 4097 0 63 def information_schema STATISTICS STATISTICS NON_UNIQUE Non_unique 8 1 1 N 36865 0 63 def information_schema STATISTICS STATISTICS INDEX_NAME Key_name 253 64 7 N 4097 0 63 -def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 8 2 1 N 36865 0 63 +def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 3 2 1 N 36897 0 63 def information_schema STATISTICS STATISTICS COLUMN_NAME Column_name 253 64 6 N 4097 0 63 def information_schema STATISTICS STATISTICS COLLATION Collation 253 1 1 Y 4096 0 63 def information_schema STATISTICS STATISTICS CARDINALITY Cardinality 8 21 1 Y 36864 0 63 @@ -920,7 +920,7 @@ Catalog Database Table Table_alias Column Column_alias Type Length Max length Is def information_schema STATISTICS STATISTICS TABLE_NAME Table 253 192 2 N 4097 0 33 def information_schema STATISTICS STATISTICS NON_UNIQUE Non_unique 8 1 1 N 36865 0 63 def information_schema STATISTICS STATISTICS INDEX_NAME Key_name 253 192 7 N 4097 0 33 -def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 8 2 1 N 36865 0 63 +def information_schema STATISTICS STATISTICS SEQ_IN_INDEX Seq_in_index 3 2 1 N 36897 0 63 def information_schema STATISTICS STATISTICS COLUMN_NAME Column_name 253 192 1 N 4097 0 33 def information_schema STATISTICS STATISTICS COLLATION Collation 253 3 1 Y 4096 0 33 def information_schema STATISTICS STATISTICS CARDINALITY Cardinality 8 21 1 Y 36864 0 63 diff --git a/mysql-test/suite/funcs_1/r/is_columns_is.result b/mysql-test/suite/funcs_1/r/is_columns_is.result index 0758acc66a9..4f739e054ba 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_is.result +++ b/mysql-test/suite/funcs_1/r/is_columns_is.result @@ -356,7 +356,7 @@ def information_schema STATISTICS INDEX_TYPE 14 NULL NO varchar 16 48 NULL NULL def information_schema STATISTICS NON_UNIQUE 4 NULL NO bigint NULL NULL 19 0 NULL NULL NULL bigint(1) select NEVER NULL def information_schema STATISTICS NULLABLE 13 NULL NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select NEVER NULL def information_schema STATISTICS PACKED 12 NULL YES varchar 10 30 NULL NULL NULL utf8 utf8_general_ci varchar(10) select NEVER NULL -def information_schema STATISTICS SEQ_IN_INDEX 7 NULL NO bigint NULL NULL 19 0 NULL NULL NULL bigint(2) select NEVER NULL +def information_schema STATISTICS SEQ_IN_INDEX 7 NULL NO int NULL NULL 10 0 NULL NULL NULL int(2) unsigned select NEVER NULL def information_schema STATISTICS SUB_PART 11 NULL YES bigint NULL NULL 19 0 NULL NULL NULL bigint(3) select NEVER NULL def information_schema STATISTICS TABLE_CATALOG 1 NULL NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select NEVER NULL def information_schema STATISTICS TABLE_NAME 3 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NEVER NULL @@ -898,7 +898,7 @@ NULL information_schema SPATIAL_REF_SYS AUTH_SRID int NULL NULL NULL NULL int(5) NULL information_schema STATISTICS NON_UNIQUE bigint NULL NULL NULL NULL bigint(1) 3.0000 information_schema STATISTICS INDEX_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema STATISTICS INDEX_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) -NULL information_schema STATISTICS SEQ_IN_INDEX bigint NULL NULL NULL NULL bigint(2) +NULL information_schema STATISTICS SEQ_IN_INDEX int NULL NULL NULL NULL int(2) unsigned 3.0000 information_schema STATISTICS COLUMN_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema STATISTICS COLLATION varchar 1 3 utf8 utf8_general_ci varchar(1) NULL information_schema STATISTICS CARDINALITY bigint NULL NULL NULL NULL bigint(21) diff --git a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result index a759b66d106..da9e42d6e09 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result +++ b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result @@ -356,7 +356,7 @@ def information_schema STATISTICS INDEX_TYPE 14 NULL NO varchar 16 48 NULL NULL def information_schema STATISTICS NON_UNIQUE 4 NULL NO bigint NULL NULL 19 0 NULL NULL NULL bigint(1) NEVER NULL def information_schema STATISTICS NULLABLE 13 NULL NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) NEVER NULL def information_schema STATISTICS PACKED 12 NULL YES varchar 10 30 NULL NULL NULL utf8 utf8_general_ci varchar(10) NEVER NULL -def information_schema STATISTICS SEQ_IN_INDEX 7 NULL NO bigint NULL NULL 19 0 NULL NULL NULL bigint(2) NEVER NULL +def information_schema STATISTICS SEQ_IN_INDEX 7 NULL NO int NULL NULL 10 0 NULL NULL NULL int(2) unsigned NEVER NULL def information_schema STATISTICS SUB_PART 11 NULL YES bigint NULL NULL 19 0 NULL NULL NULL bigint(3) NEVER NULL def information_schema STATISTICS TABLE_CATALOG 1 NULL NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) NEVER NULL def information_schema STATISTICS TABLE_NAME 3 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) NEVER NULL @@ -898,7 +898,7 @@ NULL information_schema SPATIAL_REF_SYS AUTH_SRID int NULL NULL NULL NULL int(5) NULL information_schema STATISTICS NON_UNIQUE bigint NULL NULL NULL NULL bigint(1) 3.0000 information_schema STATISTICS INDEX_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema STATISTICS INDEX_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) -NULL information_schema STATISTICS SEQ_IN_INDEX bigint NULL NULL NULL NULL bigint(2) +NULL information_schema STATISTICS SEQ_IN_INDEX int NULL NULL NULL NULL int(2) unsigned 3.0000 information_schema STATISTICS COLUMN_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema STATISTICS COLLATION varchar 1 3 utf8 utf8_general_ci varchar(1) NULL information_schema STATISTICS CARDINALITY bigint NULL NULL NULL NULL bigint(21) diff --git a/mysql-test/suite/funcs_1/r/is_statistics.result b/mysql-test/suite/funcs_1/r/is_statistics.result index 3d5ed9c3674..ae1b7db30bb 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics.result +++ b/mysql-test/suite/funcs_1/r/is_statistics.result @@ -34,7 +34,7 @@ TABLE_NAME varchar(64) NO NULL NON_UNIQUE bigint(1) NO NULL INDEX_SCHEMA varchar(64) NO NULL INDEX_NAME varchar(64) NO NULL -SEQ_IN_INDEX bigint(2) NO NULL +SEQ_IN_INDEX int(2) unsigned NO NULL COLUMN_NAME varchar(64) NO NULL COLLATION varchar(1) YES NULL CARDINALITY bigint(21) YES NULL @@ -53,7 +53,7 @@ STATISTICS CREATE TEMPORARY TABLE `STATISTICS` ( `NON_UNIQUE` bigint(1) NOT NULL, `INDEX_SCHEMA` varchar(64) NOT NULL, `INDEX_NAME` varchar(64) NOT NULL, - `SEQ_IN_INDEX` bigint(2) NOT NULL, + `SEQ_IN_INDEX` int(2) unsigned NOT NULL, `COLUMN_NAME` varchar(64) NOT NULL, `COLLATION` varchar(1), `CARDINALITY` bigint(21), @@ -72,7 +72,7 @@ TABLE_NAME varchar(64) NO NULL NON_UNIQUE bigint(1) NO NULL INDEX_SCHEMA varchar(64) NO NULL INDEX_NAME varchar(64) NO NULL -SEQ_IN_INDEX bigint(2) NO NULL +SEQ_IN_INDEX int(2) unsigned NO NULL COLUMN_NAME varchar(64) NO NULL COLLATION varchar(1) YES NULL CARDINALITY bigint(21) YES NULL diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ed32d298d19..9057ff8712f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -9392,7 +9392,7 @@ ST_FIELD_INFO stat_fields_info[]= Column("NON_UNIQUE", SLonglong(1),NOT_NULL, "Non_unique", OPEN_FRM_ONLY), Column("INDEX_SCHEMA", Name(), NOT_NULL, OPEN_FRM_ONLY), Column("INDEX_NAME", Name(), NOT_NULL, "Key_name", OPEN_FRM_ONLY), - Column("SEQ_IN_INDEX", SLonglong(2),NOT_NULL, "Seq_in_index",OPEN_FRM_ONLY), + Column("SEQ_IN_INDEX", ULong(2), NOT_NULL, "Seq_in_index",OPEN_FRM_ONLY), Column("COLUMN_NAME", Name(), NOT_NULL, "Column_name", OPEN_FRM_ONLY), Column("COLLATION", Varchar(1), NULLABLE, "Collation", OPEN_FULL_TABLE), Column("CARDINALITY", SLonglong(), NULLABLE, "Cardinality", OPEN_FULL_TABLE), From c991efd9c35eb3f2a90b2562071860a10a4cbaa2 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 28 Aug 2024 13:34:53 +1000 Subject: [PATCH 02/16] MDEV-34825 FreeBSD fails to build under clang natively clang doesn't have /usr/local/lib in the path. As such there are various depedency linkages that will fail. For example pcre and libfmt.` --- cmake/os/FreeBSD.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/os/FreeBSD.cmake b/cmake/os/FreeBSD.cmake index e1c764692bc..9eccdd90c34 100644 --- a/cmake/os/FreeBSD.cmake +++ b/cmake/os/FreeBSD.cmake @@ -28,3 +28,6 @@ SET(EXECINFO_ROOT /usr/local CACHE INTERNAL "Where to find execinfo library and INCLUDE_DIRECTORIES(${EXECINFO_ROOT}/include) SET(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${EXECINFO_ROOT}/include) SET(ENV{LIB} "$ENV{LIB}:${EXECINFO_ROOT}/lib") + +# For all userspace dependencies +LINK_DIRECTORIES(/usr/local/lib) From 2e23c7342ff724b6eb82fd020e0bcdf312ae3209 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 28 Aug 2024 14:32:37 +1000 Subject: [PATCH 03/16] MDEV-34567 unit.my_apc always failing on FreeBSD-14 Without the call to my_mutex_init, the mutex attributes my_fast_mutexattr and my_errorcheck_mutexattr are uninitialized. Linux tolerates this but FreeBSD doesn't (and segfaults). We fix for all since the unit text should be testing the standard mutexes of the system. --- unittest/sql/my_apc-t.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/unittest/sql/my_apc-t.cc b/unittest/sql/my_apc-t.cc index f1a28225f41..8fb9181377a 100644 --- a/unittest/sql/my_apc-t.cc +++ b/unittest/sql/my_apc-t.cc @@ -190,6 +190,7 @@ int main(int args, char **argv) pthread_t request_thr[N_THREADS]; int i; + my_mutex_init(); my_thread_global_init(); mysql_mutex_init(0, &apc_counters_mutex, MY_MUTEX_INIT_FAST); From b2ebe1cb7bde32945af67512a76ef7d980f8d1a2 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 12 Jan 2024 16:57:37 +0100 Subject: [PATCH 04/16] MDEV-33091 pcre2 headers aren't found on Solaris use pkg-config to find pcre2, if possible rename PCRE_INCLUDES to use PKG_CHECK_MODULES naming, PCRE_INCLUDE_DIRS --- client/CMakeLists.txt | 2 +- cmake/pcre.cmake | 34 +++++++++++++--------- cmake/plugin.cmake | 2 +- extra/mariabackup/CMakeLists.txt | 2 +- libmysqld/CMakeLists.txt | 2 +- libmysqld/examples/CMakeLists.txt | 2 +- plugin/feedback/CMakeLists.txt | 2 +- plugin/qc_info/CMakeLists.txt | 2 +- sql/CMakeLists.txt | 2 +- storage/perfschema/CMakeLists.txt | 2 +- storage/perfschema/unittest/CMakeLists.txt | 2 +- unittest/embedded/CMakeLists.txt | 2 +- 12 files changed, 32 insertions(+), 24 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 3ad788f8023..a8e80bc8b5b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -16,7 +16,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/mysys_ssl ${ZLIB_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS} diff --git a/cmake/pcre.cmake b/cmake/pcre.cmake index 89f928f684a..4873051170e 100644 --- a/cmake/pcre.cmake +++ b/cmake/pcre.cmake @@ -1,4 +1,3 @@ -INCLUDE (CheckCSourceRuns) INCLUDE (ExternalProject) SET(WITH_PCRE "auto" CACHE STRING @@ -6,7 +5,8 @@ SET(WITH_PCRE "auto" CACHE STRING MACRO(BUNDLE_PCRE2) SET(dir "${CMAKE_BINARY_DIR}/extra/pcre2") - SET(PCRE_INCLUDES ${dir}/src/pcre2-build ${dir}/src/pcre2/src) + SET(PCRE_INCLUDE_DIRS ${dir}/src/pcre2-build ${dir}/src/pcre2/src) + MESSAGE(STATUS "Will download and bundle pcre2") SET(byproducts) FOREACH(lib pcre2-posix pcre2-8) ADD_LIBRARY(${lib} STATIC IMPORTED GLOBAL) @@ -76,18 +76,26 @@ SET_TARGET_PROPERTIES(pcre2 PROPERTIES EXCLUDE_FROM_ALL TRUE) ENDMACRO() MACRO (CHECK_PCRE) - IF(WITH_PCRE STREQUAL "system" OR WITH_PCRE STREQUAL "auto") - CHECK_LIBRARY_EXISTS(pcre2-8 pcre2_match_8 "" HAVE_PCRE2) - ENDIF() - IF(NOT HAVE_PCRE2 OR WITH_PCRE STREQUAL "bundled") - IF (WITH_PCRE STREQUAL "system") - MESSAGE(FATAL_ERROR "system pcre2-8 library is not found or unusable") + IF (NOT TARGET pcre2 AND NOT PCRE_FOUND) + IF(WITH_PCRE STREQUAL "system" OR WITH_PCRE STREQUAL "auto") + FIND_PACKAGE(PkgConfig QUIET) + PKG_CHECK_MODULES(PCRE libpcre2-8) + # in case pkg-config or libpcre2-8.pc is not installed: + IF(NOT PCRE_FOUND) + UNSET(PCRE_FOUND CACHE) + CHECK_LIBRARY_EXISTS(pcre2-8 pcre2_match_8 "" PCRE_FOUND) + ENDIF() ENDIF() - BUNDLE_PCRE2() - ELSE() - CHECK_LIBRARY_EXISTS(pcre2-posix PCRE2regcomp "" NEEDS_PCRE2_DEBIAN_HACK) - IF(NEEDS_PCRE2_DEBIAN_HACK) - SET(PCRE2_DEBIAN_HACK "-Dregcomp=PCRE2regcomp -Dregexec=PCRE2regexec -Dregerror=PCRE2regerror -Dregfree=PCRE2regfree") + IF(NOT PCRE_FOUND OR WITH_PCRE STREQUAL "bundled") + IF (WITH_PCRE STREQUAL "system") + MESSAGE(FATAL_ERROR "system pcre2-8 library is not found or unusable") + ENDIF() + BUNDLE_PCRE2() + ELSE() + CHECK_LIBRARY_EXISTS(pcre2-posix PCRE2regcomp "" NEEDS_PCRE2_DEBIAN_HACK) + IF(NEEDS_PCRE2_DEBIAN_HACK) + SET(PCRE2_DEBIAN_HACK "-Dregcomp=PCRE2regcomp -Dregexec=PCRE2regexec -Dregerror=PCRE2regerror -Dregfree=PCRE2regfree") + ENDIF() ENDIF() ENDIF() ENDMACRO() diff --git a/cmake/plugin.cmake b/cmake/plugin.cmake index 7fd3b6294b9..378fc5f5f1c 100644 --- a/cmake/plugin.cmake +++ b/cmake/plugin.cmake @@ -44,7 +44,7 @@ MACRO(MYSQL_ADD_PLUGIN) # Add common include directories INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) diff --git a/extra/mariabackup/CMakeLists.txt b/extra/mariabackup/CMakeLists.txt index 6a83ba0238a..8fcc8b2af54 100644 --- a/extra/mariabackup/CMakeLists.txt +++ b/extra/mariabackup/CMakeLists.txt @@ -36,7 +36,7 @@ INCLUDE_DIRECTORIES( ) IF(NOT HAVE_SYSTEM_REGEX) - INCLUDE_DIRECTORIES(${PCRE_INCLUDES}) + INCLUDE_DIRECTORIES(${PCRE_INCLUDE_DIRS}) ADD_DEFINITIONS(${PCRE2_DEBIAN_HACK}) ENDIF() diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 44115b5c1fc..854c30d33ce 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -23,7 +23,7 @@ ${CMAKE_SOURCE_DIR}/libmysqld ${CMAKE_SOURCE_DIR}/sql ${CMAKE_SOURCE_DIR}/tpool ${CMAKE_BINARY_DIR}/sql -${PCRE_INCLUDES} +${PCRE_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS} ${SSL_INTERNAL_INCLUDE_DIRS} diff --git a/libmysqld/examples/CMakeLists.txt b/libmysqld/examples/CMakeLists.txt index 2a10def8e2e..d6646a128ca 100644 --- a/libmysqld/examples/CMakeLists.txt +++ b/libmysqld/examples/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/libmysqld/include - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/sql ${MY_READLINE_INCLUDE_DIR} ) diff --git a/plugin/feedback/CMakeLists.txt b/plugin/feedback/CMakeLists.txt index 2103250e5a6..fc35cbadc31 100644 --- a/plugin/feedback/CMakeLists.txt +++ b/plugin/feedback/CMakeLists.txt @@ -1,5 +1,5 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS}) SET(FEEDBACK_SOURCES feedback.cc sender_thread.cc diff --git a/plugin/qc_info/CMakeLists.txt b/plugin/qc_info/CMakeLists.txt index b8c5f926cff..329f49c1fc9 100644 --- a/plugin/qc_info/CMakeLists.txt +++ b/plugin/qc_info/CMakeLists.txt @@ -1,4 +1,4 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql - ${PCRE_INCLUDES}) + ${PCRE_INCLUDE_DIRS}) MYSQL_ADD_PLUGIN(QUERY_CACHE_INFO qc_info.cc RECOMPILE_FOR_EMBEDDED) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 8d24a140865..9e735a51433 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -52,7 +52,7 @@ ENDIF() INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql -${PCRE_INCLUDES} +${PCRE_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS} ${CMAKE_BINARY_DIR}/sql diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt index aea4040c408..8bdba6d9c8b 100644 --- a/storage/perfschema/CMakeLists.txt +++ b/storage/perfschema/CMakeLists.txt @@ -24,7 +24,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/sql ${CMAKE_BINARY_DIR}/sql ${CMAKE_CURRENT_BINARY_DIR} - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${SSL_INCLUDE_DIRS}) ADD_DEFINITIONS(-DMYSQL_SERVER) diff --git a/storage/perfschema/unittest/CMakeLists.txt b/storage/perfschema/unittest/CMakeLists.txt index 2a22990f807..600795c78fc 100644 --- a/storage/perfschema/unittest/CMakeLists.txt +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -22,7 +22,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include/mysql - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/sql ${SSL_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/unittest/mytap diff --git a/unittest/embedded/CMakeLists.txt b/unittest/embedded/CMakeLists.txt index cf48550c377..428bb811de6 100644 --- a/unittest/embedded/CMakeLists.txt +++ b/unittest/embedded/CMakeLists.txt @@ -1,7 +1,7 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/libmysqld/include - ${PCRE_INCLUDES} + ${PCRE_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/sql ${MY_READLINE_INCLUDE_DIR} ) From 566c22e8148433e967f3bc5b482c037b8ec44511 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 1 Feb 2024 11:26:36 +0100 Subject: [PATCH 05/16] pcre.cmake: always check the library with check_library_exists() even if pkg-config has it. otherwise build dependencies aren't detected. --- cmake/pcre.cmake | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cmake/pcre.cmake b/cmake/pcre.cmake index 4873051170e..db685c3572d 100644 --- a/cmake/pcre.cmake +++ b/cmake/pcre.cmake @@ -81,18 +81,15 @@ MACRO (CHECK_PCRE) FIND_PACKAGE(PkgConfig QUIET) PKG_CHECK_MODULES(PCRE libpcre2-8) # in case pkg-config or libpcre2-8.pc is not installed: - IF(NOT PCRE_FOUND) - UNSET(PCRE_FOUND CACHE) - CHECK_LIBRARY_EXISTS(pcre2-8 pcre2_match_8 "" PCRE_FOUND) - ENDIF() + CHECK_LIBRARY_EXISTS(pcre2-8 pcre2_match_8 "${PCRE_LIBRARY_DIRS}" HAVE_PCRE2_MATCH_8) ENDIF() - IF(NOT PCRE_FOUND OR WITH_PCRE STREQUAL "bundled") + IF(NOT HAVE_PCRE2_MATCH_8 OR WITH_PCRE STREQUAL "bundled") IF (WITH_PCRE STREQUAL "system") MESSAGE(FATAL_ERROR "system pcre2-8 library is not found or unusable") ENDIF() BUNDLE_PCRE2() ELSE() - CHECK_LIBRARY_EXISTS(pcre2-posix PCRE2regcomp "" NEEDS_PCRE2_DEBIAN_HACK) + CHECK_LIBRARY_EXISTS(pcre2-posix PCRE2regcomp "${PCRE_LIBRARY_DIRS}" NEEDS_PCRE2_DEBIAN_HACK) IF(NEEDS_PCRE2_DEBIAN_HACK) SET(PCRE2_DEBIAN_HACK "-Dregcomp=PCRE2regcomp -Dregexec=PCRE2regexec -Dregerror=PCRE2regerror -Dregfree=PCRE2regfree") ENDIF() From 7b2b03c4f27959b64618aa3ff3cd308a7d2c877c Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Wed, 28 Aug 2024 18:37:49 +1000 Subject: [PATCH 06/16] MDEV-34825 FreeBSD fails to build under clang natively Upstream the patch from: https://github.com/freebsd/freebsd-ports/blob/15d22e1c70da81aaa5751ad0d82f92e9451c4d81/databases/mariadb106-server/files/patch-mysys_crc32_crc32c.cc --- mysys/crc32/crc32c.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysys/crc32/crc32c.cc b/mysys/crc32/crc32c.cc index 6de7af70db7..0d65d0027d4 100644 --- a/mysys/crc32/crc32c.cc +++ b/mysys/crc32/crc32c.cc @@ -455,7 +455,7 @@ static int arch_ppc_probe(void) { return arch_ppc_crc32; } -# elif defined __FreeBSD_version && __FreeBSD_version >= 1200000 +# elif defined __FreeBSD__ # include # include # include From e9b70e59a3946b47ee0812c029221cb246675cf0 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj Date: Wed, 28 Aug 2024 18:40:52 +1000 Subject: [PATCH 07/16] MDEV-34825 FreeBSD - upstream riscv64 compatibility patch From https://github.com/freebsd/freebsd-ports/blob/15d22e1c70da81aaa5751ad0d82f92e9451c4d81/databases/mariadb106-server/files/patch-sql_mysqld.cc --- sql/mysqld.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e5bc8bde3d6..d874a115bce 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -208,7 +208,7 @@ typedef fp_except fp_except_t; inline void setup_fpu() { -#if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H) && !defined(HAVE_FEDISABLEEXCEPT) +#if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H) && !defined(HAVE_FEDISABLEEXCEPT) && defined(FP_X_INV) /* We can't handle floating point exceptions with threads, so disable this on freebsd Don't fall for overflow, underflow,divide-by-zero or loss of precision. @@ -221,7 +221,7 @@ inline void setup_fpu() fpsetmask(~(FP_X_INV | FP_X_OFL | FP_X_UFL | FP_X_DZ | FP_X_IMP)); #endif /* FP_X_DNML */ -#endif /* __FreeBSD__ && HAVE_IEEEFP_H && !HAVE_FEDISABLEEXCEPT */ +#endif /* __FreeBSD__ && HAVE_IEEEFP_H && !HAVE_FEDISABLEEXCEPT && FP_X_INV */ #ifdef HAVE_FEDISABLEEXCEPT fedisableexcept(FE_ALL_EXCEPT); From dff354e7df2fa774ce4da77202a17e2cae99ac59 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 28 Aug 2024 18:56:55 +1000 Subject: [PATCH 08/16] MDEV-34825: my_cpu.h - non-glibc ism for POWER Taking both the FreeBSD[1] and Alpine[1] patch concepts; provide non-GLIBC definations for HMT_*. Provide FreeBSD ASM base for __ppc_get_timebase. On alternately use __builtin_ppc_get_timebase which is described on https://gcc.gnu.org/onlinedocs/gcc/Basic-PowerPC-Built-in-Functions-Available-on-all-Configurations.html an not depended on glibc/musl. [1] https://github.com/freebsd/freebsd-ports/blob/15d22e1c70da81aaa5751ad0d82f92e9451c4d81/databases/mariadb106-server/files/patch-include_my__cpu.h [2] https://gitlab.alpinelinux.org/alpine/aports/-/blob/master/main/mariadb/ppc-remove-glibc-dep.patch --- include/my_cpu.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/include/my_cpu.h b/include/my_cpu.h index 05fa937c98d..1f562dc700c 100644 --- a/include/my_cpu.h +++ b/include/my_cpu.h @@ -24,6 +24,7 @@ */ #ifdef _ARCH_PWR8 +#ifdef __GLIBC__ #include /* Very low priority */ #define HMT_very_low() __ppc_set_ppr_very_low() @@ -37,6 +38,18 @@ #define HMT_medium_high() __ppc_set_ppr_med_high() /* High priority */ #define HMT_high() asm volatile("or 3,3,3") +#else /* GLIBC */ +#if defined(__FreeBSD__) +#include +#include +#endif +#define HMT_very_low() __asm__ volatile ("or 31,31,31") +#define HMT_low() __asm__ volatile ("or 1,1,1") +#define HMT_medium_low() __asm__ volatile ("or 6,6,6") +#define HMT_medium() __asm__ volatile ("or 2,2,2") +#define HMT_medium_high() __asm__ volatile ("or 5,5,5") +#define HMT_high() asm volatile("or 3,3,3") +#endif /* GLIBC */ #else #define HMT_very_low() #define HMT_low() @@ -81,7 +94,13 @@ static inline void MY_RELAX_CPU(void) __asm__ __volatile__ ("pause"); #endif #elif defined(_ARCH_PWR8) - __ppc_get_timebase(); +#ifdef __FreeBSD__ + uint64_t __tb; + __asm__ volatile ("mfspr %0, 268" : "=r" (__tb)); +#else + /* Changed from __ppc_get_timebase for musl compatibility */ + __builtin_ppc_get_timebase(); +#endif #elif defined __GNUC__ && (defined __arm__ || defined __aarch64__) /* Mainly, prevent the compiler from optimizing away delay loops */ #ifdef _aarch64_ From 8024b8e4c19ff08793382278b3fa36c263799d9b Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Thu, 5 Sep 2024 11:22:40 +1000 Subject: [PATCH 09/16] MDEV-33091 pcre2 headers - handle columnstore From e735cf2ed7cefb2af36f10f3cb47dfc060789df3, the PCRE_INCLUDES changed to PCRE_INCLUDE_DIRS for consistency. The columnstore module depends on the old name. Create a mapping for the columnstore submodule. 10.6+ fix for submodule is: * https://github.com/mariadb-corporation/mariadb-columnstore-engine/pull/3304 --- storage/columnstore/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/columnstore/CMakeLists.txt b/storage/columnstore/CMakeLists.txt index 7c875251214..2b6d8b7d3a5 100644 --- a/storage/columnstore/CMakeLists.txt +++ b/storage/columnstore/CMakeLists.txt @@ -21,6 +21,7 @@ CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") return() ENDIF() + SET(PCRE_INCLUDES "${PCRE_INCLUDE_DIRS}") add_subdirectory(columnstore) IF(TARGET columnstore) From 2c3e07df473e26807f097e7a5905f004ef53e890 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 6 Sep 2024 11:34:31 +1000 Subject: [PATCH 10/16] MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1 The memory leak happened on second execution of a prepared statement that runs UPDATE statement with correlated subquery in right hand side of the SET clause. In this case, invocation of the method table->stat_records() could return the zero value that results in going into the 'if' branch that handles impossible where condition. The issue is that this condition branch missed saving of leaf tables that has to be performed as first condition optimization activity. Later the PS statement memory root is marked as read only on finishing first time execution of the prepared statement. Next time the same statement is executed it hits the assertion on attempt to allocate a memory on the PS memory root marked as read only. This memory allocation takes place by the sequence of the following invocations: Prepared_statement::execute mysql_execute_command Sql_cmd_dml::execute Sql_cmd_update::execute_inner Sql_cmd_update::update_single_table st_select_lex::save_leaf_tables List::push_back To fix the issue, add the flag SELECT_LEX::leaf_tables_saved to control whether the method SELECT_LEX::save_leaf_tables() has to be called or it has been already invoked and no more invocation required. Similar issue could take place on running the DELETE statement with the LIMIT clause in PS/SP mode. The reason of memory leak is the same as for UPDATE case and be fixed in the same way. --- mysql-test/main/ps_mem_leaks.result | 30 ++++++++++++++++++++++++++ mysql-test/main/ps_mem_leaks.test | 33 +++++++++++++++++++++++++++++ sql/sql_delete.cc | 11 ++++++++-- sql/sql_insert.cc | 4 ++-- sql/sql_lex.cc | 1 + sql/sql_lex.h | 4 ++++ sql/sql_select.cc | 4 +++- sql/sql_update.cc | 11 ++++++++-- 8 files changed, 91 insertions(+), 7 deletions(-) diff --git a/mysql-test/main/ps_mem_leaks.result b/mysql-test/main/ps_mem_leaks.result index 2ddf47a992c..309433e1eb7 100644 --- a/mysql-test/main/ps_mem_leaks.result +++ b/mysql-test/main/ps_mem_leaks.result @@ -89,3 +89,33 @@ f DEALLOCATE PREPARE stmt; DROP TABLE t1; # End of 10.4 tests +# +# MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1 +# +CREATE TABLE t1 (id INT, value INT); +CREATE TABLE t2 (id INT); +PREPARE stmt FROM 'UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)'; +EXECUTE stmt; +INSERT INTO t1 VALUES (1,10),(2,10),(3,10); +INSERT INTO t2 VALUES (1),(2); +EXECUTE stmt; +SELECT * FROM t1; +id value +1 1 +2 1 +3 NULL +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2; +# Memory leak also could take place on running the DELETE statement +# with the LIMIT clause. Check it. +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1), (2), (3); +CREATE PROCEDURE p1(p1 INT) +DELETE FROM t1 LIMIT p1; +CALL p1(0); +CALL p1(1); +CALL p1(2); +# Clean up +DROP TABLE t1; +DROP PROCEDURE p1; +# End of 10.5 tests diff --git a/mysql-test/main/ps_mem_leaks.test b/mysql-test/main/ps_mem_leaks.test index dacb4ecabba..c0404687eb7 100644 --- a/mysql-test/main/ps_mem_leaks.test +++ b/mysql-test/main/ps_mem_leaks.test @@ -110,3 +110,36 @@ DEALLOCATE PREPARE stmt; DROP TABLE t1; --echo # End of 10.4 tests + +--echo # +--echo # MDEV-34447: Memory leakage is detected on running the test main.ps against the server 11.1 +--echo # +CREATE TABLE t1 (id INT, value INT); +CREATE TABLE t2 (id INT); + +PREPARE stmt FROM 'UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)'; +EXECUTE stmt; +INSERT INTO t1 VALUES (1,10),(2,10),(3,10); +INSERT INTO t2 VALUES (1),(2); +EXECUTE stmt; +SELECT * FROM t1; +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2; + +--echo # Memory leak also could take place on running the DELETE statement +--echo # with the LIMIT clause. Check it. +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1), (2), (3); + +CREATE PROCEDURE p1(p1 INT) + DELETE FROM t1 LIMIT p1; + +CALL p1(0); +CALL p1(1); +CALL p1(2); + +--echo # Clean up +DROP TABLE t1; +DROP PROCEDURE p1; + +--echo # End of 10.5 tests diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 99da79c599b..ea6b5e6ff66 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -530,6 +530,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, */ if (unlikely(thd->is_error())) DBUG_RETURN(TRUE); + + if (!thd->lex->current_select->leaf_tables_saved) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->leaf_tables_saved= true; + } + my_ok(thd, 0); DBUG_RETURN(0); // Nothing to delete } @@ -902,10 +909,10 @@ cleanup: query_cache_invalidate3(thd, table_list, 1); } - if (thd->lex->current_select->first_cond_optimization) + if (!thd->lex->current_select->leaf_tables_saved) { thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; + thd->lex->current_select->leaf_tables_saved= true; } delete deltempfile; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index cf3ec929d09..46486e09a8b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1373,10 +1373,10 @@ values_loop_end: ::my_ok(thd, info.copied + info.deleted + updated, id, buff); } thd->abort_on_warning= 0; - if (thd->lex->current_select->first_cond_optimization) + if (!thd->lex->current_select->leaf_tables_saved) { thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; + thd->lex->current_select->leaf_tables_saved= true; } my_free(readbuff); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 2dc5d119fd5..fc39bd858f8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3023,6 +3023,7 @@ void st_select_lex::init_query() olap= UNSPECIFIED_OLAP_TYPE; having_fix_field= 0; having_fix_field_for_pushed_cond= 0; + leaf_tables_saved= false; context.select_lex= this; context.init(); cond_count= between_count= with_wild= 0; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0a88e92d723..e0cb336de5a 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1182,6 +1182,10 @@ public: */ bool have_merged_subqueries; + /** + Flag to guard against double initialization of leaf tables list + */ + bool leaf_tables_saved; List leaf_tables; List leaf_tables_exec; List leaf_tables_prep; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 93b53699c9a..ea690422eac 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2054,12 +2054,14 @@ JOIN::optimize_inner() /* Convert all outer joins to inner joins if possible */ conds= simplify_joins(this, join_list, conds, TRUE, FALSE); - if (thd->is_error() || select_lex->save_leaf_tables(thd)) + if (thd->is_error() || + (!select_lex->leaf_tables_saved && select_lex->save_leaf_tables(thd))) { if (arena) thd->restore_active_arena(arena, &backup); DBUG_RETURN(1); } + select_lex->leaf_tables_saved= true; build_bitmap_for_nested_joins(join_list, 0); sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; diff --git a/sql/sql_update.cc b/sql/sql_update.cc index adb209b7b56..c2868d8165e 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -598,6 +598,13 @@ int mysql_update(THD *thd, { DBUG_RETURN(1); // Error in where } + + if (!thd->lex->current_select->leaf_tables_saved) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->leaf_tables_saved= true; + } + my_ok(thd); // No matching records DBUG_RETURN(0); } @@ -1366,10 +1373,10 @@ update_end: } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ thd->abort_on_warning= 0; - if (thd->lex->current_select->first_cond_optimization) + if (!thd->lex->current_select->leaf_tables_saved) { thd->lex->current_select->save_leaf_tables(thd); - thd->lex->current_select->first_cond_optimization= 0; + thd->lex->current_select->leaf_tables_saved= true; } *found_return= found; *updated_return= updated; From 00cb3440858dd8462e0a8f847ae486d363fdb2d1 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 16 Aug 2024 17:04:23 +1000 Subject: [PATCH 11/16] MDEV-33858 Assertion `(mem_root->flags & 4) == 0' fails on 2nd execution of PS with -DWITH_PROTECT_STATEMENT_MEMROOT=ON Simply adding tests as the bug is fixed with a backport of MDEV-34447 --- mysql-test/main/ps_mem_leaks.result | 9 +++++++++ mysql-test/main/ps_mem_leaks.test | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/mysql-test/main/ps_mem_leaks.result b/mysql-test/main/ps_mem_leaks.result index 309433e1eb7..da7fe3a3344 100644 --- a/mysql-test/main/ps_mem_leaks.result +++ b/mysql-test/main/ps_mem_leaks.result @@ -118,4 +118,13 @@ CALL p1(2); # Clean up DROP TABLE t1; DROP PROCEDURE p1; +# +# MDEV-33858: Assertion `(mem_root->flags & 4) == 0' fails on 2nd execution of PS with -DWITH_PROTECT_STATEMENT_MEMROOT=ON +# +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); +PREPARE stmt FROM "UPDATE t SET a = 0 LIMIT ?"; +EXECUTE stmt USING 0; +EXECUTE stmt USING 1; +DROP TABLE t; # End of 10.5 tests diff --git a/mysql-test/main/ps_mem_leaks.test b/mysql-test/main/ps_mem_leaks.test index c0404687eb7..1358ef7613e 100644 --- a/mysql-test/main/ps_mem_leaks.test +++ b/mysql-test/main/ps_mem_leaks.test @@ -142,4 +142,17 @@ CALL p1(2); DROP TABLE t1; DROP PROCEDURE p1; +--echo # +--echo # MDEV-33858: Assertion `(mem_root->flags & 4) == 0' fails on 2nd execution of PS with -DWITH_PROTECT_STATEMENT_MEMROOT=ON +--echo # + +CREATE TABLE t (a INT); +INSERT INTO t VALUES (1),(2); # Optional, fails either way +PREPARE stmt FROM "UPDATE t SET a = 0 LIMIT ?"; +EXECUTE stmt USING 0; +EXECUTE stmt USING 1; + +# CLeanup +DROP TABLE t; + --echo # End of 10.5 tests From e886c2ba02ac021c648f84aa8f910af4fb4fb4bb Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Wed, 21 Aug 2024 15:47:37 +1000 Subject: [PATCH 12/16] MDEV-34757 Check leaf_tables_saved in partition pruning in UPDATE and DELETE --- mysql-test/main/ps_mem_leaks.result | 198 ++++++++++++++++++++++++++++ mysql-test/main/ps_mem_leaks.test | 168 +++++++++++++++++++++++ sql/sql_delete.cc | 6 + sql/sql_update.cc | 6 + 4 files changed, 378 insertions(+) diff --git a/mysql-test/main/ps_mem_leaks.result b/mysql-test/main/ps_mem_leaks.result index da7fe3a3344..8acfb779bc5 100644 --- a/mysql-test/main/ps_mem_leaks.result +++ b/mysql-test/main/ps_mem_leaks.result @@ -119,6 +119,204 @@ CALL p1(2); DROP TABLE t1; DROP PROCEDURE p1; # +# MDEV-34757: assertion of (mem_root->flags & 4) == 0 fails in 2nd ps execution with partition pruning +# +CREATE TABLE t1 (id INT, value INT); +CREATE TABLE t2 (id INT); +PREPARE stmt FROM 'EXPLAIN UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)'; +EXECUTE stmt; +INSERT INTO t1 VALUES (1,10),(2,10),(3,10); +INSERT INTO t2 VALUES (1),(2); +EXECUTE stmt; +SELECT * FROM t1; +id value +1 10 +2 10 +3 10 +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2; +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( +partition p0 values in (null,1,2), +partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'UPDATE t1 t1 SET a = (SELECT 1 FROM t2 WHERE a = t1.a) where a = ?'; +execute stmt using @var1; +select * from t1; +a +1 +2 +3 +4 +execute stmt using @var2; +select * from t1; +a +1 +2 +1 +3 +deallocate prepare stmt; +drop table t1, t2; +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( +partition p0 values in (null,1,2), +partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'EXPLAIN UPDATE t1 t1 SET a = (SELECT 1 FROM t2 WHERE a = t1.a) where a = ?'; +execute stmt using @var1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No matching rows after partition pruning +select * from t1; +a +1 +2 +3 +4 +execute stmt using @var2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where; Using buffer +2 DEPENDENT SUBQUERY t2 system NULL NULL NULL NULL 1 +select * from t1; +a +1 +2 +3 +4 +deallocate prepare stmt; +drop table t1, t2; +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); +PREPARE stmt FROM 'UPDATE t1 t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id) WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); +PREPARE stmt FROM 'EXPLAIN UPDATE t1 t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id) WHERE ?=?'; +execute stmt using @var1, @var2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +execute stmt using @var1, @var1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 3 +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 Using where +deallocate prepare stmt; +DROP TABLE t1,t2; +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1), (2), (3); +CREATE PROCEDURE p1(p1 INT) +EXPLAIN DELETE FROM t1 LIMIT p1; +CALL p1(0); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +CALL p1(1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +CALL p1(2); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +# Clean up +DROP TABLE t1; +DROP PROCEDURE p1; +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( +partition p0 values in (1,2), +partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'DELETE FROM t1 where a = ?'; +execute stmt using @var1; +select * from t1; +a +1 +2 +3 +4 +execute stmt using @var2; +select * from t1; +a +1 +2 +3 +deallocate prepare stmt; +drop table t1, t2; +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( +partition p0 values in (1,2), +partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1 where a = ?'; +execute stmt using @var1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No matching rows after partition pruning +select * from t1; +a +1 +2 +3 +4 +execute stmt using @var2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where +select * from t1; +a +1 +2 +3 +4 +deallocate prepare stmt; +drop table t1, t2; +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); +PREPARE stmt FROM 'DELETE FROM t1 WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1 WHERE ?=?'; +execute stmt using @var1, @var2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +execute stmt using @var1, @var1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL 3 Deleting all rows +deallocate prepare stmt; +DROP TABLE t1,t2; +# # MDEV-33858: Assertion `(mem_root->flags & 4) == 0' fails on 2nd execution of PS with -DWITH_PROTECT_STATEMENT_MEMROOT=ON # CREATE TABLE t (a INT); diff --git a/mysql-test/main/ps_mem_leaks.test b/mysql-test/main/ps_mem_leaks.test index 1358ef7613e..907f0eb6bba 100644 --- a/mysql-test/main/ps_mem_leaks.test +++ b/mysql-test/main/ps_mem_leaks.test @@ -5,6 +5,7 @@ # The cmake option -DWITH_PROTECT_STATEMENT_MEMROOT is used only # for debug build --source include/have_debug.inc +--source include/have_partition.inc --echo # --echo # MDEV-32369: Memory leak when executing PS for query with IN subquery @@ -142,6 +143,173 @@ CALL p1(2); DROP TABLE t1; DROP PROCEDURE p1; +--echo # +--echo # MDEV-34757: assertion of (mem_root->flags & 4) == 0 fails in 2nd ps execution with partition pruning +--echo # +# same as the first MDEV-34444 testcase but with explain +CREATE TABLE t1 (id INT, value INT); +CREATE TABLE t2 (id INT); + +PREPARE stmt FROM 'EXPLAIN UPDATE t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id)'; +# we are testing 2nd ps assertion failure, not explain output, which +# may vary from version to version +--disable_result_log +EXECUTE stmt; +--enable_result_log +INSERT INTO t1 VALUES (1,10),(2,10),(3,10); +INSERT INTO t2 VALUES (1),(2); +--disable_result_log +EXECUTE stmt; +--enable_result_log +SELECT * FROM t1; +DEALLOCATE PREPARE stmt; +DROP TABLE t1, t2; + +# 2nd ps mem leak; partition pruning +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( + partition p0 values in (null,1,2), + partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'UPDATE t1 t1 SET a = (SELECT 1 FROM t2 WHERE a = t1.a) where a = ?'; +execute stmt using @var1; +select * from t1; +execute stmt using @var2; +select * from t1; +deallocate prepare stmt; +drop table t1, t2; + +# same but with explain +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( + partition p0 values in (null,1,2), + partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'EXPLAIN UPDATE t1 t1 SET a = (SELECT 1 FROM t2 WHERE a = t1.a) where a = ?'; +execute stmt using @var1; +select * from t1; +execute stmt using @var2; +select * from t1; +deallocate prepare stmt; +drop table t1, t2; + +# top level impossible where +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); + +PREPARE stmt FROM 'UPDATE t1 t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id) WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; + +# top level impossible where, with explain +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); + +PREPARE stmt FROM 'EXPLAIN UPDATE t1 t1 SET value = (SELECT 1 FROM t2 WHERE id = t1.id) WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; + +# Now we do delete instead of update + +# same as the second MDEV-34447 testcase but with explain +CREATE TABLE t1 (c1 INT); +INSERT INTO t1 (c1) VALUES (1), (2), (3); + +CREATE PROCEDURE p1(p1 INT) + EXPLAIN DELETE FROM t1 LIMIT p1; + +CALL p1(0); +CALL p1(1); +CALL p1(2); + +--echo # Clean up +DROP TABLE t1; +DROP PROCEDURE p1; + +# partition pruning +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( + partition p0 values in (1,2), + partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'DELETE FROM t1 where a = ?'; +execute stmt using @var1; +select * from t1; +execute stmt using @var2; +select * from t1; +deallocate prepare stmt; +drop table t1, t2; + +# same but with explain +set @var1=5; +set @var2=4; +create table t1 (a int) partition by list(a) ( + partition p0 values in (1,2), + partition p1 values in (3,4) +); +create table t2 (a int); +insert into t1 values (1),(2),(3),(4); +insert into t2 values (4); +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1 where a = ?'; +execute stmt using @var1; +select * from t1; +execute stmt using @var2; +select * from t1; +deallocate prepare stmt; +drop table t1, t2; + +# top level impossible where +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); + +PREPARE stmt FROM 'DELETE FROM t1 WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; + +# top level impossible where, with explain +set @var1=1; +set @var2=2; +CREATE TABLE t1 ( id INT(10), value INT(10) ); +CREATE TABLE t2 ( id INT(10) ); +insert into t1 values (1,10),(2,10),(3,10); +insert into t2 values (1),(2); + +PREPARE stmt FROM 'EXPLAIN DELETE FROM t1 WHERE ?=?'; +execute stmt using @var1, @var2; +execute stmt using @var1, @var1; +deallocate prepare stmt; +DROP TABLE t1,t2; + --echo # --echo # MDEV-33858: Assertion `(mem_root->flags & 4) == 0' fails on 2nd execution of PS with -DWITH_PROTECT_STATEMENT_MEMROOT=ON --echo # diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index ea6b5e6ff66..67d07c3ff44 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -500,6 +500,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (thd->lex->describe || thd->lex->analyze_stmt) goto produce_explain_and_leave; + if (!thd->lex->current_select->leaf_tables_saved) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->leaf_tables_saved= true; + } + my_ok(thd, 0); DBUG_RETURN(0); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index c2868d8165e..e96af1a19bf 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -569,6 +569,12 @@ int mysql_update(THD *thd, if (thd->is_error()) DBUG_RETURN(1); + if (!thd->lex->current_select->leaf_tables_saved) + { + thd->lex->current_select->save_leaf_tables(thd); + thd->lex->current_select->leaf_tables_saved= true; + } + my_ok(thd); // No matching records DBUG_RETURN(0); } From f0b2e76577a1b94d28671a3b6c53f08bce704821 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 6 Sep 2024 14:58:45 +0300 Subject: [PATCH 13/16] Removed ctrl-l from the source --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 97193e3e47d..b185c62aea6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5242,7 +5242,7 @@ static bool get_quick_record_count(THD *thd, SQL_SELECT *select, if (unlikely(check_stack_overrun(thd, STACK_MIN_SIZE, buff))) DBUG_RETURN(false); // Fatal error flag is set if (select) - { + { select->head=table; table->reginfo.impossible_range=0; /* From 886d740ad77013d6d080c2907b632c817c9784c9 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 6 Sep 2024 15:04:36 +0300 Subject: [PATCH 14/16] Optimized max_part_bit in sql_select.cc to use my_find_first_bit. --- sql/sql_select.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b185c62aea6..7dc76b3b35f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6967,12 +6967,13 @@ Item_equal::add_key_fields(JOIN *join, KEY_FIELD **key_fields, } -static uint +static inline uint max_part_bit(key_part_map bits) { - uint found; - for (found=0; bits & 1 ; found++,bits>>=1) ; - return found; + if (bits == 0) + return 0; + /* find first zero bit by reverting all bits and find first bit */ + return my_find_first_bit(~(ulonglong) bits); } From c41ab95a3899248059abe952725781fac32b51bf Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 6 Sep 2024 15:05:23 +0300 Subject: [PATCH 15/16] Remove rows and cost from optimizer trace for not usable key parts --- mysql-test/main/opt_trace.result | 2 -- sql/sql_select.cc | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result index 54821377fe0..ff9797a7072 100644 --- a/mysql-test/main/opt_trace.result +++ b/mysql-test/main/opt_trace.result @@ -9417,8 +9417,6 @@ S { "access_type": "ref", "index": "PRIMARY", - "rows": 1.79769e308, - "cost": 1.79769e308, "chosen": false, "cause": "no predicate for first keypart" } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7dc76b3b35f..bf80b360ab2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8085,7 +8085,6 @@ best_access_path(JOIN *join, SplM_plan_info *spl_plan= 0; table_map spl_pd_boundary= 0; Range_rowid_filter_cost_info *filter= 0; - const char* cause= NULL; enum join_type best_type= JT_UNKNOWN, type= JT_UNKNOWN; disable_jbuf= disable_jbuf || idx == join->const_tables; @@ -8113,6 +8112,7 @@ best_access_path(JOIN *join, TABLE *table= s->table; double best_records= DBL_MAX; uint max_key_part=0; + const char *cause= NULL; /* Test how we can use keys */ rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; // Assumed records/key @@ -8661,7 +8661,9 @@ best_access_path(JOIN *join, table->key_info[filter->key_no].name); } } - trace_access_idx.add("rows", records).add("cost", tmp); + + if (!cause) + trace_access_idx.add("rows", records).add("cost", tmp); if (tmp + 0.0001 < best_time - records/TIME_FOR_COMPARE) { @@ -8680,7 +8682,6 @@ best_access_path(JOIN *join, trace_access_idx.add("chosen", false) .add("cause", cause ? cause : "cost"); } - cause= NULL; } /* for each key */ records= best_records; } From c630e23a186c7ecfe0afac21163cb4fa2cdc5f7a Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Sat, 7 Sep 2024 17:17:44 +0300 Subject: [PATCH 16/16] MDEV-34894: Poor query plan, because range estimates are not reused for ref(const) (Variant 4, with @@optimizer_adjust_secondary_key_costs, reuse in two places, and conditions are replaced with equivalent simpler forms in two more) In best_access_path(), ReuseRangeEstimateForRef-3, the check for whether "all used key_part_i used key_part_i=const" was incorrect: it may produced a "NO" answer for cases when we had: key_part1= const // some key parts are usable key_part2= value_not_in_join_prefix //present but unusable key_part3= non_const_value // unusable due to gap in key parts. This caused the optimizer to fail to apply ReuseRangeEstimateForRef heuristics. The consequence is poor query plan choice when the index in question has very skewed data distribution. The fix is enabled if its @@optimizer_adjust_secondary_key_costs flag is set. --- mysql-test/main/join.result | 84 +++++++++++++++++++ mysql-test/main/join.test | 78 +++++++++++++++++ mysql-test/main/mysqld--help.result | 8 +- mysql-test/main/secondary_key_costs.result | 2 +- .../sys_vars/r/sysvars_server_embedded.result | 4 +- .../r/sysvars_server_notembedded.result | 4 +- sql/sql_priv.h | 1 + sql/sql_select.cc | 48 ++++++++--- sql/sys_vars.cc | 7 +- 9 files changed, 214 insertions(+), 22 deletions(-) diff --git a/mysql-test/main/join.result b/mysql-test/main/join.result index 34c58910f07..9f843a7e722 100644 --- a/mysql-test/main/join.result +++ b/mysql-test/main/join.result @@ -3495,3 +3495,87 @@ a b c SET OPTIMIZER_USE_CONDITION_SELECTIVITY=@tmp; DROP TABLE t1,t2; # End of 10.6 tests +# +# MDEV-34894: Poor query plan, because range estimates are not reused for ref(const) +# +create table t0 ( +a int, +b int, +dummy int +); +insert into t0 select seq,seq,seq from seq_1_to_10; +create table t1 ( +pk1 int, +pk2 int, +pk3 int, +key1 int, +key(key1), +filler char(100), +primary key(pk1,pk2,pk3) +); +insert into t1 +select +seq, seq, seq, +FLOOR(seq/2), +'filler-data' +from seq_1_to_10000; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +update t1 set pk1=1 where pk1 between 1 and 200; +explain select * from t1 where pk1=1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref PRIMARY PRIMARY 4 const 231 +explain select * from t0,t1 where t1.pk1=t0.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t1 ref PRIMARY PRIMARY 4 test.t0.a 1 +create table t2 ( +col int +); +insert into t2 select seq from seq_1_to_10000; +set optimizer_adjust_secondary_key_costs='fix_reuse_range_for_ref'; +# This must use this good query plan: +# t0 - ALL +# t1 - ref, key=key1, not PRIMARY as pk1=1 is true for 20% of all rows +# t2 - ALL +explain select * from t0, t1, t2 +where +t1.pk1=1 and t1.pk2=t2.col and t1.pk3=t0.dummy and +t1.key1=t0.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 Using where +1 SIMPLE t1 ref PRIMARY,key1 key1 5 test.t0.b 1 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 10000 Using where; Using join buffer (flat, BNL join) +set optimizer_adjust_secondary_key_costs=''; +# Bad query: +# t0 - ALL +# t1 - ref, key=PRIMARY +# t2 - ALL +explain select * from t0, t1, t2 +where +t1.pk1=1 and t1.pk2=t2.col and t1.pk3=t0.dummy and +t1.key1=t0.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t0 ALL NULL NULL NULL NULL 10 +1 SIMPLE t1 ref PRIMARY,key1 PRIMARY 4 const 1 Using index condition; Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 10000 Using where; Using join buffer (flat, BNL join) +drop table t0,t1,t2; +set @@optimizer_adjust_secondary_key_costs="fix_reuse_range_for_ref"; +CREATE OR REPLACE TABLE t1 (a INT NOT NULL, b INT NOT NULL, c INT, key(a,b,c)) ENGINE=Aria; +INSERT INTO t1 select seq/10,mod(seq,2),seq from seq_1_to_1000; +update t1 set a=10 WHERE c < 100; +update t1 set a=12 WHERE a=11; +insert into t1 values (11,1,11), (11,2,11); +create or replace table t2 select seq from seq_1_to_10; +explain select count(*) from t1, t2 as seq where a=10 and b=seq.seq; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE seq ALL NULL NULL NULL NULL 10 +1 SIMPLE t1 ref a a 8 const,test.seq.seq 5 Using where; Using index +explain select count(*) from t1, t2 as seq where a=11 and b=seq.seq; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref a a 4 const 2 Using index +1 SIMPLE seq ALL NULL NULL NULL NULL 10 Using where; Using join buffer (flat, BNL join) +drop table t1,t2; +set @@optimizer_adjust_secondary_key_costs=default; diff --git a/mysql-test/main/join.test b/mysql-test/main/join.test index f8abc3fac22..6e037100ae5 100644 --- a/mysql-test/main/join.test +++ b/mysql-test/main/join.test @@ -1904,3 +1904,81 @@ SELECT * FROM SET OPTIMIZER_USE_CONDITION_SELECTIVITY=@tmp; DROP TABLE t1,t2; --echo # End of 10.6 tests + +--source include/have_sequence.inc + +--echo # +--echo # MDEV-34894: Poor query plan, because range estimates are not reused for ref(const) +--echo # +create table t0 ( + a int, + b int, + dummy int +); +insert into t0 select seq,seq,seq from seq_1_to_10; + +create table t1 ( + pk1 int, + pk2 int, + pk3 int, + key1 int, + key(key1), + filler char(100), + primary key(pk1,pk2,pk3) +); + +insert into t1 +select + seq, seq, seq, + FLOOR(seq/2), + 'filler-data' +from seq_1_to_10000; +analyze table t1; + +update t1 set pk1=1 where pk1 between 1 and 200; + +explain select * from t1 where pk1=1; + +explain select * from t0,t1 where t1.pk1=t0.a; + +create table t2 ( + col int +); +insert into t2 select seq from seq_1_to_10000; + +set optimizer_adjust_secondary_key_costs='fix_reuse_range_for_ref'; +--echo # This must use this good query plan: +--echo # t0 - ALL +--echo # t1 - ref, key=key1, not PRIMARY as pk1=1 is true for 20% of all rows +--echo # t2 - ALL +explain select * from t0, t1, t2 +where + t1.pk1=1 and t1.pk2=t2.col and t1.pk3=t0.dummy and + t1.key1=t0.b; + +set optimizer_adjust_secondary_key_costs=''; +--echo # Bad query: +--echo # t0 - ALL +--echo # t1 - ref, key=PRIMARY +--echo # t2 - ALL +explain select * from t0, t1, t2 +where + t1.pk1=1 and t1.pk2=t2.col and t1.pk3=t0.dummy and + t1.key1=t0.b; + +drop table t0,t1,t2; + +set @@optimizer_adjust_secondary_key_costs="fix_reuse_range_for_ref"; +CREATE OR REPLACE TABLE t1 (a INT NOT NULL, b INT NOT NULL, c INT, key(a,b,c)) ENGINE=Aria; +INSERT INTO t1 select seq/10,mod(seq,2),seq from seq_1_to_1000; +update t1 set a=10 WHERE c < 100; +update t1 set a=12 WHERE a=11; +insert into t1 values (11,1,11), (11,2,11); +create or replace table t2 select seq from seq_1_to_10; + +explain select count(*) from t1, t2 as seq where a=10 and b=seq.seq; +# This will execute code in ReuseRangeEstimateForRef-4 +explain select count(*) from t1, t2 as seq where a=11 and b=seq.seq; +drop table t1,t2; + +set @@optimizer_adjust_secondary_key_costs=default; diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 08cd1202655..1e5fad44a5b 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -721,8 +721,10 @@ The following specify which files/extra groups are read (specified before remain disable_forced_index_in_group_by = Disable automatic forced index in GROUP BY. fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB secondary - keys. This variable will be deleted in MariaDB 11.0 as it - is not needed with the new 11.0 optimizer. + keys. fix_reuse_range_for_ref = Do a better job at + reusing range access estimates when estimating ref + access. This variable will be deleted in MariaDB 11.0 as + it is not needed with the new 11.0 optimizer. Use 'ALL' to set all combinations. --optimizer-join-limit-pref-ratio=# For queries with JOIN and ORDER BY LIMIT : make the @@ -1705,7 +1707,7 @@ old-alter-table DEFAULT old-mode UTF8_IS_UTF8MB3 old-passwords FALSE old-style-user-limits FALSE -optimizer-adjust-secondary-key-costs +optimizer-adjust-secondary-key-costs fix_reuse_range_for_ref optimizer-join-limit-pref-ratio 0 optimizer-max-sel-arg-weight 32000 optimizer-max-sel-args 16000 diff --git a/mysql-test/main/secondary_key_costs.result b/mysql-test/main/secondary_key_costs.result index b246b666115..3a5b883068c 100644 --- a/mysql-test/main/secondary_key_costs.result +++ b/mysql-test/main/secondary_key_costs.result @@ -82,7 +82,7 @@ json_detailed(json_extract(@trace, '$**.considered_access_paths')) drop table t1, name, flag2; select @@optimizer_adjust_secondary_key_costs; @@optimizer_adjust_secondary_key_costs - +fix_reuse_range_for_ref set @@optimizer_adjust_secondary_key_costs=7; select @@optimizer_adjust_secondary_key_costs; @@optimizer_adjust_secondary_key_costs 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 42b661bc448..850497191d0 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2275,11 +2275,11 @@ COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_ADJUST_SECONDARY_KEY_COSTS VARIABLE_SCOPE SESSION VARIABLE_TYPE SET -VARIABLE_COMMENT A bit field with the following values: adjust_secondary_key_cost = Update secondary key costs for ranges to be at least 5x of clustered primary key costs. disable_max_seek = Disable 'max_seek optimization' for secondary keys and slight adjustment of filter cost. disable_forced_index_in_group_by = Disable automatic forced index in GROUP BY. fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB secondary keys. This variable will be deleted in MariaDB 11.0 as it is not needed with the new 11.0 optimizer. +VARIABLE_COMMENT A bit field with the following values: adjust_secondary_key_cost = Update secondary key costs for ranges to be at least 5x of clustered primary key costs. disable_max_seek = Disable 'max_seek optimization' for secondary keys and slight adjustment of filter cost. disable_forced_index_in_group_by = Disable automatic forced index in GROUP BY. fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB secondary keys. fix_reuse_range_for_ref = Do a better job at reusing range access estimates when estimating ref access. This variable will be deleted in MariaDB 11.0 as it is not needed with the new 11.0 optimizer. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST adjust_secondary_key_cost,disable_max_seek,disable_forced_index_in_group_by,fix_innodb_cardinality +ENUM_VALUE_LIST adjust_secondary_key_cost,disable_max_seek,disable_forced_index_in_group_by,fix_innodb_cardinality,fix_reuse_range_for_ref READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_JOIN_LIMIT_PREF_RATIO 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 b626551a570..fcbe4f51fc5 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -2435,11 +2435,11 @@ COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_ADJUST_SECONDARY_KEY_COSTS VARIABLE_SCOPE SESSION VARIABLE_TYPE SET -VARIABLE_COMMENT A bit field with the following values: adjust_secondary_key_cost = Update secondary key costs for ranges to be at least 5x of clustered primary key costs. disable_max_seek = Disable 'max_seek optimization' for secondary keys and slight adjustment of filter cost. disable_forced_index_in_group_by = Disable automatic forced index in GROUP BY. fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB secondary keys. This variable will be deleted in MariaDB 11.0 as it is not needed with the new 11.0 optimizer. +VARIABLE_COMMENT A bit field with the following values: adjust_secondary_key_cost = Update secondary key costs for ranges to be at least 5x of clustered primary key costs. disable_max_seek = Disable 'max_seek optimization' for secondary keys and slight adjustment of filter cost. disable_forced_index_in_group_by = Disable automatic forced index in GROUP BY. fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB secondary keys. fix_reuse_range_for_ref = Do a better job at reusing range access estimates when estimating ref access. This variable will be deleted in MariaDB 11.0 as it is not needed with the new 11.0 optimizer. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST adjust_secondary_key_cost,disable_max_seek,disable_forced_index_in_group_by,fix_innodb_cardinality +ENUM_VALUE_LIST adjust_secondary_key_cost,disable_max_seek,disable_forced_index_in_group_by,fix_innodb_cardinality,fix_reuse_range_for_ref READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME OPTIMIZER_JOIN_LIMIT_PREF_RATIO diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 0bcb09a80ad..8f7962fc6b6 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -275,6 +275,7 @@ #define OPTIMIZER_ADJ_DISABLE_MAX_SEEKS (2) #define OPTIMIZER_ADJ_DISABLE_FORCE_INDEX_GROUP_BY (4) #define OPTIMIZER_FIX_INNODB_CARDINALITY (8) +#define OPTIMIZER_ADJ_FIX_REUSE_RANGE_FOR_REF (16) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bf80b360ab2..57e391dc82d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8125,6 +8125,7 @@ best_access_path(JOIN *join, key_part_map notnull_part=0; // key parts which won't have NULL in lookup tuple. table_map found_ref= 0; uint key= keyuse->key; + uint max_const_parts; filter= 0; bool ft_key= (keyuse->keypart == FT_KEYPART); /* Bitmap of keyparts where the ref access is over 'keypart=const': */ @@ -8228,6 +8229,8 @@ best_access_path(JOIN *join, rec= MATCHING_ROWS_IN_OTHER_TABLE; // Fix for small tables Json_writer_object trace_access_idx(thd); + max_const_parts= max_part_bit(const_part); + /* full text keys require special treatment */ @@ -8344,9 +8347,7 @@ best_access_path(JOIN *join, in ReuseRangeEstimateForRef-3. */ if (table->opt_range_keys.is_set(key) && - (const_part & - (((key_part_map)1 << table->opt_range[key].key_parts)-1)) == - (((key_part_map)1 << table->opt_range[key].key_parts)-1) && + table->opt_range[key].key_parts <= max_const_parts && table->opt_range[key].ranges == 1 && records > (double) table->opt_range[key].rows) { @@ -8394,6 +8395,17 @@ best_access_path(JOIN *join, found_part == PREV_BITS(uint,keyinfo->user_defined_key_parts))) { max_key_part= max_part_bit(found_part); + bool all_used_equalities_are_const; + if ((thd->variables.optimizer_adjust_secondary_key_costs & + OPTIMIZER_ADJ_FIX_REUSE_RANGE_FOR_REF)) + { + all_used_equalities_are_const= (max_key_part == max_const_parts); + } + else + { + // Old, incorrect check: + all_used_equalities_are_const= !found_ref; + } /* ReuseRangeEstimateForRef-3: We're now considering a ref[or_null] access via @@ -8408,7 +8420,7 @@ best_access_path(JOIN *join, create quick select over another index), so we can't compare them to (**). We'll make indirect judgements instead. The sufficient conditions for re-use are: - (C1) All e_i in (**) are constants, i.e. found_ref==FALSE. (if + (C1) All e_i in (**) are constants (if this is not satisfied we have no way to know which ranges will be actually scanned by 'ref' until we execute the join) @@ -8418,7 +8430,7 @@ best_access_path(JOIN *join, We also have a property that "range optimizer produces equal or tighter set of scan intervals than ref(const) optimizer". Each of the intervals in (**) are "tightest possible" intervals when - one limits itself to using keyparts 1..K (which we do in #2). + one limits itself to using keyparts 1..K (which we do in #2). From here it follows that range access used either one, or both of the (I1) and (I2) intervals: @@ -8433,7 +8445,8 @@ best_access_path(JOIN *join, (C3) "range optimizer used (have ref_or_null?2:1) intervals" */ - if (table->opt_range_keys.is_set(key) && !found_ref && //(C1) + if (table->opt_range_keys.is_set(key) && + all_used_equalities_are_const && // (C1) table->opt_range[key].key_parts == max_key_part && //(C2) table->opt_range[key].ranges == 1 + MY_TEST(ref_or_null_part)) //(C3) { @@ -8466,10 +8479,10 @@ best_access_path(JOIN *join, */ if (table->opt_range_keys.is_set(key)) { + double rows= (double) table->opt_range[key].rows; if (table->opt_range[key].key_parts >= max_key_part) // (2) { - double rows= (double) table->opt_range[key].rows; - if (!found_ref && // (1) + if (all_used_equalities_are_const && // (1) records < rows) // (3) { trace_access_idx.add("used_range_estimates", "clipped up"); @@ -8537,15 +8550,26 @@ best_access_path(JOIN *join, */ if (table->opt_range_keys.is_set(key) && table->opt_range[key].key_parts <= max_key_part && - const_part & - ((key_part_map)1 << table->opt_range[key].key_parts) && table->opt_range[key].ranges == (1 + MY_TEST(ref_or_null_part & const_part)) && records > (double) table->opt_range[key].rows) { - trace_access_idx.add("used_range_estimates", true); - records= (double) table->opt_range[key].rows; + bool all_parts_used; + if ((thd->variables.optimizer_adjust_secondary_key_costs & + OPTIMIZER_ADJ_FIX_REUSE_RANGE_FOR_REF)) + { + all_parts_used= table->opt_range[key].key_parts <= max_const_parts; + } + else + all_parts_used= (bool) (const_part & + ((key_part_map)1 + << table->opt_range[key].key_parts)); + if (all_parts_used) + { + trace_access_idx.add("used_range_estimates", true); + records= (double) table->opt_range[key].rows; + } } } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index c5b507b936d..77d0c2daf78 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2837,7 +2837,8 @@ static Sys_var_ulong Sys_optimizer_trace_max_mem_size( */ static const char *adjust_secondary_key_cost[]= { - "adjust_secondary_key_cost", "disable_max_seek", "disable_forced_index_in_group_by", "fix_innodb_cardinality",0 + "adjust_secondary_key_cost", "disable_max_seek", "disable_forced_index_in_group_by", + "fix_innodb_cardinality", "fix_reuse_range_for_ref", 0 }; @@ -2852,10 +2853,12 @@ static Sys_var_set Sys_optimizer_adjust_secondary_key_costs( "GROUP BY. " "fix_innodb_cardinality = Disable doubling of the Cardinality for InnoDB " "secondary keys. " + "fix_reuse_range_for_ref = Do a better job at reusing range access estimates " + "when estimating ref access. " "This variable will be deleted in MariaDB 11.0 as it is not needed with the " "new 11.0 optimizer.", SESSION_VAR(optimizer_adjust_secondary_key_costs), CMD_LINE(REQUIRED_ARG), - adjust_secondary_key_cost, DEFAULT(0)); + adjust_secondary_key_cost, DEFAULT(OPTIMIZER_ADJ_FIX_REUSE_RANGE_FOR_REF)); static Sys_var_charptr_fscs Sys_pid_file(